-- 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.