-- Copyright (C) Xerox Corporation 1984, 1985. All rights reserved. -- MergeControlImpl.mesa -- Last edited by Jacks 6-Nov-85 14:01:41 <<This module controls the merge operation. Currently, the only merge operation supported is for the feps9700 (Formatting Print Service)..the FEPSMerge interface. During the decomposition phase of FEPS, the text elements are separated from the graphics elements. The text elements are stored in the form of IP master, and the graphics elements are converted into bands lists as is the case with the usual decomposition operation. Then during the "merge" operation, the text elements and graphics elements are merged, page at a time, into another IP master. Actually, bands list for a page is first BandBLTed and then compressed before being "merged" with text elements. Therefore, the merge operation as it is currently implemented does the conversion of bands list into compressed pixel array as well as merging.>> DIRECTORY ControlMessages USING [Key], DecomposerControl USING [FreeDecomposeObject], Environment USING [bytesPerPage], FEPSMerge USING [ Cancel, Code, Create, ExceptionCode, ExceptionProc, FinishedCode, FreeMergeJob, Handle, Initialize, MergeDoc, PlatesMerged], Heap USING [systemZone], MergeControl USING [CancelProc, NotifyProc, TraceLevel, WaitChoice], NSFile USING[ Close, Error, Handle, ID, nullAttributeList, nullHandle, nullID, nullTime, Session, Time], NSString USING [ AppendString, FreeString, MakeString, nullString, String, StringFromMesaString], PrintingTypes USING [Option], PrintQueue USING [ Empty, maxStringBytes, nilQueueObjectHandle, ObjectStatus, QueueObjectHandle, QueueStage, QueueWatcher, Requeue], Process USING [ Detach, Priority, priorityBackground, SecondsToTicks, SetPriority, SetTimeout], PSAsyncMsg USING [ExpandArrayAndPutString, Proc, PutMesaString], PSKMessages USING [GetHandle], PSVolume USING [GetDefaultSession], QueueFile USING [CreateError, CreateTempFile, Delete, MakePermanent], String USING [ AppendDecimal, AppendLongDecimal, Overflow, StringBoundsFault], System USING [ gmtEpoch, GetGreenwichMeanTime, GreenwichMeanTime, Overflow, SecondsSinceEpoch], XMessage USING [Get, Handle], XString USING [ReaderBody]; MergeControlImpl: MONITOR IMPORTS DecomposerControl, FEPSMerge, Heap, NSFile, NSString, PrintQueue, Process, PSAsyncMsg, PSKMessages, PSVolume, QueueFile, String, System, XMessage EXPORTS MergeControl SHARES XString = BEGIN Control: TYPE = RECORD [ stop: BOOLEAN ← TRUE, start: BOOLEAN ← FALSE, jobQueueEntry: BOOLEAN ← FALSE, tpQueueEntry: BOOLEAN ← FALSE ]; State: TYPE = RECORD [ stopped: BOOLEAN ← TRUE, merging: BOOLEAN ← FALSE, mergeHandle: FEPSMerge.Handle ← NIL, --currently only one merge handle allowed tpJob: BOOLEAN ← FALSE, mergeFile: NSFile.Handle ← NSFile.nullHandle, mergeFileID: NSFile.ID ← NSFile.nullID, trace: MergeControl.TraceLevel ← none, qOH: PrintQueue.QueueObjectHandle ← PrintQueue.nilQueueObjectHandle, exceptions: CARDINAL ← 0, -- FEPSMerge.Exception calls stopStatus: PrintQueue.ObjectStatus ← canceledInMerger, -- specified in Stop call notifyProc: MergeControl.NotifyProc ← NIL, canceledWhileMerging: MergeControl.CancelProc ← NIL, putAsyncMsgFromKey: PSAsyncMsg.Proc ← NIL, startMergeTime: NSFile.Time ← NSFile.nullTime ]; event: CONDITION; stopped: CONDITION; control: Control ← []; state: State ← []; -- Message domain handle controlMsgs: XMessage.Handle ← PSKMessages.GetHandle[control]; session: NSFile.Session = PSVolume.GetDefaultSession[]; Init: PUBLIC ENTRY PROCEDURE [ currentOption: PrintingTypes.Option, priority: Process.Priority ← Process.priorityBackground, notifyProc: MergeControl.NotifyProc, putAsyncMsgFromKey: PSAsyncMsg.Proc] = BEGIN IF currentOption # feps9700 THEN ERROR; --only engine currently supporting merge operation. FEPSMerge.Initialize[ engineType: currentOption, session: session, finishedProc: Finished, exceptionProc: Exception]; state ← [ mergeHandle: FEPSMerge.Create[], notifyProc: notifyProc, putAsyncMsgFromKey: putAsyncMsgFromKey]; PrintQueue.QueueWatcher[proc: JobQueueEntry, queueStage: decomposed]; PrintQueue.QueueWatcher[proc: TPQueueEntry, queueStage: tpDecomposed]; Process.SetTimeout[@event, Process.SecondsToTicks[seconds: 30]]; --check queue every 30 secs Process.Detach[FORK MergeControlLoop[priority]]; END; --Init Start: PUBLIC ENTRY PROCEDURE = BEGIN control.start ← TRUE; control.stop ← FALSE; NOTIFY event; END; --Start Stop: PUBLIC ENTRY PROCEDURE [status: PrintQueue.ObjectStatus, canceledWhileMerging: MergeControl.CancelProc ← NIL] = BEGIN state.stopStatus ← status; state.canceledWhileMerging ← canceledWhileMerging; control.stop ← TRUE; control.start ← FALSE; NOTIFY event; END; --Stop Stopped: PUBLIC ENTRY PROCEDURE [wither: MergeControl.WaitChoice ← dontWait] RETURNS [BOOLEAN] = BEGIN IF wither = wait THEN UNTIL state.stopped DO WAIT stopped; ENDLOOP; RETURN[state.stopped]; END; --Stopped Status: PUBLIC PROCEDURE RETURNS [merging: BOOLEAN] = BEGIN RETURN[state.merging]; END; --Status ModifyTraceLevel: PUBLIC ENTRY PROCEDURE [trace: MergeControl.TraceLevel] = BEGIN state.trace ← trace; END; --ModifyTraceLevel JobQueueEntry: ENTRY PROCEDURE = -- called by PrintQueue BEGIN control.jobQueueEntry ← TRUE; NOTIFY event; IF state.trace = verbose THEN PSAsyncMsg.PutMesaString["$$Merger JobQEntry"L]; END; --JobQueueEntry TPQueueEntry: ENTRY PROCEDURE = --called by PrintQueue when TestPattern is queued BEGIN control.tpQueueEntry ← TRUE; NOTIFY event; IF state.trace = verbose THEN PSAsyncMsg.PutMesaString["$$Merger TPQEntry"L]; END; --TPQueueEntry MergeControlLoop: ENTRY PROCEDURE [priority: Process.Priority] = BEGIN Process.SetPriority[priority]; DO --forever SELECT TRUE FROM control.tpQueueEntry AND NOT state.merging => BEGIN --over-rides state.stopped state.qOH ← PrintQueue.Requeue[fromQueue: tpDecomposed, toQueue: merging]; IF state.qOH = PrintQueue.nilQueueObjectHandle THEN BEGIN control.tpQueueEntry ← FALSE; LOOP; END; state.tpJob ← TRUE; MergeDocument[]; END; control.stop => BEGIN SELECT TRUE FROM --disables merging; merge in progress is aborted state.stopped => NULL; state.merging => BEGIN FEPSMerge.Cancel[state.mergeHandle]; Queue[status: state.stopStatus]; END; ENDCASE; state.stopped ← TRUE; NOTIFY stopped; END; control.start => BEGIN state.stopped ← control.start ← FALSE; -- so we don't keep looking here LOOP; -- make another pass END; state.stopped => NULL; state.merging => NULL; --so we don't start multiple merge operations control.jobQueueEntry => BEGIN state.qOH ← PrintQueue.Requeue[fromQueue: decomposed, toQueue: merging]; IF state.qOH = PrintQueue.nilQueueObjectHandle THEN BEGIN control.jobQueueEntry ← FALSE; LOOP; END; MergeDocument[]; END; ENDCASE; WAIT event; ENDLOOP; END; --MergeControlLoop MergeDocument: INTERNAL PROCEDURE = BEGIN code: FEPSMerge.Code; retried: BOOLEAN ← FALSE; sMerging: LONG STRING = "$$Merging ""<1>"""L; nsMerging: NSString.String = Str[sMerging]; mergeFileSuffix: NSString.String = Str[".IV"]; mergeFileName: NSString.String ← NSString.MakeString[ z: Heap.systemZone, bytes: PrintQueue.maxStringBytes + 10]; BEGIN ENABLE UNWIND => NSString.FreeString[z: Heap.systemZone, s: mergeFileName]; IF state.trace # none THEN BEGIN stringArray: ARRAY [0..1) OF NSString.String; stringArray[0] ← state.qOH.fileName; PSAsyncMsg.ExpandArrayAndPutString[nsMerging, DESCRIPTOR[stringArray]]; END; mergeFileName ← NSString.AppendString[to: mergeFileName, from: state.qOH.fileName]; mergeFileName ← NSString.AppendString[to: mergeFileName, from: mergeFileSuffix]; --Create merge file (merge file initially set to be --1 page long, merger redefines it to correct length for data): [state.mergeFileID, state.mergeFile] ← QueueFile.CreateTempFile[ fName: mergeFileName, fBytes: Environment.bytesPerPage ! QueueFile.CreateError => IF retried THEN GOTO OutOfSpace ELSE IF state.notifyProc # NIL THEN BEGIN keepGoing: BOOLEAN ← state.notifyProc[ queueObject: state.qOH, code: noResources, continuable: TRUE]; IF keepGoing THEN {retried ← TRUE; RETRY} ELSE GOTO OutOfSpace; END ELSE GOTO OutOfSpace; ]; state.exceptions ← 0; state.merging ← TRUE; state.qOH.currentStatus ← merging; state.startMergeTime ← System.GetGreenwichMeanTime[]; code ← FEPSMerge.MergeDoc[ handle: state.mergeHandle, output: state.mergeFile, inputObjectH: state.qOH.printObjectHandle, bannerOnly: state.qOH.bannerOnly, paperSize: state.qOH.paper]; IF code # ok THEN ERROR; NSString.FreeString[z: Heap.systemZone, s: mergeFileName]; EXITS OutOfSpace => BEGIN NSString.FreeString[z: Heap.systemZone, s: mergeFileName]; Queue[status: mergeFailure]; END; END; --enable clause and code END; --MergeDocument Finished: ENTRY PROCEDURE [ handle: FEPSMerge.Handle, code: FEPSMerge.FinishedCode] = --parameter to FEPSMerge.Initialize BEGIN IF code = bannerOnly AND NOT state.qOH.bannerOnly THEN BEGIN state.qOH.bannerOnly ← TRUE; state.qOH.priorStatus ← mergeFailure; --After the banner is forwarded this "priorStatus" will become the "currentStatus". END; Queue[status: IF code = noOutput THEN mergeFailure ELSE merged]; END; --Finished Exception: ENTRY FEPSMerge.ExceptionProc = <<ExceptionProc: TYPE = PROCEDURE [ handle: Handle, code: ExceptionCode, continuable: BOOLEAN] RETURNS [keepGoing: BOOLEAN]>> BEGIN --NOTE: Do we need a way to get error msgs and put them in queue object? --"Msg" parameter was removed in 10.0. IF state.trace # none THEN BEGIN sPage: LONG STRING ← [10]; stringArray: ARRAY [0..5) OF NSString.String; atPage: CARDINAL ← FEPSMerge.PlatesMerged[state.mergeHandle]; sPlates: LONG STRING ← [10]; sMergeException: LONG STRING ← "$$Merge Exception: <1>File: ""<2>""; Plate: <3>; <4>Continuable"L; sNot: LONG STRING ← "NOT "L; nsMergeException: NSString.String ← Str[sMergeException]; nsNot: NSString.String ← Str[sNot]; stringArray[0] ← MergeCodeToString[code]; stringArray[1] ← state.qOH.fileName; String.AppendDecimal[sPlates, atPage]; stringArray[2] ← Str[sPlates]; stringArray[3] ← IF NOT continuable THEN nsNot ELSE NSString.nullString; PSAsyncMsg.ExpandArrayAndPutString[nsMergeException, DESCRIPTOR[stringArray]]; END; state.exceptions ← state.exceptions + 1; keepGoing ← IF code = noResources THEN state.notifyProc[state.qOH, noResources, continuable] ELSE state.notifyProc[state.qOH, other, continuable]; END; --Exception Queue: INTERNAL PROCEDURE [status: PrintQueue.ObjectStatus] = BEGIN startSinceEpoch: LONG CARDINAL; endSinceEpoch: LONG CARDINAL; IF state.qOH = PrintQueue.nilQueueObjectHandle THEN BEGIN --This could be true if Queue gets called twice (once by --MergeControlLoop and once from Finished) in case that doc --finishes decomposing while decomposition is being suspended. IF state.trace # none THEN PSAsyncMsg.PutMesaString["##Can't Queue nilQOH! "L]; RETURN; END; startSinceEpoch ← System.SecondsSinceEpoch[state.startMergeTime]; endSinceEpoch ← System.SecondsSinceEpoch[System.GetGreenwichMeanTime[]]; IF endSinceEpoch >= startSinceEpoch THEN state.qOH.markTime ← endSinceEpoch - startSinceEpoch; IF state.trace # none THEN BEGIN sDoneMerging: LONG STRING ← "$$Done Merging ""<1>""; status= <2>; Plates= <3>; seconds= <4>"L; sPlates: LONG STRING ← [10]; sSeconds: LONG STRING ← [10]; nsDoneMerging: NSString.String ← Str[sDoneMerging]; stringArray: ARRAY [0..4) OF NSString.String; stringArray[0] ← state.qOH.fileName; stringArray[1] ← Str[SELECT status FROM restart => "restart"L, canceledInMerger => "canceledInMerger"L, mergeFailure => "mergeFailure"L, ENDCASE => "merged"L]; String.AppendDecimal[sPlates, FEPSMerge.PlatesMerged[state.mergeHandle]]; stringArray[2] ← Str[sPlates]; String.AppendLongDecimal[sSeconds, state.qOH.markTime ! System.Overflow, String.Overflow, String.StringBoundsFault => CONTINUE]; stringArray[3] ← Str[sSeconds]; PSAsyncMsg.ExpandArrayAndPutString[nsDoneMerging, DESCRIPTOR[stringArray]] END; state.qOH.currentStatus ← status; FEPSMerge.FreeMergeJob[state.mergeHandle]; SELECT TRUE FROM status = restart => BEGIN --Closing the temporary merge file will delete it... IF state.mergeFile # NSFile.nullHandle THEN NSFile.Close[state.mergeFile, session ! NSFile.Error => CONTINUE]; --Free decompose handle (doc will be decomposed again)... IF state.qOH.printObjectHandle # NIL THEN BEGIN DecomposerControl.FreeDecomposeObject[state.qOH.uid]; state.qOH.printObjectHandle ← NIL END; [] ← PrintQueue.Requeue[qOH: state.qOH, fromQueue: merging, toQueue: IF state.tpJob THEN aborted ELSE spooledNormal, position: front]; END; status = canceledInMerger => BEGIN --Closing the temporary merge file will delete it... IF state.mergeFile # NSFile.nullHandle THEN NSFile.Close[state.mergeFile, session ! NSFile.Error => CONTINUE]; [] ← PrintQueue.Requeue[ qOH: state.qOH, fromQueue: merging, toQueue: aborted]; IF state.canceledWhileMerging # NIL THEN state.canceledWhileMerging[state.qOH.fileName, status]; END; status # merged => BEGIN --Closing the temporary merge file will delete it... IF state.mergeFile # NSFile.nullHandle THEN NSFile.Close[state.mergeFile, session ! NSFile.Error => CONTINUE]; [] ← PrintQueue.Requeue[qOH: state.qOH, fromQueue: merging, toQueue: aborted]; END; ENDCASE => BEGIN IF state.qOH.printObjectHandle # NIL THEN BEGIN DecomposerControl.FreeDecomposeObject[state.qOH.uid]; state.qOH.printObjectHandle ← NIL END; WITH q: state.qOH SELECT FROM feps9700 => q.ivFileID ← state.mergeFileID; ENDCASE => ERROR; --Delete the spooled file: IF NOT state.tpJob THEN state.qOH.fileID ← QueueFile.Delete[fileID: state.qOH.fileID]; [] ← QueueFile.MakePermanent[state.mergeFile, NSFile.nullAttributeList]; NSFile.Close[state.mergeFile, session]; [] ← PrintQueue.Requeue[qOH: state.qOH, fromQueue: merging, toQueue: IF state.tpJob THEN tpMerged ELSE merged]; END; state.mergeFile ← NSFile.nullHandle; state.mergeFileID ← NSFile.nullID; state.merging ← state.tpJob ← FALSE; state.qOH ← PrintQueue.nilQueueObjectHandle; control.jobQueueEntry ← NOT PrintQueue.Empty[decomposed]; control.tpQueueEntry ← NOT PrintQueue.Empty[tpDecomposed]; NOTIFY event; -- in case there's something to do END; --Queue MergeCodeToString: INTERNAL PROCEDURE [code: FEPSMerge.ExceptionCode] RETURNS [NSString.String] = BEGIN RETURN [ IF code = noResources THEN M[mDecompErrorNoResources] ELSE M[mDecompErrorUnknown]]; END; --MergeCodeToString -- Proc for getting NSString from XMessage key M: PROCEDURE [key: ControlMessages.Key] RETURNS [string: NSString.String] = { r: XString.ReaderBody ← XMessage.Get[controlMsgs, ORD[key]]; string ← [bytes: LOOPHOLE[r.bytes], length: r.limit, maxlength: r.limit]}; Str: PROCEDURE [s: LONG STRING] RETURNS [ns: NSString.String] = INLINE { RETURN[NSString.StringFromMesaString[s]]}; END. --MergeControlImpl LOG when/who/what 16-Oct-84 10:34:41 - Jacks - Created. 25-Oct-84 17:04:15 - Jacks - Added reference to bannerOnly in Finished. 16-Nov-84 12:32:26 - Jacks - Moved call to FreeMergeJob to beginning of Queue; updated to new FEPSMerge interface. 29-Nov-84 15:55:51 - Jacks - Added bannerOnly parm to MergeDoc call. 17-Dec-84 10:57:39 - Jacks - Removed ControlMessages from directory. 14-Jan-85 16:09:16 - Jacks - Make merged file permanent for TP jobs as well as normal jobs (in Queue). 17-Jun-85 16:14:37 - Jacks - Added copyright notice; updated to PS Euclid interfaces. 19-Jul-85 17:59:52 - Jacks - Updated to XMessage and QueueFile. 6-Aug-85 9:08:56 - Jacks - Updated to 10.0 FEPSMerge interface and DecomposerControl.FreeDecomposeObject. 19-Sep-85 11:45:16 - Jacks - Rewrote Finished and some of Exception to handle bannerOnlys correctly; removed mergeFailure from state record. 6-Nov-85 14:01:38 - Jacks - Get msg handle from PSKMessages.