Summer Coding 2010 proposal - Unified ink level reporting

About me
A short self Introduction is in my application page -->

Description
Several printer drivers are capable of reporting the amount of remaining ink or toner in the printer they are driving, but they all do it in different ways. Gutenprint uses escputil. But for HPLIP there is hp-levels.

CUPS provides a unified framework for all drivers to report marker levels in the same way, viewed and queried from a single user interface. Changing both gutenprint and hplip would be sufficient to cover a large number of printer models.

Tasks
There are 6 main tasks of the project:

First 3 tasks are related to the gutenprint driver report ink levels
 * 1) Adjust the existing escputil command from gutenprint so that it can use separate file descriptors for reading from and writing to the device
 * 2) Convert escputil into a library so that the gutenprint raster driver can use it
 * 3) Adjust the gutenprint raster driver to use it by changing the gutenprint CUPS command filters

the other 3 tasks are related to the hpcups driver report ink levels
 * 1) Understand the various ways HP printers communicate their marker levels using IEEE 1284 Device ID fields (base/status.py)
 * 2) Add low/empty marker detection to the hpcups driver that is part of the HPLIP project
 * 3) Add fine-grained marker level detection to the hpcups driver

about gutenprint driver
In terms of the actual work needed to add this support to gutenprint, we should shape the existing code. There is already a program to communicate with printers to obtain the necessary ink level information, which we should integrate into the gutenprint driver in the way CUPS needs it to.

The way it will end up working after being adapted is as follows:


 * 1) There will be a new library, libescputil, made from the current escputil stand-alone program.
 * 2) The existing "commandto*" gutenprint filters will be linked against this new library.

These command filters work like this:

if (!strcmp (command, "Clean") { write_clean_heads_command ; } else if (!strcmp (command, "PrintAlignmentPage") { write_alignment_command ; } ... else if (!strcmp (command, "ReportLevels") { // TODO }

i.e. the basic structure for performing commands is all there, it just needs to be able to actually to what escputil already does. So after being changed the codes look like this:

... else if (!strcmp (command, "ReportLevels") { do_ink_level_reporting ; }

do_ink_level_reporting uses functions from libescputil. The way it can report ink levels to CUPS is by sending specially formatted strings to the stderr file descriptor, like:

STATE: +marker-supply-low ATTR: marker-names=black,cyan,magenta,yellow ATTR: marker-levels=20,45,65,20

Now, for ink level reporting it is not just a case of sending a command to the printer and the job is finished: we need to be able to read its reply.

CUPS filters are chained together with a CUPS backend at the end, like this:

filter -> filter -> filter -> backend

For command filters it will be simpler:

commandfilter -> backend

This is a one-way channel. Due to the need to be able to read information back from the printer a new channel was added for "backchannel" data, so it looks more like this:

command <- backend filter  ->

For the "straight-through" communication, the filter can just write to stdout. But for the "backchannel" communication it must use the specially-supplied CUPS function cupsBackChannelRead. All this does is use a different file descriptor, CUPS_BC_FD.

We also want to still be able to have an "escputil" program though, with the same command line options as the existing one. For this to work, libescputil must be able to support both ways of working, and there are a variety of approaches to solving this problem.

One approach would be for libescputil to use "accessor functions", specified by the application using it. Then escputil can provide accessor functions which directly read/write the device node, and the commandto* CUPS filters can provide accessor functions which use cupsBackChannelRead when approprate.

hpcups
HP printers communicate their ink levels via a "Device ID". This is a standard string containing fields identifying the manufacturer and model of a printer, as well as other things such as its serial number. HP printers use various methods of including ink level information in the Device IDs of its printers.

The hpcups driver is part of the HPLIP project, and hpcups has been re-implemented within the last year. Whereas the old implementation would report to CUPS about whether the ink supply was low or empty, the new implementation does not. So that's the first thing that it would be great to fix.

To do that, the driver will need to be modified to: a) fetch the Device ID from the printer b) decode the ink level information it contains c) report marker-supply-low/marker-supply-empty state reasons when appropriate (equals to 'fputs ("STATE: +marker-supply-low\n", stderr)').

The next thing is get it to report as much detail as it can about what the ink levels actually are. This informations reporting special CUPS attributes, 'fprintf (stderr, "ATTR: marker-levels=%d, %d,%d", level1, level2, level3)'.

Timeline
Before May 28th: proposal writing, source code reading, begin coding

work schedules
Next is my work schedules:

Week 1-4: Week 5-8:
 * 1) Adjust the existing escputil command from gutenprint so that it can use separate file descriptors for reading from and writing to the device
 * 2) Convert escputil into a library so that the gutenprint raster driver can use it
 * 3) Adjust the gutenprint raster driver to use it by changing the gutenprint CUPS command filters
 * 1) Learn the various ways HP printers communicate their marker levels using IEEE 1284 Device ID fields (base/status.py)
 * 2) Add low/empty marker detection to the hpcups driver
 * 3) Add fine-grained marker level detection to the hpcups driver

