online status with pjl
Sterck Serge
serge.sterck at fmsb.be
Sun Jul 25 23:30:30 PDT 2010
> On Jul 23, 2010, at 2:26 AM, Sterck Serge wrote:
> > I need to known the exact status (out of paper,tray missing etc..)of =
> printer placed in a kiosk in real time.
> >=20
> > I have downloaded the hp backend and i saw
> > that they use pjl to enable unsolicited status of device and job
> > @PJL USTATUS DEVICE =3D ON \r\n at PJL USTATUS JOB =3D ON \
> > they start a thread that read this status and report this information =
> back
> > to cups.
> >=20
> > if i writen a new backend based on the usb backend must i use libusb =
> or the traditionnal method to find the device looking at /dev/usb ?
> >=20
> > The status of the printer is reported in cups only during the print of =
> a job . Is there a possibility to report this in real time.
>
> What you'll need to do is have a background process that sends status =
> updates to your application separately and a new backend that talks to =
> this background process to send print data and report status.
>
> ________________________________________________________________________
> Michael Sweet, Senior Printing System Engineer, PWG Chair
>
i'm using a thread in my backend like hp but cups is only aware of the status
of the printer just when a job is submited and printed because it is the only moment that the backend is called
Hereis the backend it is just a proof of concept i doesnt not print yet i'm just in the main thread performing a sleep of 60s to simulated a print of a job
and the thread read_pjl read the status received by the printer.
I have another problem i received the out of tray error but when i put it back
the printer don't send me the printer ready status grrrr lexmark
#include <cups/backend.h>
#include <cups/cups.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <cups/string.h>
#include <cups/i18n.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <syslog.h>
struct pjl_attributes
{
int current_status;
int eoj_pages; /* end-of-job pages */
int abort; /* 0=no, 1=yes */
int done; /* 0=no, 1=yes */
int usb_fd;
pthread_t tid;
pthread_mutex_t mutex;
pthread_cond_t done_cond;
};
static const char pjl_ustatus_cmd[] = "\e%-12345X at PJL USTATUS DEVICE = ON \r\n at PJL USTATUS JOB = ON \r\n at PJL JOB \r\n\e%-12345X";
static const char pjl_job_end_cmd[] = "\e%-12345X at PJL EOJ \r\n\e%-12345X";
static const char pjl_ustatus_off_cmd[] = "\e%-12345X at PJL USTATUSOFF \r\n\e%-12345X";
/*
* Local functions...
*/
void list_devices(void);
int print_device(const char *uri, const char *hostname,
const char *resource, char *options,
int print_fd, int copies, int argc, char *argv[],
struct pjl_attributes pa);
int parse_pjl_job_end(char *buf, int *pages)
{
char *p, *tail;
int stat=0;
if (buf[0] == 0)
goto bugout;
if ((p = strcasestr(buf, "ustatus job")) != NULL)
{
if (strncasecmp(p+13, "end", 3) == 0)
{
stat = 1;
if ((p = strcasestr(p+13+5, "pages=")) != NULL)
*pages = strtol(p+6, &tail, 10);
}
}
bugout:
return stat;
}
int parse_pjl_device_status(char *buf, int *status)
{
char *p, *tail;
int stat=0;
if (buf[0] == 0)
goto bugout;
if ((p = strcasestr(buf, "code=")) != NULL)
{
*status = strtol(p+5, &tail, 10);
syslog(LOG_INFO,"status = %d",*status);
stat = 1;
}
bugout:
return stat;
}
int get_pjl_input(int usb_fd,char *buf,int buf_size,int *bytes_read)
{
int stat;
int len;
*bytes_read = 0;
len = read(usb_fd, buf,buf_size);
*bytes_read = len;
return 0;
}
static void pjl_read_thread(struct pjl_attributes *pa)
{
char buf[1024];
int len,new_status,new_eoj,stat,bytes_read;
pthread_detach(pthread_self());
syslog(LOG_INFO,"fmsb backend - starting thread %d\n",(int)pa->tid);
pa->current_status = 10001; /* default is ready */
pa->eoj_pages = pa->abort = pa->done = 0;
while( !pa->abort)
{
buf[0] = '\0';
stat = get_pjl_input(pa->usb_fd,buf,sizeof(buf),&bytes_read);
pthread_mutex_lock(&pa->mutex);
new_status = parse_pjl_device_status(buf, &pa->current_status);
new_eoj = parse_pjl_job_end(buf, &pa->eoj_pages);
pthread_mutex_unlock(&pa->mutex);
if (new_status)
syslog(LOG_INFO,"read new pjl status: %d\n", pa->current_status);
if (new_eoj)
syslog(LOG_INFO,"read pjl job_end: %d\n", pa->eoj_pages);
syslog(LOG_INFO,"fmsb backend - pjl input = %s",buf);
}
syslog(LOG_INFO,"fmsb backend - exiting thread %d abord = %d stat=%d\n",(int)pa->tid,pa->abort,stat);
pa->done=1;
pthread_cond_signal(&pa->done_cond);
return;
}
void list_devices(void)
{
int i; /* Looping var */
int fd; /* File descriptor */
char device[255], /* Device filename */
device_id[1024], /* Device ID string */
device_uri[1024], /* Device URI string */
make_model[1024]; /* Make and model */
/*
* Try to open each USB device...
*/
for (i = 0; i < 16; i ++)
{
/*
* Linux has a long history of changing the standard filenames used
* for USB printer devices. We get the honor of trying them all...
*/
sprintf(device, "/dev/usblp%d", i);
if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
{
if (errno != ENOENT)
continue;
sprintf(device, "/dev/usb/lp%d", i);
if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
{
if (errno != ENOENT)
continue;
sprintf(device, "/dev/usb/usblp%d", i);
if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
continue;
}
}
if (!backendGetDeviceID(fd, device_id, sizeof(device_id),
make_model, sizeof(make_model),
"fmsb", device_uri, sizeof(device_uri)))
printf("direct %s \"%s\" \"%s FMSB #%d\" \"%s\"\n", device_uri,
make_model, make_model, i + 1, device_id);
close(fd);
}
}
/*
* 'open_device()' - Open a USB device...
*/
static int /* O - File descriptor or -1 on error */
open_device(const char *uri, /* I - Device URI */
int *use_bc) /* O - Set to 0 for unidirectional */
{
int fd; /* File descriptor */
/*
* The generic implementation just treats the URI as a device filename...
* Specific operating systems may also support using the device serial
* number and/or make/model.
*/
syslog(LOG_INFO,"fmsb backend - Opening device : %s",uri);
if (!strncmp(uri, "usb:/dev/", 9))
{
/*
* Do not allow direct devices anymore...
*/
errno = ENODEV;
return (-1);
}
if (!strncmp(uri, "fmsb://", 7))
{
/*
* For Linux, try looking up the device serial number or model...
*/
int i; /* Looping var */
int busy; /* Are any ports busy? */
char device[255], /* Device filename */
device_id[1024], /* Device ID string */
make_model[1024], /* Make and model */
device_uri[1024]; /* Device URI string */
/*
* Find the correct USB device...
*/
for (;;)
{
for (busy = 0, i = 0; i < 16; i ++)
{
/*
* Linux has a long history of changing the standard filenames used
* for USB printer devices. We get the honor of trying them all...
*/
sprintf(device, "/dev/usblp%d", i);
if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)
{
sprintf(device, "/dev/usb/lp%d", i);
if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)
{
sprintf(device, "/dev/usb/usblp%d", i);
if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)
continue;
}
}
if (fd >= 0)
{
backendGetDeviceID(fd, device_id, sizeof(device_id),
make_model, sizeof(make_model),
"fmsb", device_uri, sizeof(device_uri));
}
else
{
/*
* If the open failed because it was busy, flag it so we retry
* as needed...
*/
if (errno == EBUSY)
busy = 1;
device_uri[0] = '\0';
}
if (!strcmp(uri, device_uri))
{
/*
* Yes, return this file descriptor...
*/
fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n",
device);
return (fd);
}
/*
* This wasn't the one...
*/
if (fd >= 0)
close(fd);
}
/*
* If we get here and at least one of the printer ports showed up
* as "busy", then sleep for a bit and retry...
*/
if (busy)
_cupsLangPuts(stderr,
_("INFO: Printer busy; will retry in 5 seconds...\n"));
sleep(5);
}
}
else
{
errno = ENODEV;
return (-1);
}
}
/*
* 'print_device()' - Print a file to a USB device.
*/
int /* O - Exit status */
print_device(const char *uri, /* I - Device URI */
const char *hostname, /* I - Hostname/manufacturer */
const char *resource, /* I - Resource/modelname */
char *options, /* I - Device options/serial number */
int print_fd, /* I - File descriptor to print */
int copies, /* I - Copies to print */
int argc, /* I - Number of command-line arguments (6 or 7) */
char *argv[], /* I - Command-line arguments */
struct pjl_attributes pa) /* I - pjl structure */
{
int use_bc; /* Use backchannel path? */
int device_fd; /* USB device */
size_t tbytes; /* Total number of bytes written */
int len;
struct termios opts; /* Parallel port options */
(void)argc; /* to disable warning of gcc */
(void)argv; /* to diable warning of gcc */
/*
* Open the USB port device...
*/
fputs("STATE: +connecting-to-device\n", stderr);
do
{
use_bc = 0;
if ((device_fd = open_device(uri, &use_bc)) == -1)
{
if (getenv("CLASS") != NULL)
{
/*
* If the CLASS environment variable is set, the job was submitted
* to a class and not to a specific queue. In this case, we want
* to abort immediately so that the job can be requeued on the next
* available printer in the class.
*/
_cupsLangPuts(stderr,
_("INFO: Unable to contact printer, queuing on next "
"printer in class...\n"));
/*
* Sleep 5 seconds to keep the job from requeuing too rapidly...
*/
sleep(5);
return (CUPS_BACKEND_FAILED);
}
if (errno == EBUSY)
{
_cupsLangPuts(stderr,
_("INFO: Printer busy; will retry in 10 seconds...\n"));
sleep(10);
}
else if (errno == ENXIO || errno == EIO || errno == ENOENT ||
errno == ENODEV)
{
_cupsLangPuts(stderr,
_("INFO: Printer not connected; will retry in 30 "
"seconds...\n"));
sleep(30);
}
else
{
_cupsLangPrintf(stderr,
_("ERROR: Unable to open device file \"%s\": %s\n"),
resource, strerror(errno));
return (CUPS_BACKEND_FAILED);
}
}
} while (device_fd < 0);
/*
* enable pjl status
*/
len = write(device_fd,pjl_ustatus_cmd,strlen(pjl_ustatus_cmd));
pa.usb_fd = device_fd;
pthread_mutex_init(&pa.mutex,NULL);
pthread_cond_init(&pa.done_cond,NULL);
pthread_create(&pa.tid,NULL,(void *(*)(void*))pjl_read_thread,(void *)&pa);
tbytes = 0;
while (copies > 0)
{
copies--;
if (print_fd != 0)
{
fputs("PAGE: 1 1\n",stderr);
lseek(print_fd,0,SEEK_SET);
}
if (print_fd != 0 && tbytes >= 0)
_cupsLangPrintf(stderr,
#ifdef HAVE_LONG_LONG
_("INFO: Sent print file, %lld bytes...\n"),
#else
_("INFO: Sent print file, %ld bytes...\n"),
#endif /* HAVE_LONG_LONG */
CUPS_LLCAST tbytes);
syslog(LOG_INFO,"sleeping 60 s in print thread");
sleep(60);
}
len = write(device_fd,pjl_job_end_cmd,strlen(pjl_job_end_cmd));
len = write(device_fd,pjl_ustatus_off_cmd,strlen(pjl_ustatus_off_cmd));
syslog(LOG_INFO,"fmsb backend - stoppped");
/*
* Close the USB port and return...
*/
if( pa.tid)
{
pthread_mutex_lock(&pa.mutex);
pa.abort=1;
while (!pa.done)
pthread_cond_wait(&pa.done_cond,&pa.mutex);
pthread_mutex_unlock(&pa.mutex);
pthread_cancel(pa.tid);
pthread_mutex_destroy(&pa.mutex);
pthread_cond_destroy(&pa.done_cond);
}
close(device_fd);
return CUPS_BACKEND_OK;
}
/*
* 'main()' - Send a file to the specified USB port.
*
* Usage:
*
* printer-uri job-id user title copies options [file]
*/
int /* O - Exit status */
main(int argc, /* I - Number of command-line arguments (6 or 7) */
char *argv[]) /* I - Command-line arguments */
{
int print_fd; /* Print file */
int copies; /* Number of copies to print */
int status; /* Exit status */
int port; /* Port number (not used) */
const char *uri; /* Device URI */
char method[255], /* Method in URI */
hostname[1024], /* Hostname */
username[255], /* Username info (not used) */
resource[1024], /* Resource info (device and options) */
*options; /* Pointer to options */
struct pjl_attributes pa;
pa.tid = 0;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action; /* Actions for POSIX signals */
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
/*
* Make sure status messages are not buffered...
*/
setbuf(stderr, NULL);
/*
* Ignore SIGPIPE signals...
*/
#ifdef HAVE_SIGSET
sigset(SIGPIPE, SIG_IGN);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &action, NULL);
#else
signal(SIGPIPE, SIG_IGN);
#endif /* HAVE_SIGSET */
/*
* Check command-line...
*/
if (argc == 1)
{
list_devices();
return (CUPS_BACKEND_OK);
}
else if (argc < 6 || argc > 7)
{
_cupsLangPrintf(stderr,
_("Usage: %s job-id user title copies options [file]\n"),
argv[0]);
return (CUPS_BACKEND_FAILED);
}
/*
* Extract the device name and options from the URI...
*/
uri = cupsBackendDeviceURI(argv);
if (httpSeparateURI(HTTP_URI_CODING_ALL, uri,
method, sizeof(method), username, sizeof(username),
hostname, sizeof(hostname), &port,
resource, sizeof(resource)) < HTTP_URI_OK)
{
_cupsLangPuts(stderr,
_("ERROR: No device URI found in argv[0] or in DEVICE_URI "
"environment variable!\n"));
return (1);
}
/*
* See if there are any options...
*/
if ((options = strchr(resource, '?')) != NULL)
{
/*
* Yup, terminate the device name string and move to the first
* character of the options...
*/
*options++ = '\0';
}
/*
* If we have 7 arguments, print the file named on the command-line.
* Otherwise, send stdin instead...
*/
if (argc == 6)
{
print_fd = 0;
copies = 1;
}
else
{
/*
* Try to open the print file...
*/
if ((print_fd = open(argv[6], O_RDONLY)) < 0)
{
_cupsLangPrintf(stderr, _("ERROR: Unable to open print file %s - %s\n"),
argv[6], strerror(errno));
return (CUPS_BACKEND_FAILED);
}
copies = atoi(argv[4]);
}
/*
* Finally, send the print file...
*/
status = print_device(uri, hostname, resource, options, print_fd, copies,
argc, argv,pa);
/*
* Close the input file and return...
*/
if (print_fd != 0)
close(print_fd);
return (status);
}
More information about the cups-devel
mailing list