Documentation for scanutil


scanutil is a collection of tools to assit in printing and scanning 'scanner sheets'. A reasonable level of effort has gone into making these routines be able to mask the intricacies of different scanner models (and a great deal of that is stored in the 'scanner-model' and 'form-bldg' files).

Implementation:

DEFINE {NEW}
Define a new instance of scanutil

CLEAN_UP
Release scanutil context. You *MUST* do this before exiting your program, regardless of why you are exiting your program!!!

SET BLDG (buff)
Pass a 'building' buffer to associate a building with the scanner utilities. This clears most everything in scanutil out upon success.

SET FORM (buff) (flag)
Pass a 'form-bldg' buffer to associate a form and scanner with the scanutil context. If the form-bldg buffer is OK, then 'flag' is passed back TRUE. You must have set the building with 'SET BLDG' before using SET FORM. If the building of the form and the previously set building do not match, FALSE is returned.

SET SCAN_DEVICE (device-id) (flag)
Used after you've set the building and form to associate the scanner device to be used for reading (not needed if printing sheets). Pass the 'scanner-device.scanner-device-id' of the device to use for scanning. If it is compatibile with the selected forms, then 'flag' is returned TRUE. Otherwise, it's FALSE.

VERIFY_PRINTER
This will register a callback with the prntutil routines to make sure the user selects only a printer that is capable of handle the printing of the scan forms. If you are not printing scan forms, do not register this callback.

CREATE BATCH (desc) (sdate) (edate) (buff)
This will create a new scanner batch entry and set it up for subsequent use. 'desc' is a textual, unique description of this batch. 'sdate' and 'edate' are date ranges the batch is for and should be as appropriate as possible. 'buff' is a scan-form-batch buffer that is passed back to you for an additional needs. Nothing is expected to be passed to CREATE BATCH through this buffer.

CREATE SHEET (buff)
This will create a new scan sheet entry for the previously selected batch. You should use this before you being printing a new sheet. 'buff' is passed back with a scan-form-sheet buffer loaded with the sheet entry just created. Nothing is expected to be passed to this method via 'buff'. You must have previously used either the 'CREATE BATCH' or 'LOAD BATCH' method before using CREATE SHEET.

After creating a sheet, you'll want to set some identification data into the sheet. This is useful for you as well as the user. If each sheet represents a student, set the stu-seq value into the buffer. If each sheet represents a course, set the course-id into the buffer. If each sheet represents a course from the master schedule, then set the course-id and section-id into the buffer. If it represents something else, there are MISC-NAME and MISC-ID fields, but you should consult with the architect before using this (to insure proper support gets built into the sheet lister).

