Subject: Re: [netatalk-admins] toner low errors

From: Darren Atkinson <[email-masked]>
Date: Mon, 17 Aug 1998 10:28:00 -0700 (PDT)

Andras,

The result is coming back as 0xffff (-1 as a signed short).  My hack
to the code is below.  But, here's a strange thing.  I just tried
connecting to the printer using pap and it worked for a while.  This
is with the non-hacked version of pap:

    murdoch % pap -E -p paanini /usr/lib/atalk/pagecount.ps 
    Trying 3841.134:157 ...
    status: PrinterError: toner is low; source: AppleTalk
    Connected to paanini:LaserWriter@CSE.
    Connection closed.

    murdoch % pap -p paanini /usr/lib/atalk/pagecount.ps 
    Trying 3841.134:157 ...
    status: warming up; source: AppleTalk
    Connected to paanini:LaserWriter@CSE.
    *35121
    Connection closed.

    murdoch % pap -p paanini /usr/lib/atalk/pagecount.ps
    Trying 3841.134:157 ...
    status: PrinterError: toner is low; source: AppleTalk
    Connected to paanini:LaserWriter@CSE.
    *35121
    Connection closed.

    murdoch % pap -w -p paanini /usr/lib/atalk/pagecount.ps 
    Trying 3841.134:157 ...
    status: PrinterError: toner is low; source: AppleTalk
    Connected to paanini:LaserWriter@CSE.

    murdoch % pap -p paanini /usr/lib/atalk/pagecount.ps 
    Trying 3841.134:157 ...
    status: PrinterError: toner is low; source: AppleTalk
    status: PrinterError: toner is low; source: AppleTalk
    status: PrinterError: toner is low; source: AppleTalk

I tried pap with -E as suggested, and the job didn't hang, but I
didn't get the pagecount back either.  After that, pap worked fine
until I ran it with -w and it just waited.  I expected that behavior
since "toner is low" doesn't match "waiting".  So, I interrupted pap
and after that I started getting the problem again.

The behavior after I interrupted pap is what you mentioned.  However,
I waited several minutes before trying to connect to the printer again
hoping the connection would time out, but it hasn't so far.  Oops, let
me take that back.  After waiting about 15 minutes, I can talk to the
printer again.

I've seen similar problems with HP printers when talking to them via
TCP/IP.  If the client is terminated and doesn't close the socket, the
printer is pretty much hung until power cycled.

So, what can we learn from this behavior?  I mean besides the fact
that HP printers aren't the easiest things in the world to talk to.
Here's my current scenario of what was happening:

I need to use -w to get accurate page counts.  But, with -w the
printer status "toner is low" won't match "waiting", so pap just sits
there waiting.  So, I remove the job using lprm or just interrupt pap
if I'm just running it from the command line.  But then pap is
terminated without closing down the connection.  As a result, the
connection is hung and you have to wait for it to timeout before
trying again.

Possible fixes?  If you don't need to use -w, then don't.  If pap is
never interrupted, then things seem to work fine.  Of course, if you
cancel the active print job then you may run into problems with hung
connections.

If you need to use -w to do accurate accounting, then we need to do
some work it seems.  First, add some signal handlers to pap so that it
properly closes down the connection.  Second, reverse or otherwise
change the logic in pap so that instead of checking if the status
contains "waiting", it makes sure the status doesn't say "busy" or
"printing".  Does this sound plausible?

Thanks again for all your help!
Darren

----------------------------------------------------------------------

        if ( result != 0 ) {

{
        char big_buf [16384];

        strncpy (big_buf, iov.iov_base + 9, iov.iov_len - 9);

        if (strstr (big_buf, "toner is low")) {
            printf ("ignoring error\n");
            bcopy( &nn.nn_sat, &sat, sizeof( struct sockaddr_at ));
            quantum = rbuf[ 5 ];
            break;
        }
}
            sleep( 2 );
        } else {
            bcopy( &nn.nn_sat, &sat, sizeof( struct sockaddr_at ));
            sat.sat_port = rbuf[ 4 ];
            quantum = rbuf[ 5 ];
            break;
        }

