<> <> <> <<>> DIRECTORY BasicTime USING [Now], CrRPC USING [BulkDataCheckAbortProc, BulkDataSource, Handle], FS USING [Delete, Error, StreamOpen], IO USING [rope, card, Close, PutF, PutFR1, RopeFromROS, ROS, STREAM, Value], PrintingP4V3Aux USING [ExposePrintAttributes, ExposePrintOptions], Process USING [Detach], Rope USING [ROPE], PrintingP4V3, XNSPSMessages USING [LogMessage], XNSPSPrint USING [PrintFile], XNSPSSpooler; XNSPSSpoolerImpl: CEDAR MONITOR IMPORTS BasicTime, FS, IO, PrintingP4V3Aux, Process, XNSPSMessages, XNSPSPrint EXPORTS XNSPSSpooler, PrintingP4V3 ~ { OPEN PrintingP4V3; <<>> <> <<>> BulkDataSource: TYPE ~ CrRPC.BulkDataSource; Handle: TYPE ~ CrRPC.Handle; ROPE: TYPE ~ Rope.ROPE; <> properties: PrintingP4V3.PrinterProperties; <> spoolerBusy, formatterBusy, printerBusy: BOOL _ FALSE; State: TYPE = {empty, spooling, spooled, printing, done, aborted}; maxQueue: CARDINAL = 10; QueueIndex: TYPE = [0 .. maxQueue); nextEmpty: QueueIndex _ 0; -- Next available. queue: PrintQueue; queueChanged: CONDITION; queueCount: INTEGER _ 0; PrintQueue: TYPE = ARRAY QueueIndex OF PrintQueueEntry; PrintQueueEntry: TYPE = RECORD [ id: PrintingP4V3.RequestID _ [0, 0, 0, 0, 0], fileName: ROPE _ NIL, state: State _ empty, printAttributes: PrintingP4V3.PrintAttributes _ NIL, printOptions: PrintingP4V3.PrintOptions _ NIL ]; <> ConstructPrinterStatus: PROC [spoolerBusy, formatterBusy, printerBusy: BOOL] RETURNS [printerStatus: PrinterStatus] ~ { printerStatus _ NEW[ PrinterStatusObject[4] ]; TRUSTED {printerStatus.body[0] _ [spooler[IF spoolerBusy THEN busy ELSE available]]}; TRUSTED {printerStatus.body[1] _ [formatter[IF formatterBusy THEN busy ELSE available]]}; TRUSTED {printerStatus.body[2] _ [printer[IF printerBusy THEN busy ELSE available]]}; TRUSTED {printerStatus.body[3] _ [media[ConstructMedia[]]]}; }; <<>> <> <<>> Logger: ENTRY PROC [format: Rope.ROPE _ NIL, v1, v2, v3, v4, v5: IO.Value _ [null[]], id: PrintingP4V3.RequestID] ~ { out: IO.STREAM _ IO.ROS[]; r: ROPE; IO.PutF[out, format, v1, v2, v3, v4, v5]; r _ IO.RopeFromROS[out]; XNSPSMessages.LogMessage[r, id]; }; <> <<>> Busy: PUBLIC ERROR ~ CODE; ConnectionError: PUBLIC ERROR [problem: ConnectionProblem] ~ CODE; InsufficientSpoolSpace: PUBLIC ERROR ~ CODE; InvalidPrintParameters: PUBLIC ERROR ~ CODE; MasterTooLarge: PUBLIC ERROR ~ CODE; MediumUnavailable: PUBLIC ERROR ~ CODE; ServiceUnavailable: PUBLIC ERROR ~ CODE; SpoolingDisabled: PUBLIC ERROR ~ CODE; SpoolingQueueFull: PUBLIC ERROR ~ CODE; SystemError: PUBLIC ERROR ~ CODE; TooManyClients: PUBLIC ERROR ~ CODE; TransferError: PUBLIC ERROR [problem: TransferProblem] ~ CODE; Undefined: PUBLIC ERROR [problem: CARDINAL] ~ CODE; <<>> <> currentID: PrintingP4V3.RequestID _ [0, 0, 0, 0, 0]; GetNextID: ENTRY PROC [] RETURNS [id: PrintingP4V3.RequestID] = { ENABLE UNWIND => NULL; md: MACHINE DEPENDENT RECORD [lo, hi: CARDINAL]; i: CARD; md1: MACHINE DEPENDENT RECORD [lo, hi: CARDINAL] _ LOOPHOLE[BasicTime.Now[]]; md.lo _ currentID[4]; md.hi _ currentID[3]; i _ LOOPHOLE[md]; i _ i + 1; md _ LOOPHOLE[i]; currentID[4] _ md.lo; currentID[3] _ md.hi; currentID[2] _ md1.lo; currentID[1] _ md1.hi; RETURN [currentID]; }; GetPrinterProperties: PUBLIC PROC [h: Handle] RETURNS [PrinterProperties] ~ { <> RETURN[properties]; }; ConvertToRequestStatus: PROC [s: State] RETURNS [PrintingP4V3.InterpressMasterStatus] = { RETURN [SELECT s FROM empty => unknown, spooling => pending, spooled => pending, printing => inProgress, done => completed, aborted => aborted, ENDCASE => ERROR]; }; GetPrintRequestStatus: PUBLIC PROC [h: Handle, printRequestID: RequestID] RETURNS [status: RequestStatus] ~ TRUSTED { <> FOR i: QueueIndex IN QueueIndex DO IF queue[i].id = printRequestID THEN { status _ NEW[RequestStatusObject[1]]; status.body[0] _ [status[ConvertToRequestStatus[queue[i].state]]]; RETURN [status]; }; ENDLOOP; status _ NEW[RequestStatusObject[1]]; status.body[0] _ [status[unknown]]; RETURN [status]; }; EnumerateQueue: PUBLIC PROC [ p: PROC [id: PrintingP4V3.RequestID, status: PrintingP4V3.InterpressMasterStatus, printAttributes: PrintingP4V3.PrintAttributes, printOptions: PrintingP4V3.PrintOptions] RETURNS [continue: BOOL]] = BEGIN FOR i: QueueIndex IN QueueIndex DO cont: BOOL; cont _ p[queue[i].id, ConvertToRequestStatus[queue[i].state], queue[i].printAttributes, queue[i].printOptions]; IF ~cont THEN EXIT; ENDLOOP; END; GetPrinterStatus: PUBLIC PROC [h: Handle] RETURNS [status: PrinterStatus] ~ { <> status _ ConstructPrinterStatus[spoolerBusy, formatterBusy, printerBusy]; RETURN[status]; }; FindQueueSlot: ENTRY PROC RETURNS [slot: QueueIndex] = { ENABLE UNWIND => NULL; FOR i: QueueIndex IN QueueIndex DO IF queue[i].state = empty OR queue[i].state = done OR queue[i].state = aborted THEN { queue[i].state _ spooling; RETURN[i]; }; ENDLOOP; ERROR SpoolingQueueFull; }; Print: PUBLIC PROC [h: Handle, master: BulkDataSource, printAttributes: PrintAttributes, printOptions: PrintOptions] RETURNS [printRequestID: RequestID] ~ { <> current: QueueIndex; MarkQueued: ENTRY PROC = { ENABLE UNWIND => NULL; queue[current].state _ spooled; queueCount _ queueCount + 1; BROADCAST queueChanged; }; aborted, sendAbort: BOOL _ FALSE; out: IO.STREAM _ NIL; CheckAbort: CrRPC.BulkDataCheckAbortProc ~ {abort _ sendAbort}; { -- for exit printRequestID _ GetNextID[]; Logger[format: "Print called:", id: printRequestID]; Logger[format: " printAttributes: %g", v1: IO.rope[PrintingP4V3Aux.ExposePrintAttributes[printAttributes, 2]], id: printRequestID]; Logger[format: " printOptions: %g", v1: IO.rope[PrintingP4V3Aux.ExposePrintOptions[printOptions, 2]], id: printRequestID]; current _ FindQueueSlot[]; queue[current].id _ printRequestID; queue[current].state _ spooling; spoolerBusy _ TRUE; queue[current].printAttributes _ printAttributes; queue[current].printOptions _ printOptions; FS.Delete[queue[current].fileName ! FS.Error => CONTINUE]; out _ FS.StreamOpen[queue[current].fileName, $create]; aborted _ master[h, out, CheckAbort ! FS.Error => {sendAbort _ TRUE; GOTO Abort}]; IF out # NIL THEN IO.Close[out]; IF aborted THEN GOTO Abort; spoolerBusy _ FALSE; MarkQueued[]; EXITS Abort => { FS.Delete[queue[current].fileName ! FS.Error => CONTINUE]; queue[current].state _ empty; spoolerBusy _ FALSE; ERROR TransferError[aborted]; }; }; -- for exit }; DecomposeAndPrint: PROC [] = { Wait: ENTRY PROC [] ={ENABLE UNWIND => NULL; WAIT queueChanged}; current: QueueIndex _ 0; potential: QueueIndex; found, abort, ok: BOOL _ FALSE; DO -- Forever WHILE queueCount <= 0 DO Wait[] ENDLOOP; found _ FALSE; FOR i: QueueIndex IN QueueIndex DO potential _ (current + i) MOD maxQueue; IF queue[potential].state = spooled THEN {current _ potential; found _ TRUE; EXIT}; ENDLOOP; IF ~found THEN LOOP; -- Excess queueChanged queue[current].state _ printing; abort _ FALSE; formatterBusy _ printerBusy _ TRUE; ok _ XNSPSPrint.PrintFile[id: queue[current].id, fileName: queue[current].fileName, printAttributes: queue[current].printAttributes, printOptions: queue[current].printOptions ! ABORTED => {abort _ TRUE; CONTINUE}]; queue[current].state _ IF abort OR ~ok THEN aborted ELSE done; formatterBusy _ printerBusy _ FALSE; queueCount _ queueCount - 1; ENDLOOP; -- Forever }; <> ConstructPrinterProperties: PROC [] RETURNS [properties: PrinterProperties] ~ { properties _ NEW[ PrinterPropertiesObject[3] ]; TRUSTED { properties.body[0] _ [ppmedia[ConstructMedia[]]] }; TRUSTED { properties.body[1] _ [ppstaple[FALSE]] }; TRUSTED { properties.body[2] _ [pptwoSided[FALSE]] }; }; ConstructMedia: PROC [] RETURNS [media: Media] ~ { media _ NEW[ MediaObject[1] ]; TRUSTED { media.body[0] _ [paper[paper: [knownSize[usLetter]]]] }; <> }; SetUpQueue: PROC [] = { FOR i: QueueIndex IN QueueIndex DO queue[i].fileName _ IO.PutFR1["///Spool/File%02g.ip", IO.card[i]]; FS.Delete[queue[i].fileName ! FS.Error => CONTINUE]; ENDLOOP; }; Init: ENTRY PROC ~ { properties _ ConstructPrinterProperties[]; SetUpQueue[]; TRUSTED {Process.Detach[FORK DecomposeAndPrint[]]}; }; Init[]; }...