-- file: ChollaStep.mesa -- edited by Barth, August 17, 1981 1:04 AM -- edited by Crowther, January 14, 1982 12:03 PM -- edited by Brotz, March 3, 1983 5:33 PM DIRECTORY Ascii, ccD: FROM "ChollaCmdDefs", dsD: FROM "DisplayDefs", DMSTimeDefs, Editor, exD: FROM "ExceptionDefs", inD: FROM "InteractorDefs", intCommon, MessageParse, opD: FROM "OperationsDefs", prD: FROM "ProtectionDefs", Storage, String, TimeDefs, vmD: FROM "VirtualMgrDefs"; ChollaStep: PROGRAM IMPORTS ccD, dsD, DMSTimeDefs, Editor, exD, inD, intC: intCommon, MessageParse, opD, prD, Storage, String, TimeDefs, vmD EXPORTS ccD = BEGIN currentStepSpecName: STRING _ [ccD.maxSpecNameLength]; -- from step list currentStepDataName: STRING _ [ccD.maxSpecNameLength]; currentProcessName: STRING _ [ccD.maxProcessNameLength]; currentStepName: STRING _ [50]; currentStep: ccD.ParsedStep _ [currentStepSpecName, 0, currentStepDataName, 0, currentStepName, notStarted, 0, 0]; currentStepNotStarted: BOOLEAN _ TRUE; -- valid only when ccD.currentStepNumber#0. invalidDataFile: BOOLEAN _ TRUE; mySpecVersion: CARDINAL; StepOperation: PUBLIC PROCEDURE [stepListString: STRING] = -- display the spec and data for the current step. "stepListString" contains a toc string -- from which the spec name, version, data form name, version and parameters may be -- extracted. BEGIN specName: STRING _ [ccD.tocCacheFileStringLength]; ccD.ParseStep[stepListString, @currentStep]; String.AppendString[specName, currentStep.specName]; String.AppendString[specName, "Spec"L]; SpecCmd[specName, currentStep.specVersion]; IF currentStep.dataName.length = 0 THEN String.AppendString[currentStep.dataName, currentStep.specName]; PreserveChangeInDoneFile[]; FillLowerWindowWithDataForm [currentStep.dataName, currentStep.dataVersion, stepListString]; ccD.iOwnThisStep _ FALSE; invalidDataFile _ FALSE; END; -- of StepOperation -- SpecCommand: PUBLIC inD.CommandProcedure= -- display the spec for the step typed into the brackets BEGIN cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; name: STRING _ [ccD.tocCacheFileStringLength]; --my copy of the file name version: INTEGER _ 0; -- each message in the file is a different version IF ~ccD.IsAuthorized[onlooker, hp] THEN RETURN; IF ~confirmed AND ~inD.ConfirmBrackets[hp: hp.nextHouse] THEN RETURN; version _ ParseSpecBrackets[hp, name]; --fills in name too IF version < 0 THEN RETURN; --something went wrong ccD.DoLowerMenu[ccD.stepListLowerHp]; PreserveChangeInDoneFile[]; SpecCmd[name, version]; invalidDataFile _ TRUE; END; -- of SpecCommand -- ParseSpecBrackets: PROCEDURE [hp: inD.HousePtr, name: STRING] RETURNS [version: INTEGER] = -- Breaks apart hp.nextHouse.text of form "name!version" (no blanks allowed) into name -- and version, which are returned in the respectively named variables. -- N.B. Blanks terminate brackets typein, so there won't be any in hp.nextHouse.text. BEGIN s: STRING = hp.nextHouse.text; --the string as typed into the brackets dotFound: BOOLEAN _ FALSE; version _ 0; name.length _ s.length; FOR i: CARDINAL IN [0 .. s.length) DO SELECT s[i] FROM '. => dotFound _ TRUE; '! => BEGIN versionString: STRING _ [10]; name.length _ i; versionString.length _ s.length - i - 1; FOR j: CARDINAL IN [0 .. versionString.length) DO versionString[j] _ s[i + 1 + j]; ENDLOOP; version _ String.StringToDecimal[versionString ! String.InvalidNumber => GOTO bad]; EXIT; EXITS bad=> {exD.DisplayExceptionString["Illegal number."L]; RETURN[-1]}; END; ENDCASE; name[i] _ s[i]; ENDLOOP; IF ~dotFound THEN String.AppendString[name, "Spec.chml"L]; END; -- of ParseSpecBrackets -- SpecCmd: PROCEDURE [specName: STRING, messageId: CARDINAL] = --given a file name and a message number in the file, -- put that selected message in the display window. Presumably it contains insructions for how to do the step --Should the messageId happen to be 0, the correct messageId will be found in the first message of the file under Current Version: BEGIN dm: vmD.DisplayMessagePtr = vmD.DisplayMessage[intC.dmTextNbr.message]; key: CARDINAL; toc: vmD.TOCHandle; ccD.CleanupDisplayMessage[]; ccD.activeStepSpecName.length _ 0; String.AppendString[ccD.activeStepSpecName, specName]; [toc, key] _ ccD.GetTOCForFile[specName, spec]; IF toc.indexFF = 1 THEN {exD.DisplayExceptionString["Not a spec file!"L]; vmD.UnlockTOC[toc, key]; RETURN}; IF messageId = 0 THEN messageId _ CurrentVersionMessageID[toc, key]; IF messageId + 1 IN (1 .. vmD.FirstFreeTOCIndex[toc, key]) THEN BEGIN mySpecVersion _ messageId; IF RetrieveFromArchiveIfNecessary[toc, key, specName, messageId] THEN BEGIN vmD.LoadDisplayMessage[toc, key, messageId + 1, dm]; WriteMessageIntoDisplayWindow[]; END; END ELSE exD.DisplayExceptionString["No such version."L]; vmD.UnlockTOC[toc, key]; END; -- of SpecCmd -- RetrieveFromArchiveIfNecessary: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, name: STRING, version: CARDINAL] RETURNS [haveIt: BOOLEAN] = BEGIN fp: vmD.TOCFixedPart; vmD.GetTOCFixedPart[toc, key, version + 1, @fp]; IF fp.mark = 'a THEN BEGIN cm: vmD.ComposedMessagePtr _ vmD.AllocateComposedMessageObject[]; haveIt _ ccD.RetrieveFromArchive[name, version, cm]; IF haveIt THEN [] _ opD.ReplaceMailOperation [delete: TRUE, index: version + 1, msg: cm, toc: toc, key: key]; vmD.FreeVirtualMessageObject[cm]; END ELSE haveIt _ TRUE; END; -- of RetrieveFromArchiveIfNecessary -- WriteMessageIntoDisplayWindow: PROCEDURE = BEGIN mnp: inD.MessageTextNbrPtr = intC.dmTextNbr; mnp.message.formatStart _ mnp.message.formatEnd _ 0; mnp.haveMessage _ TRUE; dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, mnp.topY, mnp.bottomY]; Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines, mnp: mnp]; END; FillLowerWindowWithDataForm: PROCEDURE [dataName: STRING, messageId: CARDINAL, tocString: STRING] = -- Given a file name and given a messageId, put that selected message in the lower -- window. Presumably it contains a blank data form for the step -- Should the messageId happen to be 0, the correct messageId will be found in the first -- message of the file under Current Version: BEGIN toc: vmD.TOCHandle; key: CARDINAL; dm: vmD.DisplayMessagePtr _ vmD.AllocateDisplayMessageObject[]; fileName: STRING _ [ccD.tocCacheFileStringLength]; index, firstLegalIndex: vmD.TOCIndex; fieldList: MessageParse.FieldList _ NIL; ccD.currentDoneFileName.length _ 0; String.AppendString[ccD.currentDoneFileName, ccD.stepListRunName]; String.AppendString[ccD.currentDoneFileName, "Done.chml"L]; ccD.currentStepNumber _ ccD.CurrentStep[]; currentStepNotStarted _ currentStep.status = notStarted; BEGIN -- for EXITS IF currentStepNotStarted THEN BEGIN String.AppendString[fileName, dataName]; String.AppendString[fileName, "Data"L]; [toc, key] _ ccD.GetTOCForFile[fileName, data]; IF toc.indexFF = 1 THEN {exD.DisplayExceptionString["Not a data form file!"L]; GO TO Return}; IF messageId = 0 THEN messageId _ CurrentVersionMessageID[toc, key]; index _ messageId + 1; firstLegalIndex _ 2; END ELSE BEGIN [toc, key] _ ccD.GetTOCForFile[ccD.currentDoneFileName, done]; index _ ccD.currentStepNumber; firstLegalIndex _ 1; END; IF index IN [firstLegalIndex .. vmD.FirstFreeTOCIndex[toc, key]) THEN BEGIN IF currentStepNotStarted AND ~RetrieveFromArchiveIfNecessary[toc, key, fileName, messageId] THEN GO TO Return; vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _ MessageParse.MakeFieldList[dm]; WriteMessageIntoComposeWindow[dm, @fieldList]; vmD.FlushDisplayMessage[dm, key]; IF currentStepNotStarted AND currentStep.paramIndex # 0 THEN BEGIN -- fill in the parameters. i: CARDINAL _ currentStep.paramIndex; name: STRING _ Storage.String[50]; value: STRING _ Storage.String[100]; stringToFill: POINTER TO STRING; char: CHARACTER; cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; IF i >= tocString.length OR tocString[i] # '( THEN exD.SysBug[]; DO initialWhite: BOOLEAN _ TRUE; name.length _ value.length _ 0; stringToFill _ @name; FOR i _ i + 1, i + 1 UNTIL i >= tocString.length DO SELECT char _ tocString[i] FROM '), '; => EXIT; ': => {stringToFill _ @value; initialWhite _ TRUE}; '/ => FOR i _ i + 1, i + 1 UNTIL i >= tocString.length OR tocString[i] = '/ DO ENDLOOP; ENDCASE => IF ~initialWhite OR ~(initialWhite _ dsD.GetCharProperty[char, white]) THEN BEGIN IF stringToFill^.length + 2 >= stringToFill^.maxlength THEN Storage.ExpandString[stringToFill, stringToFill^.maxlength / 2]; String.AppendChar[stringToFill^, char]; END; ENDLOOP; IF name.length > 0 THEN BEGIN IF ~String.EquivalentString[name, "Replace"L] AND ~String.EquivalentString[name, "Append"L] THEN FOR i: CARDINAL IN [0 .. value.length) DO IF value[i] = Ascii.ControlA THEN EXIT; REPEAT FINISHED => String.AppendString[value, ""L]; ENDLOOP; MessageParse.ReplaceOrAppendField[cm, @fieldList, name, value]; END; IF i >= tocString.length OR char = ') THEN EXIT; ENDLOOP; Storage.FreeString[name]; Storage.FreeString[value]; END; ResetTheEditor[FALSE, fieldList]; END ELSE exD.DisplayExceptionString["No such version."L]; EXITS Return => NULL; END; -- of block for EXITS vmD.FreeVirtualMessageObject[dm]; vmD.UnlockTOC[toc, key]; MessageParse.FreeFieldList[fieldList]; END; -- of FillLowerWindowWithDataForm -- WriteMessageIntoComposeWindow: PUBLIC PROCEDURE [newm: vmD.VirtualMessagePtr, fieldListPtr: MessageParse.FieldListPtr] = BEGIN oldm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; oldSize: vmD.CharIndex _ vmD.GetMessageSize[oldm]; newSize: vmD.CharIndex _ vmD.GetMessageSize[newm]; vmD.ReplaceRangeInMessage[[0, oldSize, oldm], [0, newSize, newm]]; MessageParse.DeleteField[oldm, fieldListPtr, "UniqueID"L]; MessageParse.DeleteField[oldm, fieldListPtr, "ThisID"L]; END; -- of WriteMessageIntoComposeWindow -- ResetTheEditor: PUBLIC PROCEDURE [keepDeletionBuffer: BOOLEAN _ FALSE, fieldList: MessageParse.FieldList _ NIL] = BEGIN mnp: inD.MessageTextNbrPtr = intC.cmTextNbr; composedMessage: vmD.ComposedMessagePtr = vmD.ComposedMessage[mnp.message]; targetRange: vmD.MessageRange _ [0, 0, composedMessage]; Editor.ResetInsertionBuffer[mnp]; IF ~keepDeletionBuffer THEN Editor.ResetDeletionBuffer[mnp]; intC.commandType _ IF keepDeletionBuffer THEN get ELSE noCommand; IF intC.source.key # 0 THEN exD.SysBug[]; IF mnp.protectedFieldPtr # NIL THEN prD.UnprotectAllFields[@mnp.protectedFieldPtr]; UNTIL fieldList = NIL DO prD.ProtectNewField [pfpp: @mnp.protectedFieldPtr, range: [MAX[fieldList.start, 1] - 1, fieldList.valueStart, composedMessage]]; fieldList _ fieldList.next; ENDLOOP; prD.FindUnprotectedSubrange[mnp.protectedFieldPtr, @targetRange, TRUE]; intC.source _ intC.target _ [mnp: mnp, key: 0, start: targetRange.start, end: targetRange.start, point: targetRange.start, mode: char, pendingDelete: FALSE]; intC.newTargetSelection _ TRUE; intC.actionPoint _ targetRange.start; intC.pendingDeleteSetByControl _ FALSE; composedMessage.formatStart _ composedMessage.formatEnd _ 0; Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines, mnp: mnp]; mnp.haveMessage _ TRUE; intC.runCommandMode _ FALSE; intC.composedMessageEdited _ FALSE; END; -- ResetTheEditor -- CurrentVersionMessageID: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL] RETURNS [messageID: vmD.TOCIndex] = -- Returns the messageID of the spec or data form that is the current version in toc. The -- messageID is one less than the TOCIndex of that message. BEGIN -- First search the toc for a message marked with *. If none is found, then look in the -- first message for the current version, and mark the corresponding message with a * -- for future reference. fp: vmD.TOCFixedPart; dm: vmD.DisplayMessagePtr; fieldList: MessageParse.FieldList; FOR i: vmD.TOCIndex IN [1 .. vmD.FirstFreeTOCIndex[toc, key]) DO vmD.GetTOCFixedPart[toc, key, i, @fp]; IF fp.mark = '* AND i > 1 THEN RETURN[i - 1]; ENDLOOP; dm _ vmD.AllocateDisplayMessageObject[]; vmD.LoadDisplayMessage[toc, key, 1, dm]; fieldList _ MessageParse.MakeFieldList[dm]; messageID _ MessageParse.GetNumberFromField[dm, fieldList, "CurrentVersion"L]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; MessageParse.FreeFieldList[fieldList]; vmD.GetTOCFixedPart[toc, key, messageID + 1, @fp]; IF fp.mark # 'a THEN {fp.mark _ '*; vmD.PutTOCFixedPart[toc, key, messageID + 1, @fp]}; END; -- of CurrentVersionMessageID -- -- ///////// THE LOWER WINDOW COMMANDS -- to understand these procedures you must understand the lower window utilities; briefly: -- GetStatus plucks the Status from currentStep (may fail if no current step) -- CheckStatus deals with inappropiate status for commands -- FillAField finds (or makes) a field and replaces it with a string -- MakeTimeField is FillAField with the current time -- MakeitHappen refreshes the window and sends the message StartStepCommand: PUBLIC inD.CommandProcedure = BEGIN -- send a message via a routine exported by chollacmd that updates the -- bulletin board and the step list entry. The bulletin board change should -- be of the form "add this active step to the bulletin board list" rather -- than replacing the entire entry for the run in order to avoid update -- conflicts. The step list entry must change to point to the bound -- specification and the new data record entry created in the done file -- for the run. cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; st: STRING _ [10]; cantStart: STRING = "You cannot start a step twice."L; status: ccD.StepStatus; fieldList: MessageParse.FieldList _ NIL; IF ~ccD.IsAuthorized[technician, hp] THEN RETURN; ccD.iOwnThisStep _ TRUE; status _ GetStatus[]; IF status = none THEN {ccD.iOwnThisStep _ FALSE; RETURN}; IF ~CheckStatus [status: status, notStarted: NIL, -- Returns TRUE if status = notStarted -- started: cantStart, inProcess: cantStart, processDone: cantStart, rejected: cantStart, finished: cantStart] THEN RETURN; currentStepNotStarted _ FALSE; currentStep.status _ started; CheckTechnician[intC.user.name, currentStep.specName, mySpecVersion]; st.length _ 0; String.AppendDecimal[st, ccD.currentStepNumber]; fieldList _ MessageParse.MakeFieldList[cm]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Run"L, ccD.stepListRunName]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Step"L, st]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Status"L, "Started"L]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Started-By"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Spec"L, currentStep.specName]; st.length _ 0; String.AppendDecimal[st, mySpecVersion]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Version"L, st]; MakeTimeField[cm, @fieldList, "StartTime"L]; ReplaceSpecialPlaceholders[cm, @fieldList]; FillInIncludedFields[cm, @fieldList]; SendDataMail[cm, @fieldList, new]; MessageParse.FreeFieldList[fieldList]; END; -- of StartStepCommand -- ProcessStepCommand: PUBLIC inD.CommandProcedure = BEGIN -- update the bulletin board and step list, check to see if on proper -- workstation first. Startup the equipment. cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; error: STRING; fieldList: MessageParse.FieldList _ NIL; IF ~ccD.IsAuthorized[technician, hp] THEN RETURN; IF ~CheckStatus [status: GetStatus[], notStarted: "Must start before process."L, started: NIL, inProcess: "Already in process."L, processDone: "Can't process twice."L, rejected: "Can't process a rejected step."L, finished: "Can't process a finished step."L] THEN RETURN; BEGIN -- for EXITS -- fieldList _ MessageParse.MakeFieldList[cm]; MessageParse.GetStringFromField[cm, fieldList, "Process"L, currentProcessName ! MessageParse.ParseFault => GOTO Bad]; IF currentProcessName.length = 0 THEN GO TO Bad; error _ ccD.MachineDriver [data: cm, spec: intC.dmTextNbr.message, name: currentProcessName]; IF error # NIL THEN {exD.DisplayExceptionString[error]; RETURN}; MakeTimeField[cm, @fieldList, "ProcessStartTime"L]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Processed-By"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Status"L, "In Process"L]; currentStep.status _ inProcess; SendDataMail[cm, @fieldList, old]; EXITS Bad => exD.DisplayExceptionString["Not a process type step."L]; END; -- of EXITS block -- MessageParse.FreeFieldList[fieldList]; END; -- of ProcessStepCommand -- CancelStepCommand: PUBLIC inD.CommandProcedure = BEGIN -- check to see if step in automatic processing. If not then bitch. -- Update bulletin board and step list. cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; status: ccD.StepStatus; fieldList: MessageParse.FieldList _ NIL; IF ~ccD.IsAuthorized[technician, hp] THEN RETURN; status _ GetStatus[]; IF ~CheckStatus [status: status, notStarted: "Must start before cancelling."L, started: "This step is not in automatic processing."L, inProcess: NIL, processDone: "Processing has already finished."L, rejected: "This step has already been rejected."L, finished: "This step has already been finished."L] THEN RETURN; SELECT status FROM started, processDone => NULL; inProcess => IF ~ccD.MachineCancel [data: cm, spec: intC.dmTextNbr.message, name: currentProcessName] THEN {exD.DisplayExceptionString["Cannot cancel!"L]; RETURN}; ENDCASE => exD.SysBug[]; fieldList _ MessageParse.MakeFieldList[cm]; MakeTimeField[cm, @fieldList, "CancelledTime"L]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Cancelled-By"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Status"L, "Rejected"L]; currentStep.status _ rejected; SendDataMail[cm, @fieldList, old]; MessageParse.FreeFieldList[fieldList]; END; -- of CancelStepCommand -- ProcessDoneStepCommand: PUBLIC inD.CommandProcedure = BEGIN -- check to see if step started, if not bitch. Update bulletin board -- and step list with new state. cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; fieldList: MessageParse.FieldList _ NIL; IF ~ccD.IsAuthorized[technician, hp] THEN RETURN; IF ~CheckStatus [status: GetStatus[], notStarted: "No process running."L, started: NIL, inProcess: "Process is still under way."L, processDone: "It's already done."L, rejected: "No process running."L, finished: "No process running."L] THEN RETURN; fieldList _ MessageParse.MakeFieldList[cm]; MakeTimeField[cm, @fieldList, "ProcessFinishTime"L]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Processed-By"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Status"L, "Processed"L]; currentStep.status _ processDone; SendDataMail[cm, @fieldList, old]; MessageParse.FreeFieldList[fieldList]; END; -- of ProcessDoneStepCommand -- PreserveChangeInDoneFile: PUBLIC PROCEDURE = BEGIN cm: vmD.ComposedMessagePtr _ vmD.ComposedMessage[intC.cmTextNbr.message]; fieldList: MessageParse.FieldList; IF ccD.currentStepNumber = 0 THEN RETURN; IF intC.composedMessageEdited AND ~currentStepNotStarted THEN BEGIN exD.DisplayBothExceptionLines [string1: "The current data form has been changed."L, exception1: exD.nil, string2: "Type ESC to save it in the Done file, DEL to proceed without saving it."L, exception2: exD.nil]; IF inD.Confirm[0] THEN BEGIN fieldList _ MessageParse.MakeFieldList[cm]; SetEditedBy[cm, @fieldList, 0]; SendDataMail[cm, @fieldList, old]; MessageParse.FreeFieldList[fieldList]; END; END; vmD.InitComposedMessage[cm, ""L]; ccD.currentStepNumber _ 0; intC.composedMessageEdited _ ccD.iOwnThisStep _ FALSE; ResetTheEditor[]; END; -- of PreserveChangeInDoneFile -- RejectStepCommand: PUBLIC inD.CommandProcedure = BEGIN cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; fieldList: MessageParse.FieldList _ NIL; IF ~ccD.IsAuthorized[technician, hp] THEN RETURN; IF ~CheckStatus [status: GetStatus[], notStarted: "Must start before rejecting."L, started: NIL, inProcess: "Can't reject until process finishes. You may wish to invoke ""Cancel"" first."L, processDone: NIL, rejected: "Step has already been rejected."L, finished: "Step has already been finished."L] THEN RETURN; fieldList _ MessageParse.MakeFieldList[cm]; MakeTimeField[cm, @fieldList, "RejectedTime"L]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Rejected-By"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Status"L, "Rejected"L]; currentStep.status _ rejected; SendDataMail[cm, @fieldList, old]; ShipToInterestedOnlookers[cm, @fieldList]; MessageParse.FreeFieldList[fieldList]; END; -- of RejectStepCommand -- FinishStepCommand: PUBLIC inD.CommandProcedure = BEGIN -- check to see if started, if not bitch. check to see if all fields of -- data record are filled out. If not ask technician to confirm. If confirmed -- then send exception message to line manager indicating the run, step, -- date/time, and technician name. If not confirmed forget the whole thing. -- send update message to bulletin board removing step from active list, -- maybe if that reduces the active step list on the bulletin board to nil -- then we should post the next unstarted step on the bulletin board. -- update the step list to reflect new state of step. cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[intC.cmTextNbr.message]; fieldList: MessageParse.FieldList _ NIL; IF ~ccD.IsAuthorized[technician, hp] THEN RETURN; IF ~CheckStatus [status: GetStatus[], notStarted: "Must start before finishing."L, started: NIL, inProcess: "Can't finish until process finishes."L, processDone: NIL, rejected: "Step has already been rejected."L, finished: "Can't finish twice."L] THEN RETURN; FOR i: vmD.CharIndex IN [0 .. vmD.GetMessageSize[cm]) DO IF vmD.GetMessageChar[cm, i] = Ascii.ControlA THEN BEGIN exD.DisplayExceptionStringOnLine["A field of the data form is not filled in!"L, 1]; IF ~ccD.IsAuthorized[kahuna, hp, FALSE] THEN BEGIN exD.DisplayExceptionStringOnLine ["Please correct the form and try the Finish command again."L, 2]; exD.FlashExceptionsRegion[]; RETURN; END; IF inD.Confirm[2] THEN EXIT ELSE RETURN; END; ENDLOOP; fieldList _ MessageParse.MakeFieldList[cm]; MakeTimeField[cm, @fieldList, "FinishTime"L]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Finished-By"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, @fieldList, "Status"L, "Finished"L]; currentStep.status _ finished; SendDataMail[cm, @fieldList, old]; ShipToInterestedOnlookers[cm, @fieldList]; MessageParse.FreeFieldList[fieldList]; END; -- of FinishStepCommand -- -- LOWER WINDOW UTILITIES GetStatus: PROCEDURE RETURNS [status: ccD.StepStatus] = -- Parses "vm", looking for a "Status:" field. If not found, then an error message is -- returned flashed, and "none" is returned. BEGIN noStatus: STRING = "No status field: malformed data form."L; noStepSelected: STRING = "No step selected."L; IF invalidDataFile THEN {exD.DisplayExceptionString[noStepSelected]; RETURN[none]}; IF ~ccD.iOwnThisStep AND ~(ccD.iOwnThisStep _ inD.Confirm[1]) THEN RETURN[none]; status _ currentStep.status; IF status = none THEN exD.DisplayExceptionString[noStatus]; END; -- of GetStatus -- CheckStatus: PROCEDURE [status: ccD.StepStatus, notStarted, started, inProcess, processDone, rejected, finished: STRING] RETURNS [ok: BOOLEAN] = BEGIN ans: STRING; IF status = none THEN RETURN[FALSE]; ans _ SELECT status FROM started => started, notStarted => notStarted, inProcess => inProcess, processDone => processDone, rejected => rejected, finished => finished, ENDCASE => ERROR; ok _ ans = NIL; IF ~ok THEN exD.DisplayExceptionString[ans]; END; -- of CheckStatus -- FillInIncludedFields: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr] = BEGIN IncludeType: TYPE = {replace, append}; fields: ARRAY IncludeType OF MessageParse.FieldList; dm: vmD.DisplayMessagePtr; replaceCount, appendCount: CARDINAL; stepListToc, doneToc: vmD.TOCHandle; stepListKey, doneKey: CARDINAL; [fields[replace], replaceCount] _ MessageParse.LocateField[fieldListPtr^, "Replace"L]; [fields[append], appendCount] _ MessageParse.LocateField[fieldListPtr^, "Append"L]; IF replaceCount = 0 AND appendCount = 0 THEN RETURN; dm _ vmD.AllocateDisplayMessageObject[]; stepListToc _ intC.tocTextNbr.toc; stepListKey _ vmD.WaitForLock[stepListToc]; [doneToc, doneKey] _ ccD.GetTOCForFile[ccD.currentDoneFileName, done]; FOR type: IncludeType IN IncludeType DO fieldList: MessageParse.FieldList _ fields[type]; IF (type = replace AND replaceCount # 0) OR (type = append AND appendCount # 0) THEN BEGIN charIndex: vmD.CharIndex _ fieldList.valueStart; spec: STRING _ [50]; field: STRING _ [50]; currentField: STRING _ [50]; specName: STRING _ [ccD.tocCacheFileStringLength - 9]; dataName: STRING _ [0]; stepName: STRING _ [0]; tocString: STRING _ [opD.maxTOCStringLength]; parsedStep: ccD.ParsedStep _ [specName, 0, dataName, 0, stepName, notStarted, 0, 0]; index: vmD.TOCIndex; GetField: PROCEDURE [s: STRING] RETURNS [BOOLEAN] = BEGIN charIndex _ MessageParse.GetNextWord[cm, charIndex, fieldList.valueEnd, s]; IF s.length = 0 THEN exD.DisplayExceptionString["Included fields must be specified properly."L]; RETURN[s.length # 0]; END; -- of GetField -- DO charIndex _ MessageParse.GetNextWord [cm, charIndex, fieldList.valueEnd, currentField]; IF currentField.length = 0 THEN EXIT; IF ~GetField[spec] THEN EXIT; IF ~GetField[field] THEN EXIT; FOR index DECREASING IN [1 .. MIN[doneToc.indexFF, ccD.currentStepNumber]) DO vmD.GetTOCString[stepListToc, stepListKey, index, tocString]; ccD.ParseStep[tocString, @parsedStep]; IF String.EquivalentString[parsedStep.specName, spec] THEN BEGIN fl: MessageParse.FieldList; count: CARDINAL; vmD.LoadDisplayMessage[doneToc, doneKey, index, dm]; fl _ MessageParse.MakeFieldList[dm]; [fl, count] _ MessageParse.LocateField[fl, field]; IF count > 0 THEN BEGIN cmSize: vmD.CharIndex; fromRange: vmD.MessageRange _ [fl.valueStart, fl.valueEnd, dm]; cfl: MessageParse.FieldList; delta: INTEGER; [cfl, count] _ MessageParse.LocateField[fieldListPtr^, currentField]; IF count = 0 THEN BEGIN MessageParse.ReplaceOrAppendField[cm, fieldListPtr, currentField, " "L]; [cfl, count] _ MessageParse.LocateField[fieldListPtr^, currentField]; END; cmSize _ vmD.GetMessageSize[cm]; IF type = replace THEN BEGIN vmD.ReplaceRangeInMessage [to: [cfl.valueStart, cfl.valueEnd, cm], from: fromRange]; cfl.valueEnd _ cfl.valueStart + (fromRange.end - fromRange.start); END ELSE BEGIN -- type = append -- vmD.StartMessageInsertion[cm, cfl.valueEnd]; vmD.InsertStringInMessage[cm, " "L]; vmD.StopMessageInsertion[cm]; vmD.InsertRangeInMessage[cfl.valueEnd + 2, cm, fromRange]; cfl.valueEnd _ cfl.valueEnd + (fromRange.end - fromRange.start) + 2; END; delta _ vmD.GetMessageSize[cm] - cmSize; MessageParse.UpdateFieldList[cfl.next, delta]; vmD.FlushDisplayMessage[dm, doneKey]; MessageParse.FreeFieldList[fl]; IF cfl.start < charIndex THEN charIndex _ charIndex + delta; EXIT; END; vmD.FlushDisplayMessage[dm, doneKey]; MessageParse.FreeFieldList[fl]; END; REPEAT FINISHED => exD.DisplayExceptionString["Included field not found."L]; ENDLOOP; ENDLOOP; END; ENDLOOP; vmD.UnlockTOC[doneToc, doneKey]; vmD.UnlockTOC[stepListToc, stepListKey]; vmD.FreeVirtualMessageObject[dm]; END; -- of FillInIncludedFields -- ReplaceSpecialPlaceholders: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr] = BEGIN start: vmD.CharIndex _ 0; end: vmD.CharIndex; found, anyFound: BOOLEAN _ FALSE; DO [found, start, end, ] _ Editor.FindOperation["$tep"L, start, vmD.GetMessageSize[cm], cm]; IF ~found THEN EXIT; anyFound _ TRUE; vmD.DeleteRangeInMessage[[start, end, cm]]; InsertString[cm, start, currentStep.stepName]; start _ start + currentStep.stepName.length; ENDLOOP; IF anyFound THEN BEGIN MessageParse.FreeFieldList[fieldListPtr^]; fieldListPtr^ _ MessageParse.MakeFieldList[cm]; END; END; -- of ReplaceSpecialPlaceholders -- MakeTimeField: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, field: STRING] = BEGIN st: STRING _ [DMSTimeDefs.timeStringLength]; DMSTimeDefs.MapPackedTimeToTimeZoneString [LOOPHOLE[TimeDefs.CurrentDayTime[]], st, arpaMsg]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, field, st]; END; -- of MakeTimeField -- InsertString: PUBLIC PROCEDURE [cm: vmD.ComposedMessagePtr, start: vmD.CharIndex, s: STRING] = BEGIN IF cm = NIL OR s = NIL THEN RETURN; vmD.StartMessageInsertion[cm, start]; vmD.InsertStringInMessage[cm, s]; vmD.StopMessageInsertion[cm]; END; -- of InsertString -- SetEditedBy: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, n: INTEGER] = BEGIN technician: STRING _ [50]; fieldName: STRING _ [15]; String.AppendString[fieldName, "Edited-By"L]; IF n # 0 THEN String.AppendDecimal[fieldName, n]; MessageParse.GetStringFromField[cm, fieldListPtr^, fieldName, technician]; SELECT TRUE FROM technician.length = 0 => MessageParse.ReplaceOrAppendField[cm, fieldListPtr, fieldName, intC.user.name]; String.EquivalentString[technician, intC.user.name] => NULL; n >= 9 => NULL; ENDCASE => SetEditedBy[cm, fieldListPtr, n + 1]; END; -- of SetEditedBy -- New: TYPE = {new, old}; SendDataMail: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, n: New] = BEGIN index: vmD.TOCIndex = ccD.currentStepNumber; key: CARDINAL; toc: vmD.TOCHandle; ccD.StartMultiTOCOperation[]; [toc, key] _ ccD.GetTOCForFile[ccD.currentDoneFileName, done]; IF n = old THEN ccD.CopyUniqueIDsIntoMessage[toc, key, index, cm, fieldListPtr] ELSE BEGIN --may have to insert dummy messages st: STRING _ [10]; spare: vmD.ComposedMessagePtr _ NIL; spareFieldList: MessageParse.FieldList _ NIL; UNTIL toc.indexFF >= index DO IF spare = NIL THEN spare _ vmD.AllocateComposedMessageObject[]; vmD.InitComposedMessage[spare, "Subject: Dummy Message to keep file in order Run: "L]; vmD.StartMessageInsertion[spare, vmD.GetMessageSize[spare]]; vmD.InsertStringInMessage[spare, ccD.stepListRunName]; vmD.InsertStringInMessage[spare, " Step: "L]; st.length _ 0; String.AppendDecimal[st, toc.indexFF]; vmD.InsertStringInMessage[spare, st]; vmD.InsertMessageChar[spare, Ascii.CR]; vmD.StopMessageInsertion[spare]; spareFieldList _ MessageParse.MakeFieldList[spare]; IF ~ccD.SendChollaMail [insert, done, toc, key, toc.indexFF, spare, @spareFieldList, TRUE] THEN exD.DisplayExceptionString["Problem sending placeholder message!"L]; MessageParse.FreeFieldList[spareFieldList]; ENDLOOP; IF spare # NIL THEN vmD.FreeVirtualMessageObject[spare]; END; IF ~ccD.SendChollaMail[insert, done, toc, key, index, cm, fieldListPtr] THEN exD.DisplayExceptionString["Problem with step update!"L]; vmD.UnlockTOC[toc, key]; ccD.FinishMultiTOCOperation[]; END; -- of SendDataMail -- ShipToInterestedOnlookers: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr] = BEGIN sendToFL: MessageParse.FieldList; cmSize: vmD.CharIndex; value: STRING; count: CARDINAL; [sendToFL, count] _ MessageParse.LocateField[fieldListPtr^, "SendTo"L]; IF count = 0 THEN RETURN; value _ Storage.String[sendToFL.valueEnd - sendToFL.valueStart]; value.length _ sendToFL.valueEnd - sendToFL.valueStart; cmSize _ vmD.GetMessageSize[cm]; FOR i: CARDINAL IN [0 .. value.length) DO value[i] _ vmD.GetMessageChar[cm, sendToFL.valueStart + i]; ENDLOOP; ccD.InsertString[cm, 0, " "L]; ccD.InsertString[cm, 0, value]; ccD.InsertString[cm, 0, " To: "L]; ccD.InsertString[cm, 0, intC.workstationName]; ccD.InsertString[cm, 0, "From: "L]; ccD.AppendToUnsentMailQueue[cm]; vmD.DeleteRangeInMessage[[0, vmD.GetMessageSize[cm] - cmSize, cm]]; Storage.FreeString[value]; END; -- of ShipToInterestedOnlookers -- --///////OPERATOR SECTION CheckTechnician: PROC [technician: STRING, spec: STRING, version: CARDINAL] = BEGIN key, oldVersion: CARDINAL; toc: vmD.TOCHandle; index: vmD.TOCIndex; dm: vmD.DisplayMessagePtr _ vmD.AllocateDisplayMessageObject[]; fieldList: MessageParse.FieldList _ NIL; IF version = 0 THEN exD.SysBug[]; [toc, key, index] _ TechnicianNameToMessageDescriptor[technician]; vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _ MessageParse.MakeFieldList[dm]; oldVersion _ MessageParse.GetNumberFromField[dm, fieldList, spec]; IF oldVersion # version THEN DO exD.DisplayBothExceptionLines ["This specification is different from the last of its type that you have read!"L, exD.nil, "Type ESC to continue"L, exD.nil]; IF inD.Confirm[0] THEN EXIT; ENDLOOP; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; vmD.UnlockTOC[toc, key]; MessageParse.FreeFieldList[fieldList]; END; -- of CheckTechnician -- UpdateTechnician: PUBLIC PROCEDURE [technician: STRING, spec: STRING, version: CARDINAL] = BEGIN key, oldVersion: CARDINAL; toc: vmD.TOCHandle; index: vmD.TOCIndex; cm: vmD.ComposedMessagePtr _ vmD.AllocateComposedMessageObject[]; fieldList: MessageParse.FieldList _ NIL; IF version = 0 THEN exD.SysBug[]; [toc, key, index] _ TechnicianNameToMessageDescriptor[technician]; ccD.LoadComposedMessage[toc, key, index, cm]; fieldList _ MessageParse.MakeFieldList[cm]; oldVersion _ MessageParse.GetNumberFromField[cm, fieldList, spec]; IF oldVersion # version THEN BEGIN s: STRING _ [10]; String.AppendDecimal[s, version]; MessageParse.ReplaceOrAppendField[cm, @fieldList, spec, s]; [] _ opD.ReplaceMailOperation [delete: TRUE, index: index, msg: cm, toc: toc, key: key]; END; vmD.FreeVirtualMessageObject[cm]; vmD.UnlockTOC[toc, key]; MessageParse.FreeFieldList[fieldList]; END; -- of UpdateTechnician -- TechnicianNameToMessageDescriptor: PROCEDURE [technician: STRING] RETURNS [toc: vmD.TOCHandle, key: CARDINAL, index: vmD.TOCIndex] = BEGIN cm: vmD.ComposedMessagePtr _ vmD.AllocateComposedMessageObject[]; fieldList: MessageParse.FieldList _ NIL; [toc, key] _ ccD.GetTOCForFile["Technician"L, technician]; IF toc.indexFF # 1 THEN ccD.LoadComposedMessage[toc, key, 1, cm] ELSE BEGIN vmD.InitComposedMessage[cm, " "L]; [] _ opD.ReplaceMailOperation [delete: FALSE, index: 1, msg: cm, toc: toc, key: key]; END; fieldList _ MessageParse.MakeFieldList[cm]; index _ MessageParse.GetNumberFromField[cm, fieldList, technician]; IF index >= toc.indexFF THEN exD.SysBug[]; IF index = 0 THEN BEGIN s: STRING _ [10]; index _ toc.indexFF; String.AppendDecimal[s, index]; MessageParse.ReplaceOrAppendField[cm, @fieldList, technician, s]; [] _ opD.ReplaceMailOperation [delete: TRUE, index: 1, msg: cm, toc: toc, key: key]; vmD.InitComposedMessage[cm, " "L]; [] _ opD.ReplaceMailOperation [delete: FALSE, index: index, msg: cm, toc: toc, key: key]; END; vmD.FreeVirtualMessageObject[cm]; MessageParse.FreeFieldList[fieldList]; END; -- of TechnicianNameToMessageDescriptor -- END. -- of ChollaStep -- z19932(635)\27798v2V5149v1V62v1V2560v1V542v1V