In the code above, rbuf[4] is always 0, which is why I'm not setting
sat.sat_port to rbuf [4].  I looked and sat.sat_port does contain a
value but I guess it isn't the right value to talk to the printer and
you need to extract the right port number from the reply packet.

---

From: Andras Kadinger <[email-masked]>
Date: Tue, 18 Aug 1998 03:51:38 +0200

Darren, List,

[Warning: long message follows; also please excuse the bits of infancy
scattered around; it's already 4AM; although those two are not
necessarily related...]

Darren Atkinson wrote:
> The result is coming back as 0xffff (-1 as a signed short).

That means 'printer is busy' by definition, which should also mean the
connection is not opened.

>  My hack to the code is below.  But, here's a strange thing.  I just tried

Hmm, might the flush operator be missing from pagecount.ps? No, it
doesn't. Then we seem to have a race: the last (and in this case, the
only) data packet from the printer might or might not get sent by the
printer before we close the connection (with -E, that is). It didn't in
the above case.

What do you see with an #EBUG compiled pap and with no -E? We might be
better off with making up some heuristics to allow at least some chance
for an eof (and the last piece of data) to be received from the printer
when -E, e.g. wait for it for a couple of seconds before closing the
connection. Depending on actual printer behaviour, that might make
everything happy (and lose only a couple of seconds per job in the worst
case).

[...]

> properly closes down the connection.

Yes, this is one thing I definitely wanted to do. However, since the
current atp implementation is not in the kernel, this would involve
blocking and unblocking signals around calls to the atp routines - or
maybe not? I'm not very experienced with signals, so I could need some
help with this.

> Second, reverse or otherwise change the logic in pap so that instead of
> checking if the status contains "waiting", it makes sure the status doesn't
> say "busy" or "printing".  Does this sound plausible?

Well, that's a good idea. An even more flexible (but possibly overkill)
solution might be to parse the printer's PPD file, which should contain
a list of all possible printer messages. To no avail, as I checked the
PPD for our 4MV now and there is no indication of meaning for the
messages.

What do you get when there is a PrinterError: and the printer is busy
and/or printing? Do you get both messages, or does the error suppress
other indications? The latter may make it impossible to tell the actual
printer status based on the status reply. Otherwise it's simple, I
think.

> Thanks again for all your help!

Well, things seem to get in motion now, and that's good! :-)
I was glad to help! What's more, I'm still glad, so I help further! Ah,
you thought you wouldn't have to pay for it?! Wrong! :-)

That is, when the printer returns 'busy' for the open request...

>             bcopy( &nn.nn_sat, &sat, sizeof( struct sockaddr_at ));

You ignore the error and set the address.