Week 9-11: Code debugging, documentation writing

Convincing
Last summer I participated in GSOC working on vlc. But at the end of this summer I hope to contribute to the Fedora Project as a regular contributer.

Impact
If your project is successfully completed, what will its impact be on the Fedora community? Give 3 answers, each 1-3 paragraphs in length. The first one should be yours. The other two should be answers from members of the Fedora community, at least one of whom should be a Fedora Summer Coding mentor. Provide email contact information for non-Summer Coding mentors.

answer 1
The Cups implies unified framework to report the printer ink level. It benefits not only the Federa but also various kinds of printer users.

answer 2
As well as improving ink level reporting for a large number of Fedora users (and Linux users generally), the existence of more than one printer driver showing how ink level reporting can be hooked into the CUPS marker attribute framework will hopefully function as an example to other printer driver authors.

In this way, I hope that more printer drivers will gain this functionality over time.

Tim Waugh

Miscellaneous
We want to make sure that you are prepared before the project starts
 * 1) Can you set up an appropriate development environment?  yes.
 * 2) Have you met your proposed mentor and members of the associated community?   Yes, I've talked with mentor clearly about our project goals and schedules.
 * 3) What is your t-shirt size? L

about Gutenprint
Gutenprint, formerly named Gimp-Print, is a suite of printer drivers that may be used with most common Unix and Linux print spooling systems, including CUOS, lpr, LPRng, or others. These drivers provide high quality printing for UNIX(including Mac OS) and linux systems that in many cases equal or exceed proprietray vendeor supplied drivers in quality and functionality.

escputil
escputil is a command line utility which allows the user to perform a variety of maintenance tasks on EPSON Stylus inkjet printers. These tasks include haed alginment, head cleaning, nozzle check, printer identification, and retrieval of the ink level from the printer. In order for many of the escputil functions to work, ther user must have read/write access to the raw printer device(typically /dev/lp0). On many systems,

-c Clean the print head. This does not require access to the raw printer device.

-n Print a nozzle check pattern. Dirty or clogged nozzles will show as gaps in the pattern

-a align the print head. This is an interactive operation that prints a number of test patterns and ask you to select the best algined patterns. This operation does not function on all printers.

-s Display printer status. This requires access to the raw printer device.

-i Display the quantity of ink remaining in the printer. This requires access the the raw printer device

-e Display extended ink information, including cartridge number and date of manufacture. This requires access to the raw printer device

-d indentify the printer. This requires access to the raw printer device

about hplips
HPLIP (Hewlett-Packard Linux Imaging & Printing) is an HP-developed solution for printing, scanning, and faxing with HP inkjet and laser based printers in Linux. The HPLIP project provides printing support for 1,971 printer models, including Deskjet, Officejet, Photosmart, PSC (Print Scan Copy), Business Inkjet, LaserJet, Edgeline MFP, and LaserJet MFP.

The HPLIP Project maintains a project page, hosted by SourceForge, Inc. at http://sourceforge.net/projects/hplip

The code of HPLIP contains C and Python. Like the level.py does part job of the ink level report:

for x in sorted_supplies: a, agent_kind, agent_type, agent_sku = x               agent_health = d.dq['agent%d-health' % a]                agent_level = d.dq['agent%d-level' % a]                agent_desc = d.dq['agent%d-desc' % a]                agent_health_desc = d.dq['agent%d-health-desc' % a]

if agent_health in (AGENT_HEALTH_OK, AGENT_HEALTH_UNKNOWN) and \ agent_kind in (AGENT_KIND_SUPPLY,                                   AGENT_KIND_HEAD_AND_SUPPLY,                                    AGENT_KIND_TONER_CARTRIDGE,                                    AGENT_KIND_MAINT_KIT,                                    AGENT_KIND_ADF_KIT,                                    AGENT_KIND_INT_BATTERY,                                    AGENT_KIND_DRUM_KIT,):

log.info(log.bold(agent_desc)) log.info("Part No.: %s" % agent_sku) log.info("Health: %s" % agent_health_desc) logBarGraph(agent_level, agent_type, size, color, bar_char) log.info("")

else: log.info(log.bold(agent_desc)) log.info("Part No.: %s" % agent_sku) log.info("Health: %s" % agent_health_desc) log.info("")

My GitHub
http://github.com/xiangwang/Unified_Ink_Report