CREATE LINE (student) (line#)
This will create a new data line for the most recently created/loaded scan sheet. You must pass a 'stu-seq' value in 'student'. If you are not using students, then pass an appropriate identifying value or '?'. 'line#' is passed back with the line # created for this dataline. You can use this to help determine when a sheet is full (check the scan-form-sheet.data-line-cnt to see the max # of datalines per sheet). You must have previously wither used the 'CREATE SHEET' method or a combination of the 'LOAD SHEET and LOAD DATASET' methods before using CREATE LINE.

Note: If you decide to manipulate the scan-form-line records, be aware there may be multiple entries per data line. This is because there is one entry per dataset.

LOAD BATCH (batchid) (flag)
Load a previously created batch. The batch must match the previously set form. 'batchid' is the ID of the batch to load. 'flag' is passed back TRUE if the batch is successfully loaded and matches the form.

LOAD SHEET (sheetseq) (flag)
Load a previous created sheet. The sheet must be part of the previously set batch. 'sheetseq' is the sheet seq# of the sheet to load. 'flag' is passed back TRUE if the sheet is successfully loaded and matches the batch.

LOAD DATASET (set#) (flag)
Load a dataset of the previously loaded sheet. Set# is the set to load and 'flag' is returned TRUE if the dataset is in range and loaded OK.

LOAD LINE (line#) (flag)
Load a line of a dataset using a previously loaded dataset. Line # is the line to load and 'flag' is returned TRUE if the line is in range and loaded.

GET BATCH (batchbuff)
Load the passed 'scan-form-batch' buffer with what we beleive to be the current scan batch. This call is usually used when scanning data in.

GET SHEET (sheetbuff)
Load the passed 'scan-form-sheet' buffer with what we beleive to be the current sheet. This call is usually used when scanning data in and needing to extract info from the sheet read.

GET DATASET (datasetbuff)
Load the passed 'scan-form-dataset' buffer with that we beleive to be the current dataset for the scan batch. This call is ususally used when scanning data in.

GET LINE (linebuff)
Load the passed 'scan-form-line' buffer with what we beleive to be gte current line. Usually used when scanning data

GET FORM (formbldg-buff)
Load the passed 'for-bldg' buffer with the form last set into SCANUTIL via the SET FORM directive.

SLUGS INITIALIZE (prtseq)
Returns an initialization string that should be fed to the printer that will eventually print slug marks. This could be an empty string if there is no initialization for the printer.

The resultant string should be printed using the PUT-CONTROL service of 'prntutil' as it may have escaped control characters in it.

If you will be printing slugs on an HP laser form, you must pass two form parameters to the form. [LASER] specifies that this is a laser form, and [ORIGIN:x,y] specifies the starting location for the slug marks. Using the offset of PRINT_ALIGNMENT or PRINT_LINE will not work for HP printers.

You must use this service *BEFORE* using PRINT_ALIGNMENT or PRINT_LINE.

SLUGS PRINT_ALIGNMENT (line) (offset) (prtseq)
This works the same way the 'PRINT_LINE' work, but instead of needing a sheet, it will produce an 'alignment' pattern of slugs to print. This will not disturb any previously loaded sheets. Other than printing an alignment pattern, this works exactly like 'PRINT_LINE'

You must call and print SLUGS INITIALIZE before using this service.

SLUGS PRINT_LINE (line) (offset) (prtseq)
Generates the necessary characters to print a graphic slug or slugs for identifying the sheet. Each scan sheets identification area is described in the 'form-bldg' record as a rectangle of a given number of rows by a given number of columns.

'line' specifies which line in the slug box you are printing. 'offset' is the horizontal position on the sheet where the first character of the identification slug box should appear (measured in the appropriate CPI for the form (usually 10 CPI)). 'prtseq' is a character variable that will be filled with an escape sequence used to print the slugs for the passed line at the passed horizontal offset. You should use the 'PUT-CONTROL' service in 'prntutil' to write this out since it may contain specially quoted escape characters.

The 'prtseq' doesn't depend on the print header being anywhere and the location of the print head after printing is undefined. You should print any textual data that will appear on the line *BEFORE* printing the 'prtseq' variable. In addition, it is your responsibility to move the printhead to the next line when you printed all the slug marks you intend on printing (using a PUT .... SKIP perhaps).

You can 'optimize' things a bit by checking the length of 'prtseq' on return. If it's 0, then you do not need to print anything for the current line.

After the last slug line is returned, the 'prtseq' is returned with the value "DONE" in the string.

You must call and print SLUGS INITIALIZE before using this service.

LIST BATCH (batchid)
Presents the user with a listing of the various batches for the previously selected form. 'batchid' is the ID (not RECID) of the batch to start the lister on. If passed as '?', then an attempt is made to find the best starting point in the batches (using dates). On return, 'batchid' is the ID of the batch the user selected or "?" if the user hit or there were no batches.

LIST SHEET (sheetid)
Presents the user with a lsiting of the various sheets for the previously selected form/batch. 'sheetid' is the ID (form-seq, not RECID) of the sheet to start the lister at. On return, it is either the form-seq of the sheet selected or ? if the user pressed or there were no sheets.

LIST SHEET makes an attempt to provide useful information about each sheet. The validity and usefullness of the data is highly dependent on you setting the appropriate values into the 'scan-form-sheet' buffer after the sheet was created.

LIST SCAN_DEVICE (deviceid)
Present the user with a list of the various scanner devices. We'll make sure the user selects a scanner the is compatible with the selected form. You must have 'SET FORM' before calling this lister. If the user successfully chooses a scanner, the device-id of it is returned. If the user cancels, then ? is returned.

SCANNER OPEN (status)
This will open the selected scanner, initialize it and prepare it to read the sheets from the scanner. You must have previously set the building, form and scanner device in place. If the open is successful, 'status' is passed back with 'OKAY' in it. Consult the discussion on interface modules for list of supported failure codes. as TRUE.

SCANNER CLOSE (status)
This will close the previously opened scanner device and cleanup any working files and/or OS dependencies. 'status' will have 'OKAY' in it if the close was successful, or one of the listed failure types if not.

SCANNER READ_SHEET (readstat) (sheetid) (dataset#) (sheetdata)
This will attempt to read a sheet from the previously opened scanner device. This will attempt to read the sheet header and find the form identification. If that is succesfull, the sheet is loaded internally and the ID of the sheet is returned, along with the scan data.

'readstat' is a character and can be returned with one of the following:

sheetid is the ID of the sheet associated with this sheet or ? if a valid sheet wasn't read.

dataset# is the dataset number associated with the form. If the sheet doesn't have datasets, this will be returned as 0.

'sheetdata' is a character with the data returned from the scanner, less the sheet identification (which has been stripped off). No other processing is done to the line.

SHEET_PRINTED
Marks the last sheets loaded/created as printed. This sets the printed flags for the sheet and batch. You should do this only AFTER you have actually printed the sheet. You cannot work with the scan sheet after this call (think of it as 'releasing' the sheet.

SHEET_SCANNED
Marks the last sheet read from the scanner as actually being read. This is used becuse a sheet may prove to be 'unread' if it seemed OK but contained invalid data. You should mark a sheet read only AFTER you have journal the sheet data to the database. You cannot access the scan sheet data after this call (it 'releases' the data).

LINE_SCANNED (mark) (mark#) (flush)
Marks the last line read from the scanner as actually read and sets the raw scanner marks into the record. 'mark' is the text of the raw mark to save for the line. This should be exactly what you received from the scanner, don't "cook" it!. 'mark#' is the mark on the sheet to record (1-64). Most sheets only use mark #1 (grade sheets may use more, for example). 'flush' should be set on the last LINE_SCANNED call for a given line. Since multiple marks may need to be recorded, we can't auto-flush it.

You should do each LINE_SCANNED before using SHEET_SCANNED. You must has previoustly loaded a line with either LOAD LINE or CREATE LINE.

Scanner Protocol for support mods

Scanning is a complex area. There all types of operating system issues, scanner issues and communications issues. This doesn't even touch on the vaugaries or interpretting data.

scanutil will do quite a bit of work for you, but should be aware of how it expects to deal with the various scanner-device.device-types:

File


Files are very simple. The device specification in the scanner-device. device-read-spec is checked to see if it's an O/S file and if so, it is opened for additional input. Eventually, a token will be allowed in the 'device-read-spec' to allow interactive prompting. File types are intended for folks who do the scanning on a PC and the xfer the resultant file over the the system for importation.

Filter


Filter's are only available on Unix and heavily use the O/S pipe facility. They give you a great deal of power, but can be harder to work with.

Of all the options outlined below, the only required module is the scannner-device.device-read-spec field.

There are two types of 'helpers' in a filter - O/S modules and Progress modules. All O/S module definition fields end in '-spec' and all Progress module definition fields end in '-mod'.

The basic calling order during an open is

Sheets are read via form-bldg.scanner-read-mod.

The basic calling order during a close is

All modules, except the 'scanner-device.device-read-spec' and the 'form-bldg.form-read-sheet-mod' are optional.

O/S modules receive the following parameters on the command line

The device-parm and form-parm are quoted to preserve any spaces. All O/S modules are expected to echo a 'OKAY' to stdout if they are OK. All O/S modules, except the scanner-device.device-read-spec, are expected to complete their work and return 'OKAY' (or an error description if there was an error) immediatly.

scanner-device.device-read-spec has a special form when responding. When done initializing, it should send an 'OKAY' out and then, on the next line, some combination of these flags:

If neither are present, the default is a non-interactive scanner that cannot be stopped except by the user pressing the end of batch key.

When at all possible, you want your scanner to support interactive and cancel facilities.

Progress modules receive the following parameters

ReadProcedure, WriteProcedure and Procedure handle provide you with the tools to read/write from a stream. This is because you cannot pass/share streams. Their calling format is:

  1. RUN ReadProcedure IN ProcedureHandle (OUTPUT LineRead).
  2. RUN WriteProcedure IN ProcedureHandle (TextToOutput).

ReadProcedure will return one line at a time. WriteProcedure will write the text out and will not append any line terminators. WriteProcedure uses the 'prntutil.i PUT-CONTROL' mehtod to handle the need to write NUL characters to the stream (see prntutil for more info).

If ReadProcedure encounters an error trying to read data from the scanner, the string '*** READ ERROR ***' is returned.

The form-bldg, scanner-model and scanner-device buffers just serve as references that a program can use.

The module is expected to return 'OKAY' in 'ReturnStatus' on completion or an explanation if there was an error.

Device


Devices are intended for systems without pipes that still need to be able to directly interface with a scanner. This is generally much less flexible than a 'Filter' reader, but the only option for other systems.

Device scanner types are not currently supported and will always return a 'BAD_DEVICE' status if called. (3/25/95)

Return Status values


The interface routines can expect to return a status code as a character variable. These codes are eventually returned to the user so they can apprise the end-user why something worked or didn't. This is the list of supported codes:

Notes on scanner form design

In order for a scanner sheet to be compatible with the scanutil services, the output from the scanner must follow some basic guidelines.

Note: scanutil.i depends on the scanner being able to some limited forms processing. This should involve interpretting marks and sending back ASCII, human readable results. This pretty much outs old scanners like ScanTron 5200 and earlier, but should work great with ScanTron 8200's, NCS's OpScan line and any other scanner designed within the last few years.

Note: mark resolution should be as follows. A field with illegal multiple marks should have a '*' as the multiple-mark character. A field with no mark, even if it's required, should send a space back.

Scanner Form header

The scanner form header consists of space delimited fields. They are (in order):

The numeric strings can be zero filled or not (i.e. "000123" is as good as "123"). There must be at least one field after each field (more than that is OK as well) except the last field.

This header information is processed by scanutil itself before returning wether a sheet is OK or not. This data is stripped from the received data string before being returned to you so there is no additional processing needed before beginig the processing of the scanned info.

The actual scan data begins immediatly after this last field with no intervening spaces or other characters.

If your scanner puts things in fron of each record (like NCS OpScans), then use the scanner-model.sheet-preamble-len field to say how many characters should be stripped off.

Received data:

When the sheet is read, the ID, check sum and dataset numbers (and terminating spaces) are removed from the string and the remainder of it is passed back to the caller for final interpretation. No trimming or cleanup on the string is done at all.


Last update: October 31, 1995