>         } else {

But when the printer is not busy,

>             sat.sat_port = rbuf[ 4 ];

You (correctly) set the port as well.

> sat.sat_port to rbuf [4].

It seems reasonable for the printer to set the port number (rbuf[4]) to
0 when the reply indicates 'busy'.

> I looked and sat.sat_port does contain a value but I guess it isn't the
> right value to talk to the printer and you need to extract the right port
> number from the reply packet.

Initially (after the nbplkup but before the successful OPENREPLY), the
port in sat.sat_port is the port of the socket the printer is registered
under with NBP (you can see it with nbplkup). The PAP spec says, You
have to send an OPEN request to this port, and if and when your
connection request is accepted, you get an OPENREPLY packet back with
'ready' status indication, and with the actual port address you should
use in the future to send the actual data to the printer.

What I can imagine happening here is this: the printer probably handles
out the same port for each connection (since it can handle only one at a
time), and I bet You used the same port number You got in a previous
connection. PAP numbers request data packets, but does not number send
data packets, going further, it allows a sequence number of 0 to mean
'unnumbered', which practically disables sequence numbering checks. What
I thought up can happen, is that the printer probably has a process
bound to the 'connection' socket (whose address normally gets returned
in an OPENREPLY) running continously, and you don't have to issue an
OPEN to start that process. So that process might just sit there, and
accept and digest 'any' ATP/PAP packets sent to it, not strictly
enforcing the semantics of PAP. This may make it possible to send a
(possibly unnumbered) data packet to it and in this way actually 'force'
printing data into the printer even when it showed itself to be busy in
OPENREPLY for some reason. Since the HP probably (as most laserprinters
nowadays I guess) has a multitasking operating system, I can not see any
reason to stop and restart the RIP process or the network input process
just because the engine is printing - in fact, this is why You can get
back the page count too early: you can submit another job while the
pages of the previous one are still being printed by the engine from
raster memory (or I guess from display lists for that matter). I
personally consider this as a feature (it increases throughput,
substantially in some cases), but I agree that this is not very
practical when one tries to get back the page count with pagecount
(which in fact has the semantics of actual hard-copy printed pages by
the engine, and it seems to actually behave that way).

Hmm, a different approach would be to piggyback a page counting
mechanism either onto showpage (for level 1 I guess), or to EndPage (for
level 2); this in itself however doesn't account for #copies or Copies -
but that can be incorporated as well. Although handling of
interruption/cancellation issues gets even messier, since one would have
to put this page count to unencapsulated global PostScript VM to let it
be retained between jobs and thus make sure it doesn't get lost when
cancelling. Or mix 'page #n complete' or similar messages into other
(data) output coming from the printer through %stdout, which would make
handling of the channel unclean and possibly non-transparent - but could
work. The #copies/Copies issue needs handling here as well. But this
wouldn't correctly account: if someone turned the printer off while it
had unprinted pages in memory, those pages would be accounted for. Pages
with n-up printing should pose a problem as well.

[some PostScript tidbits (like MTV Europe does nowadays):

the first PostScript language specification (1985) was called Level 1;
many necessary enhancements were made as time went by, and the result
was PostScript Level 2 (1990), which standardized many real-life,
important facilities previously implemented in vendor-specific ways.

most PostScript procedures can be overridden, to change their behaviour

showpage: the PostScript procedure to tell the printer to 'show the
page',
	that is, to print it out; we can enhance it to maintain a counter

EndPage: a PostScript procedure that gets called from each showpage;
it's
	a placeholder for things, like n-up printing, where a physical page
	receives the (usu reduced) images of multiple logical pages; only
	in Level 2; also can be enhanced to increase a counter; problem is,
	some n-up implementations however replace it, instead piggybacking
	onto it, and this would disable the the counter for these jobs.

#copies: PostScript variable from the vendor-dependent Level 1 days;
should
	be set to the number of copies needed from a page before calling
	showpage

Copies: much like #copies, but the newer, standardized Level 2 flavour

VM: memory area used to store PostScript variables

local VM: used for short lifetime variables, similar to storage for
local
	variables in other languages;

global VM: used for variables with longer lifetime, similar to storage
for
	global variables in other languages;

the above two gets cleared between print jobs

unencapsulated (global) VM: VM which does not get cleaned between jobs;
	a special operator has to be called with a password argument to
	enable access to this memory area, which takes up time like context
	switches in unix and also clears local and global VM

%stdin, %stdout: the PostScript interpreter has two devices with these
	names; %stdin is where the interpreter reads the PostScript program
	text from; %stdout can be used much like in any ordinary C program:
	to report progress, results, etc. (the graphic output of course
	does not go to %stdout; in a direct connection the Mac uses this
	channel to receive results of the queries (questions) it sends to
	the printer, otherwise mostly unused
]

Hope, this helps, [it would be a shame if it wouldn't, wouldn't it? :-)]

Sincerely,
Andras Kadinger
[email-masked]