-- Last edited by Chi Yung, April 23, 1982 5:06 PM
-- Last edited by Chi Yung, April 30, 1982 2:05 PM
-- Last edited by Chi Yung, May 21, 1982 12:23 PM
-- Last edited by Chi Yung, September 1, 1983 11:21 AM
-- This program has 52,000 bytes of codes or 60,900 bytes of codes
-- including comments.
DIRECTORY
Ascii,
BruceDefs,
MachineParseDefs,
String,
Storage,
InlineDefs,
inD: FROM "interactordefs",
exD: FROM "exceptiondefs",
vmD: FROM "VirtualMgrDefs";
BruceCompiler: PROGRAM
IMPORTS BruceDefs, MachineParseDefs, String, Storage, InlineDefs, exD, inD
EXPORTS BruceDefs =
BEGIN
OPEN MachineParseDefs;
DescribeTable: TYPE = DESCRIPTOR FOR ARRAY [1..20] OF CARDINAL;
--The number of indexing elements (20 here) should be changed when there
--are more than 20 calibration points for either the temperature correction
--table or the boat speed conversion table or the flow rate coversion
--table.
Record: TYPE = RECORD[a,b: DescribeTable];
--DescribeTable and Record allow arrays of different sizes to use or call
--the same procedures,such as the procedure known as Interpolation2 or
--FillArray.It is an efficient and compact way to transmit informations,
--such as the contents of a big array, into a procedure.
-- The following 4 constants are compiled time constants. They can not be
-- changed or updated by the constant file. If one or more of these
-- constants has to be changed, one must compile the BruceCompiler program
-- and then bind it by bind BruceTalk.
NumberOfProfiledTemp: CARDINAL = 5;
-- This number should be changed if you want to expand the correctable
-- range of temperature or if you want to increase the accuracy of the
-- interpolation.
NumberOfGasType: CARDINAL = 12;
-- This number should be changed if there are more than 12 types of gases.
NumberOfCalibratedGasRate: CARDINAL = 11;
-- This number should be changed if you want to increase the accuracy of
-- the interpolation.
NumberOfCalibratedboatRate: CARDINAL = 10;
-- This number should be changed if you want to increase the accuracy of
-- the interpolation.
-- GasOneToGasTwoSafetyRatioHole:CARDINAL;
GasOneToGasTwoSafetyRatio: CARDINAL = 250;
-- The safety ratio of Hydrogen (Gas1) to Oxygen (Gas2) is set to be 2.5.
-- If the ratio of H2 to O2 mixture in any of our steps is larger
-- than this saftey ratio, the program will stop. The number, 2.5, is
-- multiplied by a factor 100 to improve the accuracy of comparision.
GasOne: STRING = "H2";
GasTwo: STRING = "O2";
tcaON: BOOLEAN ← FALSE;
O2ON: BOOLEAN ← FALSE;
POCl3ON: BOOLEAN ← FALSE;
boatScaledFactor:CARDINAL ← 1;
TempScaledFactor:CARDINAL ← 1;
GasScaledFactor:CARDINAL ← 2;
-- ScaledFactor is the number of digits allowed after the decimal.
-- Temperatures are scaled by a factor of 10.
-- Gas flow rates are scaled by a factor of 100.
-- The reason for the factor of a 100 is because the parsing of the
-- gas flow rate is done with a multiplication factor of a 100 so as
-- to talk with the DDC microcontroller of the Bruce furnaces. For
-- similar reason temperature has be scaled by a factor of 10 to
-- talk with the DDC.
boatSpeedInchUpperBound:CARDINAL ← 1000;
--Max boat speed rate is 100 inches/min. Since the scale factor for boat
--speed is 1 therefore 100 becomes 1000.
boatSpeedPercentUpperBound:CARDINAL ← 990;
--Max boat speed rate is 99%. Since the scale factor for boat speed is 1
--therefore 99% becomes 990.
GasFlowLitreUpperBound: CARDINAL ← 2000;
--Max gas flow rate is 20 l/m.Since the scale factor for gas flow is 2
--therefore 20 becomes 2000.
GasFlowPercentUpperBound:CARDINAL ← 9999;
--Max gas flow rate is 99.99%.Since the scale factor for gas flow is 2
--therefore 99.99% becomes 9999.
NumberOfGasController: CARDINAL = 7;
-- At present, each of the Bruce 8-stack furnace has a maximum capability
-- of controlling 7 gas controllers.
MinProfiledTemp: ARRAY BruceDefs.TubeNumber OF Index51;
Index51: TYPE =ARRAY Reaction OF CARDINAL;
MaxProfiledTemp: ARRAY BruceDefs.TubeNumber OF Index61;
Index61: TYPE =ARRAY Reaction OF CARDINAL;
maxRampRateUp:CARDINAL;
maxRampRateDown:CARDINAL;
MinGasFlowConversion: CARDINAL;
MaxGasFlowConversion: CARDINAL;
boatSpeedLoBound: CARDINAL;
boatSpeedHiBound: CARDINAL;
boatSpeedSafetyLimit: CARDINAL;
ProfiledTemp: TYPE = CARDINAL;
ProfiledTempIndex: TYPE = [1..NumberOfProfiledTemp];
ProfiledTempTable: TYPE = ARRAY BruceDefs.TubeNumber OF Index11;
Index11: TYPE = ARRAY Reaction OF Index12;
Index12: TYPE = ARRAY ProfiledTempIndex OF ProfiledTemp;
ptt:ProfiledTempTable;
-- The first and the last components of ptt[BruceDefs.TubeNumber][Reaction]
-- are automtically the MinProfiledTemp[BruceDefs.TubeNumber][Reaction] and
-- the MaxProfiledTemp[BruceDefs.TubeNumber][Reaction].
ProfiledTempCorrectionTable: TYPE = ARRAY BruceDefs.TubeNumber OF Index1;
Index1: TYPE = ARRAY Reaction OF Index2;
Index2: TYPE = ARRAY BruceDefs.ZoneIndex OF Index3;
Index3: TYPE = ARRAY ProfiledTempIndex OF CARDINAL;
ptct: ProfiledTempCorrectionTable;
CalibratedGasRate: TYPE = CARDINAL; --this rate is in litres per minute x 100
CalibratedGasRateIndex: TYPE = [1..NumberOfCalibratedGasRate];
CalibratedGasRateTable: TYPE = ARRAY CalibratedGasRateIndex OF
CalibratedGasRate;
cgrt: CalibratedGasRateTable;
-- The first and the last components of cgrt are automatically the
-- MinGasFlowConversion and the MaxGasFlowConversion.
CailibratedGasRateConversionTable: TYPE = ARRAY GasTypeIndex OF Index31;
Index31: TYPE = ARRAY CalibratedGasRateIndex OF CARDINAL;
cgrct: CailibratedGasRateConversionTable;
Gas: TYPE = STRING;
GasControllerIndex: TYPE = [1..NumberOfGasController];
GasTypeIndex: TYPE = [1..NumberOfGasType];
GasPlumbingTable: TYPE = ARRAY BruceDefs.TubeNumber OF Index21;
Index21: TYPE = ARRAY GasControllerIndex OF Gas;
GasTypeTable: TYPE = ARRAY GasTypeIndex OF Gas;
gpt: GasPlumbingTable;
gtt: GasTypeTable;
-- gtt is where you enter the names of the gases that you are going to use
-- in all eight furnace tubes.
CalibratedboatRate: TYPE = CARDINAL; --this rate is in inches per minute
CalibratedboatRateIndex: TYPE = [1..NumberOfCalibratedboatRate];
CalibratedboatRateTable: TYPE = ARRAY CalibratedboatRateIndex OF
CalibratedboatRate;
cbrt: CalibratedboatRateTable;
-- The first and the last components cbrt are automatically the
-- boatSpeedLoBound and the boatSpeedHiBound.
CalibratedboatRateConversionTable: TYPE = ARRAY CalibratedboatRateIndex OF
CARDINAL;
cbrct: CalibratedboatRateConversionTable;
Reaction: TYPE ={Dry, Wet};
R: Reaction;
s:STRING ← [10];
sDry:STRING = "dry";
sWet:STRING = "wet";
j: BruceDefs.GasColIndex;
l: GasTypeIndex;
WriteGasRow: BOOLEAN ← TRUE;
WriteTempRow:BOOLEAN ← TRUE;
GasOneFlow: CARDINAL ← 0;-- Gas1 now is H2.
GasTwoFlow: CARDINAL ← 0;-- Gas2 now is O2.
tNumberOfDigits, gNumberOfDigits, bNumberOfDigits: CARDINAL;
-- tNumberOfFDigits is the number of digits plus decimal if applicable in
-- the temperature parsing. ( eg. tNumberOfDigits for 899.9 is 5.)
-- Similarly gNumberOfDigits and bNumberOfDigits are for gas and boat.
InitTube: ARRAY BruceDefs.TubeNumber OF BOOLEAN;
-- If the idle conitions for a tube (eg. tube 5 ) is specified in the idle
-- condition table , then the corresponding entry in the InitTube array is
-- set to be TRUE. This allows the checking of whether the idle conditions
-- of the tube were set when we parse the tube number in the
-- BruceRecipe.text. ( InitTube[5]← TRUE )
CompileData: TYPE=RECORD[
brp:BruceDefs.RecipePtr,
parsedTube:BOOLEAN,
tube:BruceDefs.TubeNumber];
CompileHandle: TYPE=POINTER TO CompileData;
IdleData: TYPE = RECORD[
IdleTube:BruceDefs.TubeNumber,
IdleReaction: ARRAY BruceDefs.TubeNumber OF Reaction,
IdleTempTable: ARRAY BruceDefs.TubeNumber OF BruceDefs.TempRow
← ALL[ALL[0]],
IdleGasTable: ARRAY BruceDefs.TubeNumber OF BruceDefs.GasRow
← ALL[ALL[0]],
IdleFunction1Table: ARRAY BruceDefs.TubeNumber OF BruceDefs.Function1Bits];
IdleHandle: TYPE = POINTER TO IdleData;
id: IdleData;
InitTables: PUBLIC PROCEDURE [constantsdata: vmD.VirtualMessagePtr] =
-- This procedure calls on six other procedures to intake the informations
-- from the BruceConstants.text file to initialize the furnaces tubes.
BEGIN
ph:ParseHandle ← @pd;
pd:ParseData;
fields: ARRAY [0 .. 6) OF FieldRec ←
[["BoatRateCalibration"L, ParseBoatRateCalibration],
["Constants"L, ParseConstants],
["GasFlowCalibration"L, ParseGasFlowCalibration],
["GasPlumbing"L, ParseGasPlumbing],
["TemperatureCalibration"L, ParseTemperatureCalibration],
["IdleConditions"L, ParseIdleConditions]];
pd.breakSet ← ":.@""+/"L;
pd.blankSet ← "
"L;
ph.message ← constantsdata;
pd.userData ← @id;
FOR i: CARDINAL IN GasTypeIndex DO
gtt[i] ← Storage.String[10];
ENDLOOP;
-- This loop initializes gtt, the gas type table, so that each component
-- ,which is the name of the name of a gas, is a string of at most ten
-- characters.
FOR i: CARDINAL IN GasTypeIndex DO
String.AppendString[gtt[i], "GasName"];
ENDLOOP;
-- This loop sets up the dummy names for the gas type table to be
-- GasName. This implies that the real name of the gas cannot be GasName.
FOR i: CARDINAL IN BruceDefs.TubeNumber DO
FOR k: CARDINAL IN GasControllerIndex DO
gpt[i][k] ← Storage.String[10];
ENDLOOP;
ENDLOOP;
-- This double loop initializes gpt, the gas plumbing table, so that each
-- component , which is the name of a gas, is a string of at most ten
-- characters.
FOR i: CARDINAL IN BruceDefs.TubeNumber DO
FOR k: CARDINAL IN GasControllerIndex DO
String.AppendString[gpt[i][k], "*"];
ENDLOOP;
ENDLOOP;
-- This double loop sets up the gas plumbing table in such a way that if
-- the gas controller of the furance tube is not plumbed then a * is
-- assigned to the corresponding component. This implies that the
-- real name of a gas cannot have the symbol *.
ParseMessage[ph, DESCRIPTOR[fields]];
exD.DisplayBothExceptionLines
["Hi! I am ready. Get your favorite recipe and click the word compile inside", 0, "the lower menu. Let's start cooking! I am hungry! Aren't you?", 0, FALSE];
END; -- of InitTables
ParseBoatRateCalibration: FieldParseProc=
BEGIN
ih:IdleHandle ← ph.userData;
dtcbrt: DescribeTable ← DESCRIPTOR[cbrt];
-- This DesribeTable, dtcbrt, describes the CalibratedBoatRateTable.
dtcbrct: DescribeTable ← DESCRIPTOR[cbrct];
cbrt ←ALL[0]; -- All the entries for cbrt is initialized to be zero.
cbrct ← ALL[0];
IF ParseToLeadingChar[ph] THEN BEGIN
CheckWord[ph, "InchesPerMin"];
FillArray[ph, dtcbrt, boatScaledFactor, boatSpeedInchUpperBound];
CheckWord[ph, "Percentage"];
FillArray[ph, dtcbrct, boatScaledFactor, boatSpeedPercentUpperBound];
boatSpeedLoBound ← cbrt[1];
boatSpeedHiBound ← cbrt[LENGTH[cbrt]];
ParseToEndOfField[ph];
END ELSE ERROR
ParseFault["BoatRateCalibration field is missing.", ph.currentChar-1, ph.currentChar-1];
END; -- of BoatRateCalibration
ParseConstants: FieldParseProc=
BEGIN
SubFieldRecord: TYPE = RECORD
[name: STRING, var: CARDINAL, ScaleFactor: CARDINAL,
LowBound: CARDINAL, HiBound: CARDINAL, Unit: CHARACTER,
found: BOOLEAN ← FALSE];
ih:IdleHandle ← ph.userData;
curPosition, lastPosition: vmD.CharIndex;
word: STRING ← [30];
SubFieldName: STRING ← [30];
ErrorMessage: STRING ← [100];
SubField: ARRAY [0..3) OF SubFieldRecord ←
[["boatSpeedSafetyLimit"L , 0, boatScaledFactor,
boatSpeedLoBound, boatSpeedHiBound, '"],
["maxRampRateUp"L, 0, TempScaledFactor, 0, 1000,
Ascii.ControlD],
["maxRampRateDown"L, 0, TempScaledFactor, 0, 1000,
Ascii.ControlD]];
--The following procedure is an internal procedure called within this
--ParseConstants procedure.
ReadConstantLine: PROCEDURE[ph: ParseHandle, ScaleFactor: CARDINAL,
LowBound: CARDINAL, HiBound: CARDINAL, Unit: CHARACTER]
RETURNS[Value: CARDINAL]=
BEGIN
ParseToNonWhite[ph]; CheckChar[ph, '←];
MissingValueCheck[ph];
Value ← ParseScaledNumber[ph, ScaleFactor, LowBound, HiBound].n;
ParseToNonWhite[ph]; CheckChar[ph, Unit];
ParseToNonWhite[ph]; CheckChar[ph,'/];
CheckWord[ph,"min"];
ParseToEndOfLine[ph];
END;-- Of ReadConstantLine
IF ParseToLeadingChar[ph] THEN BEGIN
WHILE ParseToLeadingChar[ph] DO
curPosition ← ph.currentChar;
SubFieldName.length ← 0;
DO
ParseWord[ph,word];
IF word[0] ~IN ['a..'z] THEN
BEGIN
ph.currentChar ← ph.currentChar-word.length;
EXIT;
END;
String.AppendString[SubFieldName, word];
ENDLOOP;
lastPosition ← ph.currentChar;
FOR i: CARDINAL IN [0..3) DO
IF String.EquivalentString[SubFieldName,SubField[i].name] THEN
BEGIN
IF SubField[i].found = FALSE THEN BEGIN
SubField[i].var ←ReadConstantLine
[ph,SubField[i].ScaleFactor,SubField[i].LowBound,
SubField[i].HiBound, SubField[i].Unit].Value;
SubField[i].found ← TRUE;
EXIT;
END ELSE ERROR ParseFault["Multiple defined subfield illegal",
curPosition,lastPosition];
END
REPEAT
FINISHED =>
ParseFault["Unrecogonizable constant!",curPosition,lastPosition];
ENDLOOP;
ENDLOOP;
boatSpeedSafetyLimit ← SubField[0].var;
maxRampRateUp ← SubField[1].var;
maxRampRateDown ← SubField[2].var;
FOR i: CARDINAL IN [0..3) DO
IF SubField[i].found = FALSE THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, SubField[i].name];
String.AppendString[ErrorMessage," subfield is missing."];
ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
ENDLOOP;
END ELSE ERROR
ParseFault["Constants field is missing.", ph.currentChar-1,
ph.currentChar-1];
END; -- of ParseConstants
ParseGasFlowCalibration: FieldParseProc=
BEGIN
dtcgrct: DescribeTable;
i: CARDINAL ← 0;
ih:IdleHandle ← ph.userData;
dtcgrt: DescribeTable ← DESCRIPTOR[cgrt];
-- This DesribeTable, dtcgrt, describes the CalibratedGasRateTable.
GasTypeName: STRING ← [10];
ErrorMessage: STRING ← [100];
cgrt ← ALL [0];
cgrct ← ALL[ALL[0]];
-- ALL the components of the cgrt and cgrct are initialized to be zero.
IF ParseToLeadingChar[ph] THEN BEGIN
CheckWord[ph, "Gas"];
FillArray[ph, dtcgrt, GasScaledFactor, GasFlowLitreUpperBound];
MinGasFlowConversion ← cgrt[1];
MaxGasFlowConversion ← cgrt[LENGTH[cgrt]];
WHILE ParseToLeadingChar[ph] DO
IF i >= NumberOfGasType THEN
BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "> "];
String.AppendNumber[ErrorMessage, NumberOfGasType, 10];
String.AppendString[ErrorMessage,
" gas types. Change in the BruceCompiler.mesa is needed."];
ERROR ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
ParseWord[ph,GasTypeName];
FOR m: CARDINAL IN [1..i] DO
IF String.EquivalentString[GasTypeName, gtt[m]] THEN ERROR
ParseFault["This gas has already been calibrated.",
ph.currentChar - GasTypeName.length, ph.currentChar];
ENDLOOP;
i ← i +1;
gtt[i].length ← 0;
-- This reintializes the name of the gas to be nil so that we can
-- use String.AppendString
String.AppendString[gtt[i], GasTypeName];
dtcgrct ← DESCRIPTOR[cgrct[i]];
FillArray[ph, dtcgrct, GasScaledFactor,GasFlowPercentUpperBound];
ENDLOOP; -- End of the WHILE statement.
END ELSE
ParseFault["GasFlowCalibration field is missing.", ph.currentChar-1,
ph.currentChar-1];
END; -- Of GasFlowCalibration
ParseGasPlumbing: FieldParseProc=
BEGIN
i, m, n: CARDINAL;
p: CARDINAL ← 0;
GasPlumbingName: STRING ←[10];
ErrorMessage: STRING ← [100];
ih:IdleHandle ← ph.userData;
IF ParseToLeadingChar[ph] THEN BEGIN
WHILE ParseToLeadingChar[ph] DO
IF p>=8 THEN ERROR
ParseFault["There are only 8 furnace tubes.",ph.currentChar,
ph.currentChar + 1];
i ← ParseDecimal[ph,1,8];
p ← p +1;
-- The following codes check whether the tube is plumbed or not.
n ← 0;
FOR q: [1..NumberOfGasController] IN [1..NumberOfGasController] DO
IF String.EquivalentString[gpt[i][q], "*"] THEN n←n+1 ELSE EXIT;
ENDLOOP;
IF n # NumberOfGasController THEN
BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Tube number "];
String.AppendNumber[ErrorMessage, i, 10];
String.AppendString[ErrorMessage, " has already been plumbed."];
ERROR ParseFault[ErrorMessage, ph.currentChar -1, ph.currentChar];
END;-- of tube plumbing check.
FOR k: [1..NumberOfGasController] IN [1..NumberOfGasController] DO
ParseWord[ph, GasPlumbingName];
-- The following codes check the name of the gas with the names in
-- the gas type table.
IF ~String.EquivalentString[GasPlumbingName, "*"] THEN BEGIN
m ← 0;
FOR r: [1..LENGTH[gtt]] IN [1..LENGTH[gtt]] DO
IF ~String.EquivalentString[GasPlumbingName, gtt[r]] THEN m←m+1
ELSE EXIT;
ENDLOOP;
IF m = LENGTH[gtt] THEN ERROR
ParseFault["The gas you specified has not be calibrated at all.",
ph.currentChar-GasPlumbingName.length, ph.currentChar];
END; -- of gas name check.
gpt[i][k].length ←0;
-- this initializes the string to be nil so that we can use
-- String.AppendString.
String.AppendString[gpt[i][k], GasPlumbingName];
ENDLOOP;
IF ParseToLeadingCharOnLine[ph] THEN BEGIN
ParseWord[ph, GasPlumbingName];
ParseFault["You can't have more than 7 gases plumbed.",
ph.currentChar-GasPlumbingName.length, ph.currentChar];
END;
ENDLOOP; -- End of the WHILE statement.
END ELSE
ParseFault["GasPlumbing field is missing.", ph.currentChar-1, ph.currentChar-1];
END; --Of GasPlumbing
ParseTemperatureCalibration: FieldParseProc=
BEGIN
ReactionIndex: TYPE = Reaction[Dry..Wet];
RecordTemp: TYPE = RECORD[a, b,c,d : CARDINAL];
c, i, m, e, w, NoOfDigits: CARDINAL;
pArray: ARRAY [1.. NumberOfProfiledTemp] OF POINTER TO RecordTemp;
pptt:ARRAY [1.. NumberOfProfiledTemp] OF POINTER TO RecordTemp;
RecordArray: ARRAY [1.. NumberOfProfiledTemp] OF RecordTemp;
TemStore: ARRAY [1.. NumberOfProfiledTemp] OF RecordTemp;
curPosition: vmD.CharIndex;
st: STRING ← [10];
ExtraNumber: STRING ← [10];
ErrorMessage: STRING ← [100];
ih:IdleHandle ← ph.userData;
ptt ← ALL[ALL[ALL[0]]]; -- initialize ptt.
ptct ← ALL[ALL[ALL[ALL[0]]]]; -- initialize ptct.
MinProfiledTemp ← ALL[ALL[0]];-- initialize MinProfiledTemp
MaxProfiledTemp ← ALL[ALL[0]];-- initialize MaxProfiledTemp
IF ParseToLeadingChar[ph] THEN BEGIN
WHILE ParseToLeadingChar[ph] DO
curPosition ← ph.currentChar;
-- This provides a "marker" for the error message.
i ← ParseDecimal[ph,1,8];
ParseWord[ph, st];-- This provides the word in the error message below.
ph.currentChar ← ph.currentChar - st.length;
-- this allow the handle to go back in front of the word so that
-- we can use the procedure GetReaction.
R ← GetReaction[ph];
-- The following codes fill the ptt[i][R] and check for full ptt[i][R].
FOR m IN [1..NumberOfProfiledTemp] DO
IF ptt[i][R][m] = 0 THEN BEGIN
MissingValueCheck[ph];
[ptt[i][R][m], NoOfDigits] ←
ParseScaledNumber[ph,TempScaledFactor,0,13000];
-- 1300 degree C is the absolute maximum temperature allowed.
EXIT;
END;
REPEAT
FINISHED => BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Entries for tube "];
String.AppendNumber[ErrorMessage, i, 10];
String.AppendString[ErrorMessage, ", "];
String.AppendString[ErrorMessage, st];
String.AppendString[ErrorMessage,
" process profiled temp table is full."];
ERROR ParseFault[ErrorMessage, curPosition, curPosition];
END;
ENDLOOP;-- End of fill ptt[i][R].
-- The following codes fill the ptct.
FOR k: BruceDefs.ZoneIndex IN BruceDefs.ZoneIndex DO
IF ~ParseToLeadingCharOnLine[ph] THEN BEGIN
ErrorMessage.length ← 0;
String.AppendNumber[ErrorMessage, 4-k , 10];
String.AppendString[ErrorMessage,
" of the 3 entries for the 3 zones are missing."];
ERROR ParseFault[ErrorMessage, ph.currentChar , ph.currentChar];
END;
[ptct[i][R][k][m], NoOfDigits] ←
ParseScaledNumber[ph,TempScaledFactor,0,13000]
ENDLOOP; -- End of fill ptct.
-- The following codes take care of the extra number on the line.
IF ParseToLeadingCharOnLine[ph] THEN BEGIN
ParseWord[ph, ExtraNumber];
e ← ExtraNumber.length;
IF ParseCharIfCharIs[ph, '.] THEN BEGIN
ParseWord[ph, ExtraNumber];
e ← e +1 +ExtraNumber.length;
END;
ERROR ParseFault["There are only three zones in the furnace.",
ph.currentChar-e, ph.currentChar];
END; -- of taking care of the extra number on the line.
ENDLOOP; --End of the WHILE statement.
-- The following codes check for partially filled table.
FOR u : BruceDefs.TubeNumber IN BruceDefs.TubeNumber DO
FOR v: ReactionIndex IN ReactionIndex DO
IF ptt[u][v][1] # 0 AND ptt[u][v][NumberOfProfiledTemp] = 0 THEN
BEGIN
FOR w IN [2..NumberOfProfiledTemp] DO
IF ptt[u][v][w] = 0 THEN EXIT;
ENDLOOP;
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Profiled temp table needs "];
String.AppendNumber[ErrorMessage, NumberOfProfiledTemp - w +1, 10];
String.AppendString[ErrorMessage, " more rows for tube "];
String.AppendNumber[ErrorMessage, u, 10];
IF v = Dry THEN
String.AppendString[ErrorMessage, " dry process."]
ELSE
String.AppendString[ErrorMessage, " wet process."];
ERROR ParseFault[ErrorMessage, ph.currentChar-1,
ph.currentChar-1];
END;
ENDLOOP;
ENDLOOP; -- End of check for partially filled table.
-- The following loops sort out the order of the profiled temperatures.
-- This subroutine is NOT based on binary search.
FOR u : BruceDefs.TubeNumber IN BruceDefs.TubeNumber DO
FOR v: ReactionIndex IN ReactionIndex DO
IF ptt[u][v][1] # 0 THEN BEGIN
FOR g: CARDINAL IN [1..NumberOfProfiledTemp] DO
RecordArray[g] ←[ ptt[u][v][g] , ptct[u][v][1][g] ,
ptct[u][v][2][g], ptct[u][v][3][g]];
pptt[g] ← @RecordArray[g];
ENDLOOP;
pArray ← pptt;
FOR h: CARDINAL IN [2..NumberOfProfiledTemp] DO
c ←h;
UNTIL c = 1 DO
IF pptt[c]↑.a < pptt[c-1]↑.a THEN BEGIN
pArray[c]←pptt[c-1];
pArray[c-1]←pptt[c];
c ← c-1;
pptt ← pArray;
END ELSE BEGIN
c ← c-1;
END;
ENDLOOP;
ENDLOOP;
FOR f: CARDINAL IN [1..NumberOfProfiledTemp] DO
TemStore[f] ← pArray[f]↑;
ENDLOOP;
FOR f: CARDINAL IN [1..NumberOfProfiledTemp] DO
ptt[u][v] [f]← TemStore[f].a;
ptct[u][v][1][f]← TemStore[f].b;
ptct[u][v][2][f]← TemStore[f].c;
ptct[u][v][3][f]← TemStore[f].d;
ENDLOOP;
END;
ENDLOOP;
ENDLOOP;
-- End of the sorting routine.
FOR u : BruceDefs.TubeNumber IN BruceDefs.TubeNumber DO
FOR v: ReactionIndex IN ReactionIndex DO
FOR z: BruceDefs.ZoneIndex IN BruceDefs.ZoneIndex DO
FOR f: CARDINAL IN [2..NumberOfProfiledTemp] DO
IF ptct[u][v][z][f]< ptct[u][v][z][f-1] THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Profiled temp "];
AppendScaleNumber[ptt[u][v][f], TempScaledFactor,ErrorMessage];
String.AppendString[ErrorMessage, " for tube # "];
String.AppendNumber[ErrorMessage, u, 10];
IF v = Dry THEN
String.AppendString[ErrorMessage, " dry process"]
ELSE String.AppendString[ErrorMessage, " wet process"];
String.AppendString[ErrorMessage, " zone # "];
String.AppendNumber[ErrorMessage, z, 10];
String.AppendString[ErrorMessage,
" is not in ascending order. "];
exD.DisplayBothExceptionLines[ ErrorMessage,0, "", 0, FALSE];
IF ~inD.Confirm[2] THEN ERROR ParseFault
["Change profiled temps and click InitTables in the lower menu.",
ph.currentChar, ph.currentChar];
END;
ENDLOOP;
ENDLOOP;
ENDLOOP;
ENDLOOP;
FOR u : BruceDefs.TubeNumber IN BruceDefs.TubeNumber DO
FOR v: ReactionIndex IN ReactionIndex DO
MinProfiledTemp[u][v] ← ptt[u][v][1];
MaxProfiledTemp[u][v] ← ptt[u][v][NumberOfProfiledTemp] ;
ENDLOOP;
ENDLOOP;
END ELSE
ParseFault["TemperatureCalibration field is missing.",
ph.currentChar-1, ph.currentChar-1];
END; --Of TemperatureCalibration
ParseIdleConditions: FieldParseProc=
BEGIN
k,m,n: CARDINAL;
temp: CARDINAL;
gas: CARDINAL;
TempCorrectArray:ARRAY[1..3] OF CARDINAL;
gasName: STRING ← [10];
ErrorMessage: STRING ← [100];
curPosition: vmD.CharIndex;
ih: IdleHandle ←ph.userData;
id.IdleTempTable ← ALL[ALL[0]];
id.IdleGasTable← ALL[ALL[0]];
id.IdleFunction1Table ← ALL[ALL[0]];
FOR q:CARDINAL IN BruceDefs.TubeNumber DO
InitTube[q] ← FALSE; -- this loop initializes the InitTube.
ENDLOOP;
IF ParseToLeadingChar[ph] THEN BEGIN
WHILE ParseToLeadingChar[ph] DO
curPosition ← ph.currentChar;
MissingValueCheck[ph];
id.IdleTube ← ParseDecimal[ph, 1, 8];
IF id.IdleTempTable[id.IdleTube][1] # 0 THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Idle conditions for tube no. "];
String.AppendNumber[ErrorMessage, id.IdleTube, 10];
String.AppendString[ErrorMessage, " have already been entered "];
ERROR ParseFault[ErrorMessage, curPosition, curPosition];
END;
R ← GetReaction [ph];
id.IdleReaction[id.IdleTube] ← R;
IF ptt[id.IdleTube][R][1] = 0 THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Temperatures for tube no. "];
String.AppendNumber[ErrorMessage, id.IdleTube, 10];
IF R = Dry THEN String.AppendString[ErrorMessage, " dry process"]
ELSE String.AppendString[ErrorMessage, " wet process"];
String.AppendString[ErrorMessage, " have not been profiled."];
ERROR ParseFault[ErrorMessage, curPosition, curPosition];
END;
-- Check for plumbing.
n ← 0;
FOR q: [1..NumberOfGasController] IN [1..NumberOfGasController] DO
IF String.EquivalentString[gpt[id.IdleTube][q], "*"] THEN n←n+1
ELSE EXIT;
ENDLOOP;
IF n = NumberOfGasController THEN
BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "Tube number "];
String.AppendNumber[ErrorMessage, id.IdleTube, 10];
String.AppendString[ErrorMessage, " has not been plumbed."];
ERROR ParseFault[ErrorMessage, curPosition, curPosition];
END; -- End of check for plumbing.
InitTube[id.IdleTube] ← TRUE;
MissingValueCheck[ph];
-- The following codes take care of the temperature.
curPosition ← ph.currentChar;
[temp,tNumberOfDigits] ←BruceDefs.ParseTemp
[ph,MinProfiledTemp[id.IdleTube][R],MaxProfiledTemp[id.IdleTube][R]];
FOR p:BruceDefs.TempColIndex IN BruceDefs.TempColIndex DO
id.IdleTempTable[id.IdleTube][p] ← temp
ENDLOOP;
FOR u: ProfiledTempIndex IN ProfiledTempIndex DO
IF temp # ptt[id.IdleTube][R][u] THEN m←u ELSE EXIT;
ENDLOOP;
IF m = NumberOfProfiledTemp THEN BEGIN
exD.DisplayBothExceptionLines
["Temp not profiled. Program will proceed with temp obtained by interpolation.", 0, "", 0, FALSE];
-- There are only two lines in the bottom message window.
BruceDefs.WarningCarat[curPosition, curPosition + tNumberOfDigits];
IF ~inD.Confirm[2] THEN ERROR ParseFault
["Change temperature value and compile again.", curPosition,
curPosition + tNumberOfDigits];
END;
FOR zo: BruceDefs.ZoneIndex IN BruceDefs.ZoneIndex DO
table1: DescribeTable ← DESCRIPTOR[ptt[id.IdleTube][R]];
table2: DescribeTable ← DESCRIPTOR[ptct[id.IdleTube][R][zo]];
recordtable: Record ← [table1, table2];
TempCorrectArray[zo] ← Interpolation2 [temp, recordtable];
ENDLOOP;
id.IdleTempTable[id.IdleTube][1] ← TempCorrectArray[1];
id.IdleTempTable[id.IdleTube][2] ← TempCorrectArray[2];
id.IdleTempTable[id.IdleTube][3] ← TempCorrectArray[3];
-- The following codes take care of the gas.
IF id.IdleTube IN [1..4] THEN
id.IdleFunction1Table[id.IdleTube][3]←1;
--function switch # 6 will be 1 for interval 0 for tube 1 to 4.
DO
ParseWord[ph,gasName];
IF gasName[0] IN ['0..'9] OR gasName[0] ='. OR gasName[0] ='@ THEN
ERROR ParseFault["Please specify the name of gas.",
ph.currentChar-gasName.length -1,
ph.currentChar-gasName.length -1];
FOR i:GasControllerIndex IN GasControllerIndex DO
IF String.EquivalentString[gasName,gpt[id.IdleTube][i]] THEN
BEGIN
k ← i;
EXIT;
END;
REPEAT
FINISHED => ERROR
ParseFault["The system was not hooked up with the type of gas you specified.",
ph.currentChar-gasName.length, ph.currentChar];
ENDLOOP;
ParseToNonWhite[ph];
CheckChar[ph,'@];
MissingValueCheck[ph];
[gas, gNumberOfDigits] ← BruceDefs.ParseGas[ph,
MinGasFlowConversion,MaxGasFlowConversion];
IF String.EquivalentString[gasName,GasOne] THEN GasOneFlow ← gas;
IF String.EquivalentString[gasName,GasTwo] THEN GasTwoFlow ← gas;
IF String.EquivalentString[gasName,"tca"] THEN tcaON ← TRUE;
IF String.EquivalentString[gasName,"O2"] THEN O2ON ← TRUE;
IF String.EquivalentString[gasName,"POCl3"] THEN POCl3ON ← TRUE;
IF String.EquivalentString[gasName,"N2"] AND id.IdleTube IN [1..4]
THEN BEGIN
id.IdleFunction1Table[id.IdleTube][4] ← 1;
k ← 4;
END;
IF String.EquivalentString[gasName,"N2"] AND id.IdleTube IN [5..6]
THEN BEGIN
id.IdleFunction1Table[id.IdleTube][4] ← 1;
k ← 3;
END;
id.IdleGasTable[id.IdleTube][k]←
GetGasFlow[ph, gasName, gas, gNumberOfDigits] ;
id.IdleFunction1Table[id.IdleTube][9-k] ← 1;
IF ~ParseToLeadingCharOnLine[ph] THEN EXIT;
IF ~ParseCharIfCharIs[ph,'+] THEN EXIT;
ENDLOOP; -- End of the gas DO loop.
IF GasOneFlow # 0 AND GasTwoFlow # 0 THEN BEGIN
IF (LONG[GasOneFlow] *LONG[100])/LONG[GasTwoFlow] >=
LONG[GasOneToGasTwoSafetyRatio] THEN BEGIN
GasOneFlow ← 0;
GasTwoFlow ← 0;
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, " The ratio of "];
String.AppendString[ErrorMessage, GasOne];
String.AppendString[ErrorMessage, " to "];
String.AppendString[ErrorMessage, GasTwo];
String.AppendString[ErrorMessage, " >= "];
AppendScaleNumber[GasOneToGasTwoSafetyRatio,
GasScaledFactor, ErrorMessage];
String.AppendString[ErrorMessage,
". Explosion may occur. Process rejected "];
ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
END;
GasOneFlow ← 0;
GasTwoFlow ← 0;
IF tcaON = TRUE AND O2ON = FALSE THEN BEGIN
tcaON ← FALSE;
ParseFault
["You have your tca on without the O2. Please turn on the O2",
ph.currentChar, ph.currentChar];
END;
tcaON ← FALSE;
O2ON ← FALSE;
IF POCl3ON = TRUE AND O2ON = FALSE THEN BEGIN
POCl3ON ← FALSE;
ParseFault
["You have your POCl3 on without the O2. Please turn on the O2",
ph.currentChar, ph.currentChar];
END;
POCl3ON ← FALSE;
O2ON ← FALSE;
ENDLOOP;
END ELSE
ParseFault["IdleConditions field is missing.", ph.currentChar-1,
ph.currentChar-1];
END; --Of IdleConditions
SpecToBinary: PUBLIC PROCEDURE [data, spec: vmD.VirtualMessagePtr,
brp:BruceDefs.RecipePtr] RETURNS[tube:BruceDefs.TubeNumber]=
BEGIN
cd:CompileData;
ph:ParseHandle ← @pd;
pd:ParseData;
fields: ARRAY [0 .. 2) OF FieldRec ←
[["Tube"L, ParseTube], ["Recipe"L, ParseRecipe]];
pd.breakSet ← ":.@""+/"L;
pd.blankSet ← "
"L;
ph.message ← spec;
pd.userData ← @cd;
cd.brp ← brp;
cd.parsedTube ← FALSE;
ParseMessage[ph, DESCRIPTOR[fields]];
RETURN[cd.tube];
END; -- of SpecToBinary
FillArray: PROCEDURE[ph:ParseHandle, dt:DescribeTable, ScaledFactor:CARDINAL, UpperBound:CARDINAL]=
BEGIN
i: CARDINAL;
k: CARDINAL ← 0;
ExtraNumber: STRING ← [10];
ErrorMessage: STRING ← [100];
FOR i IN [1..LENGTH[dt]] DO
IF ~ParseToLeadingCharOnLine[ph] THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "There should be "];
String.AppendNumber[ErrorMessage, LENGTH[dt] - i +1, 10];
String.AppendString[ErrorMessage, " more entries on this line."];
ERROR ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
MissingValueCheck[ph];
dt[i] ← ParseScaledNumber[ph, ScaledFactor, 0,
UpperBound].n;
ENDLOOP;
IF ParseToLeadingCharOnLine[ph] THEN
BEGIN
ParseWord[ph, ExtraNumber];
k ← ExtraNumber.length;
IF ParseCharIfCharIs[ph, '.] THEN BEGIN
ParseWord[ph, ExtraNumber];
k ← k +1 +ExtraNumber.length;
END;
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "> "];
String.AppendNumber[ErrorMessage, LENGTH[dt], 10];
String.AppendString[ErrorMessage,
" entries. Change in the BruceCompiler.mesa is needed."];
ERROR ParseFault[ErrorMessage, ph.currentChar-k, ph.currentChar];
END;
END; -- of FillArray
CheckWord: PROCEDURE[ph:ParseHandle, Word: STRING] =
BEGIN
st: STRING ← [40];
ErrorMessage: STRING ← [100];
IF ParseToLeadingChar[ph] THEN BEGIN
ParseWord[ph,st];
IF ~String.EquivalentString[st, Word] THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "The word should be "];
String.AppendString[ErrorMessage, Word];
String.AppendString[ErrorMessage, ". No space in between."];
ERROR ParseFault[ErrorMessage, ph.currentChar- st.length ,
ph.currentChar];
END;
END ELSE BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, "The word, "];
String.AppendString[ErrorMessage, Word];
String.AppendString[ErrorMessage,
", and the corresponding entries are missing."];
ERROR ParseFault[ErrorMessage, ph.currentChar-1, ph.currentChar-1];
END;
END; -- of CheckWord
CheckAndInc: PROCEDURE[ph:ParseHandle, val:CARDINAL, max:CARDINAL]
RETURNS[newval:CARDINAL]=
BEGIN
IF val=max THEN ERROR ParseFault["Table overflow", ph.currentChar,
ph.currentChar];
newval ← val+1;
END; -- of CheckAndInc
Interpolation2: PROCEDURE[ value:CARDINAL, recordtable: Record] RETURNS[returnValue:CARDINAL] =
BEGIN
a, b, c, d, e: LONG CARDINAL;
FOR i: CARDINAL IN [2..LENGTH[recordtable.a]] DO
IF value <= recordtable.a[i] THEN BEGIN
a ← recordtable.a[i - 1];
b ← recordtable.a[i];
c ← recordtable.b[i - 1];
d ← recordtable.b[i];
IF d >= c THEN
e ←( ( d - c ) * ( value - a ) * 10 / ( b - a ) + 5 ) / 10 + c ELSE
e ← c -( ( c - d ) * ( value - a ) * 10 / ( b - a ) + 5 ) / 10 ;
returnValue ← InlineDefs.LowHalf [e];
EXIT;
END;
ENDLOOP;
END;
GetReaction: PROCEDURE[ph: ParseHandle] RETURNS[R: Reaction] =
BEGIN
ParseToNonWhite[ph];
ParseWord[ph, s];
DO
IF s[s.length-1] IN ['0..'9] THEN BEGIN
s.length ← s.length -1;
ph.currentChar ← ph.currentChar - 1;
END ELSE EXIT;
ENDLOOP;
IF String.EquivalentString[s,sDry] OR String.EquivalentString[s,"d"]
THEN R ←Dry ELSE
IF String.EquivalentString[s,sWet] OR String.EquivalentString[s,"w"]
THEN R ←Wet ELSE
IF s[0] IN ['0..'9] THEN ERROR
ParseFault
["Specify your process by typing in the word Dry(orD) or Wet(orW) at the blinking carat",
ph.currentChar - s.length -1 , ph.currentChar - s.length -1]
ELSE ERROR
ParseFault
["Unknown word. Type in the word Dry(orD) or Wet(orW) at the blinking carat",
ph.currentChar - s.length , ph.currentChar];
END; -- GetReaction
TempCorrection: PROCEDURE [ph:ParseHandle, rp: BruceDefs.RecipePtr, t:CARDINAL, tn:BruceDefs.TubeNumber, R:Reaction, temp:CARDINAL, tNumberOfDigits:CARDINAL]
RETURNS[a, b, c: CARDINAL] =
-- This procedure corrects the temperatures to be used in columns 1, 2 and
-- 3 of the temperature setpoint tables due to the different thermal
-- behavior of the different tubes,the different zones within each tube ,
-- the different temperatures and the different reactions (exothermic or
-- endothermic-) used when one is using the normal control mode.Note that
-- all the temperatures are multiplied by a factor of 10 in order to
-- communicate with the DDC microcontroller of the Bruce furnaces.
BEGIN
m: CARDINAL;
k: CARDINAL ←0;
TempCorrectArray:ARRAY[1..3] OF CARDINAL;
FOR i: ProfiledTempIndex IN ProfiledTempIndex DO
IF temp # ptt[tn][R][i] THEN m←i ELSE EXIT;
ENDLOOP;
FOR i: CARDINAL IN [1..t-1] DO
IF temp = rp.temp[i][6] THEN BEGIN
k←i;
EXIT;
END;
ENDLOOP;
IF k = 0 AND m = NumberOfProfiledTemp THEN BEGIN
exD.DisplayBothExceptionLines["Temp not profiled. Program will proceed with temp obtained by interpolation.", 0, "", 0, FALSE];
-- There are only two lines in the bottom message window.
BruceDefs.WarningCarat
[ph.currentChar-tNumberOfDigits-2, ph.currentChar-2];
IF ~inD.Confirm[2] THEN ERROR ParseFault[" Change temperature value and compile again.", ph.currentChar-tNumberOfDigits-2, ph.currentChar-2];
END;
FOR zo: BruceDefs.ZoneIndex IN BruceDefs.ZoneIndex DO
table1: DescribeTable ← DESCRIPTOR[ptt[tn][R]];
table2: DescribeTable ← DESCRIPTOR[ptct[tn][R][zo]];
recordtable: Record ← [table1, table2];
TempCorrectArray[zo] ← Interpolation2 [temp, recordtable];
ENDLOOP;
a ← TempCorrectArray[1];
b ← TempCorrectArray[2];
c ← TempCorrectArray[3];
END; -- of TempCorrection
GetGasFlow: PROCEDURE [ ph:ParseHandle, gasName:STRING, gasRate: CARDINAL,
gNumberOfDigits: CARDINAL] RETURNS [gasFlow: CARDINAL] =
-- This procedure converts the gas rate in litres per minute (l/m) into
-- percentages of the full flow.
BEGIN
k:CARDINAL ← 0;
table1: DescribeTable ← DESCRIPTOR[cgrt];
table2: DescribeTable;
recordtable: Record ;
FOR i:GasTypeIndex IN GasTypeIndex DO
IF String.EquivalentString[gasName,gtt[i]] THEN BEGIN
l←i;
-- l is a global variable defined at the beginning of this program.
EXIT;
END;
ENDLOOP;
FOR i: CalibratedGasRateIndex IN CalibratedGasRateIndex DO
IF gasRate # cgrt[i] THEN k←i ELSE EXIT;
ENDLOOP;
IF k = NumberOfCalibratedGasRate THEN BEGIN
exD.DisplayBothExceptionLines
["Gas rate not profiled. Program will proceed by interpolation.",
0, "", 0, FALSE];
-- There are only two lines in the bottom message window.
BruceDefs.WarningCarat
[ph.currentChar- gNumberOfDigits-4, ph.currentChar-4];
IF ~inD.Confirm[2] THEN
ERROR ParseFault[" Change the gas rate value and compile again.",
ph.currentChar- gNumberOfDigits-4, ph.currentChar-4];
END;
table2 ← DESCRIPTOR[cgrct[l]];
recordtable ← [table1, table2];
gasFlow ← Interpolation2[gasRate, recordtable];
END; -- of GetGasFlow
BoatRateConversion: PROCEDURE [ ph:ParseHandle, boatRate: CARDINAL,
bNumberOfDigits: CARDINAL] RETURNS [boatRatePercent:CARDINAL] =
-- This procedure convert the boat speed in inches per minute into
-- percentages of the full speed which is inches per minute
BEGIN
k: CARDINAL ←0;
table1: DescribeTable ← DESCRIPTOR[cbrt];
table2: DescribeTable ← DESCRIPTOR[cbrct];
recordtable: Record ← [table1, table2];
FOR i: CalibratedboatRateIndex IN CalibratedboatRateIndex DO
IF boatRate # cbrt[i] THEN k←i ELSE EXIT;
ENDLOOP;
IF k = NumberOfCalibratedboatRate THEN BEGIN
exD.DisplayBothExceptionLines
["Boat rate not profiled. Program will proceed by interpolation.",
0, "", 0, FALSE];
-- There are only two lines in the bottom message window.
BruceDefs.WarningCarat
[ph.currentChar- bNumberOfDigits, ph.currentChar];
IF ~inD.Confirm[2] THEN
ERROR ParseFault[" Change the boat rate value and compile again.",
ph.currentChar- bNumberOfDigits, ph.currentChar];
END;
boatRatePercent ← Interpolation2[boatRate, recordtable];
END; -- of BoatRateConversion:
ParseTube: FieldParseProc=
BEGIN
ErrorMessage: STRING ← [100];
ch:CompileHandle ← ph.userData;
IF ~ParseToLeadingCharOnLine[ph] THEN ERROR
ParseFault["Tube number is missing.", ph.currentChar, ph.currentChar];
ch.tube ← ParseDecimal[ph,1,8];
IF InitTube[ch.tube] = FALSE THEN BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage,
"Go back to the constant table. Idle conditions for tube no. "];
String.AppendNumber[ErrorMessage, ch.tube, 10];
String.AppendString[ErrorMessage, " are missing."];
ERROR ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
ch.parsedTube ← TRUE;
ParseToEndOfField[ph];
END; -- of ParseTube
ParseRecipe: FieldParseProc=
BEGIN
ch:CompileHandle ← ph.userData;
rp:BruceDefs.RecipePtr ← ch.brp;
i:BruceDefs.IntervalIndex ← 0;
t:CARDINAL ← 1;
tt:CARDINAL ← 1;
g:CARDINAL ← 1;
prevCtl:CARDINAL;
prevTemp:CARDINAL;
prevReaction: Reaction;
temp:CARDINAL;
diffTemp: CARDINAL;
diffTempLong : LONG CARDINAL;
correctiveFactor: LONG CARDINAL ← 600;
-- The correctiveFactor is 60 x 10. The number 60 came from the
-- sixty seconds of a minute. The factor 10 came from the fact
-- that we had to scale up the number so as to avoid
-- truncation when we do a division.
maxRampRate:CARDINAL ;
time,hour,min,sec,m:CARDINAL;
timeString1:STRING ← [100];
timeString2:STRING ← [100];
timeString3:STRING ← [100];
ErrorMessage: STRING ← [100];
KeyWord: STRING ← [30];
boatSpeedSafetyString: STRING ← [100];
gas: CARDINAL;
newRow:BruceDefs.GasRow ← ALL[0];
tempnewRow:BruceDefs.TempRow ← ALL[0];
gasName:STRING ← [10];
pushed:BOOLEAN ← FALSE;
pulled:BOOLEAN ← FALSE;
curPosition: vmD.CharIndex;
IF ~ch.parsedTube THEN
ERROR ParseFault["Tube number must be specified before recipe",0,0];
--The following codes take care of the idle conditions.
rp.temp[1] ← id.IdleTempTable[ch.tube];
rp.gas[1] ← id.IdleGasTable[ch.tube];
rp.interval[0].controlAlg ← 0;
rp.interval[0].tempAlarm ← 13; -- disable temperature alarm
prevCtl ← rp.interval[0].controlAlg;
prevTemp ← id.IdleTempTable[ch.tube][4];
prevReaction ← id.IdleReaction[ch.tube];
rp.interval[0].gasAlarm ← 13; -- disable gas alarm
rp.interval[0].controlCoeff ← 0; -- default the control coefficients
rp.interval[0].abortGroup ← 0; -- don't allow any aborts
rp.interval[0].gasTempControl ← 11; -- set temp and gas control row
rp.interval[0].function1 ← id.IdleFunction1Table[ch.tube];
-- End of the idle conditions codes.
WHILE ParseToLeadingChar[ph] DO
curPosition ← ph.currentChar;
i ← CheckAndInc[ph,i,30];
[rp.interval[i].hours, rp.interval[i].minutes, rp.interval[i].seconds]
← ParseTime[ph];
R ← GetReaction [ph];
-- The following codes detect whether the temperatuers of the
-- dry or wet process of the tube have been profiled or not.
IF MinProfiledTemp[ch.tube][Dry] = 0 AND MinProfiledTemp[ch.tube][Wet]
= 0 THEN
BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage,"Temperatures for tube # "];
String.AppendNumber[ErrorMessage, ch.tube, 10];
String.AppendString
[ErrorMessage, " dry and wet processes have not been profiled."];
ERROR ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
IF MinProfiledTemp[ch.tube][R] = 0 THEN
BEGIN
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage,"Tube # "];
String.AppendNumber[ErrorMessage, ch.tube, 10];
IF R = Dry THEN String.AppendString[ErrorMessage, " dry proc"]
ELSE String.AppendString[ErrorMessage, " wet proc"];
String.AppendString[ErrorMessage, " not profiled."];
String.AppendString
[ErrorMessage, "Can use the profiled temps from"];
IF R = Dry THEN String.AppendString
[ErrorMessage, " wet proc instead."]
ELSE String.AppendString[ErrorMessage, " dry proc instead."];
exD.DisplayBothExceptionLines[ErrorMessage, 0, "", 0, FALSE];
BruceDefs.WarningCarat[curPosition - 1, curPosition];
IF ~inD.Confirm[2] THEN
ERROR ParseFault["Profile the process and fill in the temperature calibration table.", ph.currentChar, ph.currentChar];
IF R = Dry THEN R ← Wet ELSE R ← Dry;
END;-- of detection of profiled temperatures.
DO
ParseWord[ph, KeyWord];
DO
IF KeyWord.length = 0 THEN EXIT;
IF KeyWord[KeyWord.length-1] IN ['0..'9] THEN BEGIN
KeyWord.length ← KeyWord.length -1;
ph.currentChar ← ph.currentChar - 1;
END ELSE EXIT;
ENDLOOP;
IF KeyWord[0] IN ['0..'9] THEN BEGIN
ph.currentChar ← ph.currentChar - KeyWord.length;
KeyWord.length ← 0;
EXIT;
END ELSE KeyWord.length ← 0;
ENDLOOP;
MissingValueCheck[ph];
[temp,tNumberOfDigits] ← BruceDefs.ParseTemp
[ph,MinProfiledTemp[ch.tube][R],MaxProfiledTemp[ch.tube][R]];
--The following codes take care of the temp ramp situation.
IF temp # prevTemp THEN BEGIN
timeString1.length ← 0;
timeString2.length ← 0;
timeString3.length ← 0;
IF temp > prevTemp THEN BEGIN
maxRampRate ← maxRampRateUp;
diffTemp ← temp - prevTemp;
String.AppendString[timeString1, "Up"];
String.AppendString[timeString3, "Up"];
END ELSE BEGIN
maxRampRate ← maxRampRateDown;
diffTemp ← prevTemp -temp;
String.AppendString[timeString1, "Down"];
String.AppendString[timeString3, "Down"];
END;
time ← rp.interval[i].hours*3660+rp.interval[i].minutes*60+rp.interval[i].seconds;
diffTempLong ←(LONG[diffTemp]*correctiveFactor)/LONG[time];
IF diffTempLong > maxRampRate*10 AND i > 1 THEN BEGIN
-- the 10 in the maxRampRate*10 arised from the fact that the
-- diffTempLong has a multipication factor 10 through the
-- correctiveFactor
time ← ((diffTemp)*10 / maxRampRate );
sec ← (time - (time/10)*10)*6;
-- Because temperatures are scaled by a factor of 10, therfore
-- time here is in minute, scaled also by a factor of 10.
min ← (diffTemp/maxRampRate) MOD 60;
hour ← ((diffTemp/maxRampRate)/60);
String.AppendString[timeString1,
"Ramp rate too high. Advise to change time to "];
String.AppendNumber[timeString2, hour, 10];
String.AppendString[timeString2, "hr "];
String.AppendNumber[timeString2, min, 10];
String.AppendString[timeString2, "min "];
String.AppendNumber[timeString2, sec, 10];
String.AppendString[timeString2, "sec."];
String.AppendString[timeString1, timeString2];
exD.DisplayBothExceptionLines[timeString1,0,"",0,FALSE];
BruceDefs.WarningCarat[curPosition - 1, curPosition];
String.AppendString[timeString3, " time interval to "];
String.AppendString[timeString3, timeString2];
IF ~inD.Confirm[2] THEN ERROR ParseFault[timeString3,
ph.currentChar -16, ph.currentChar-16];
END;
END;-- of temp ramp situation.
--The following codes take care of the temperature.
--IF ~(i>1 AND temp=prevTemp AND R=prevReaction AND prevCtl=8) THEN BEGIN
--BEGIN
--t ← CheckAndInc[ph,t,8];
--FOR h:BruceDefs.TempColIndex IN BruceDefs.TempColIndex DO
--rp.temp[t][h] ← temp;
--ENDLOOP;
--[rp.temp[t][1] ,rp.temp[t][2],rp.temp[t][3]]
--← TempCorrection[ph,rp, t, ch.tube, R, temp, tNumberOfDigits];
--provide temp corrections for column 1 to 3
--END;
t←CheckAndInc[ph,t,8];
FOR h:BruceDefs.TempColIndex IN BruceDefs.TempColIndex DO
tempnewRow[h]←temp;
ENDLOOP;
[tempnewRow[1],tempnewRow[2],tempnewRow[3]]
← TempCorrection[ph,rp, t,ch.tube, R,temp, tNumberOfDigits];
FOR m IN [1..t) DO
FOR n: BruceDefs.TempColIndex IN BruceDefs.TempColIndex DO
IF rp.temp[m][n] # tempnewRow[n] THEN EXIT;
REPEAT
FINISHED => WriteTempRow ← FALSE;
ENDLOOP;
IF WriteTempRow = FALSE THEN EXIT;
ENDLOOP;
IF WriteTempRow= TRUE THEN BEGIN
rp.temp[t] ← tempnewRow;
tt ← t;
END ELSE BEGIN
tt ← m;
t ← t-1;
END;
rp.interval[i].controlAlg ← IF temp=prevTemp THEN 8 ELSE 0;
rp.interval[i].tempAlarm ← 13; -- disable temperature alarm
prevCtl ← rp.interval[i].controlAlg;
prevTemp ← temp;
-- End of temperature related codes.
-- The following codes take care of the gas table and
-- the gas switches in the interval table.
GasOneFlow ← 0;
GasTwoFlow ← 0;
tcaON ← FALSE;
O2ON ← FALSE;
POCl3ON ← FALSE;
DO
ParseWord[ph,gasName];
IF gasName[0] IN ['0..'9] OR gasName[0] ='. OR gasName[0] ='@
THEN ERROR
ParseFault["Please specify the name of gas.",
ph.currentChar-gasName.length -1, ph.currentChar-gasName.length -1];
FOR p:GasControllerIndex IN GasControllerIndex DO
IF String.EquivalentString[gasName,gpt[ch.tube][p]] THEN BEGIN
j←p;
-- j is a global variable defined at the beginning of
-- this program.
EXIT;
END;
REPEAT
FINISHED => ERROR
ParseFault
["The system was not hooked up with the type of gas you specified.",
ph.currentChar-gasName.length, ph.currentChar];
ENDLOOP;
FOR q:GasTypeIndex IN GasTypeIndex DO
IF String.EquivalentString[gasName,gtt[q]] THEN EXIT;
REPEAT
FINISHED => ERROR
ParseFault
["The type of gas you specified has not been profiled at all.",
ph.currentChar-gasName.length, ph.currentChar];
ENDLOOP;
IF String.EquivalentString[gasName,"Ar"] THEN
rp.interval[i].function1[2] ← 0;
IF String.EquivalentString[gasName,"N2"] THEN
rp.interval[i].function1[2] ← 0;
ParseToNonWhite[ph];
[] ← ParseCharIfCharIs[ph,'@];
MissingValueCheck[ph];
[gas, gNumberOfDigits]
← BruceDefs.ParseGas[ph, MinGasFlowConversion,MaxGasFlowConversion];
IF String.EquivalentString[gasName,GasOne] THEN GasOneFlow ← gas;
IF String.EquivalentString[gasName,GasTwo] THEN GasTwoFlow ← gas;
IF String.EquivalentString[gasName,"tca"] THEN tcaON ← TRUE;
IF String.EquivalentString[gasName,"O2"] THEN O2ON ← TRUE;
IF String.EquivalentString[gasName,"POCl3"] THEN POCl3ON ← TRUE;
IF String.EquivalentString[gasName,"N2"] AND ch.tube IN [1..4] THEN
BEGIN
rp.interval[i].function1[4] ← 1;
j ← 4;
END;
IF String.EquivalentString[gasName,"N2"] AND ch.tube IN [5..6] THEN
BEGIN
rp.interval[i].function1[4] ← 1;
j ← 3;
END;
newRow[j] ←
GetGasFlow[ph, gasName, gas, gNumberOfDigits] ;
rp.interval[i].function1[9-j] ← 1;
-- The order of the switches in the table form and in the
-- DDC microcontroller is opposite. 123456789 in the function1 format
-- of the DDC microcontroller corresponds to 876543210 in the
-- table format seen on the Alto after you click the word compile.
IF ~ParseToLeadingCharOnLine[ph] THEN EXIT;
IF ~ParseCharIfCharIs[ph,'+] THEN EXIT;
ENDLOOP;
prevReaction ← R;
IF GasOneFlow # 0 AND GasTwoFlow # 0 THEN BEGIN
IF (LONG[GasOneFlow] *LONG[100])/LONG[GasTwoFlow] >=
LONG[GasOneToGasTwoSafetyRatio] THEN BEGIN
GasOneFlow ← 0;
GasTwoFlow ← 0;
ErrorMessage.length ← 0;
String.AppendString[ErrorMessage, " The ratio of "];
String.AppendString[ErrorMessage, GasOne];
String.AppendString[ErrorMessage, " to "];
String.AppendString[ErrorMessage, GasTwo];
String.AppendString[ErrorMessage, " >= "];
AppendScaleNumber[GasOneToGasTwoSafetyRatio,
GasScaledFactor, ErrorMessage];
String.AppendString[ErrorMessage,
". EXPLOSION may occur. Process rejected. "];
ParseFault[ErrorMessage, ph.currentChar, ph.currentChar];
END;
END;
GasTwoFlow ← 0;
IF tcaON = TRUE AND O2ON = TRUE THEN rp.interval[i].function1[2] ← 1;
IF tcaON = TRUE AND O2ON = FALSE THEN BEGIN
ParseFault
["You have your tca on without the O2. Please turn on the O2",
ph.currentChar, ph.currentChar];
END;
--tcaON ← FALSE;
IF POCl3ON = TRUE AND O2ON = FALSE THEN BEGIN
--POCl3ON ← FALSE;
ParseFault
["You have your POCl3 on without the O2. Please turn on the O2",
ph.currentChar, ph.currentChar];
END;
--POCl3ON ← FALSE;
--O2ON ← FALSE;
IF ch.tube IN [1..4] THEN rp.interval[i].function1[3] ← 1;
-- The above line of codes will cause the function switch # 6 in the
--interval table to take on the value 1 if the tube specified in the
-- recipe is either tube 1, 2, 3 or 4.
-- End of taking care of the gas table and the gas switches
-- in the interval table.
-- The following codes take care of the temp and gas set in
-- the interval table.
g ← CheckAndInc[ph,g,8];
FOR m IN [1..g) DO
FOR n: BruceDefs.GasColIndex IN BruceDefs.GasColIndex DO
IF rp.gas[m][n]# newRow[n] THEN EXIT;
REPEAT
FINISHED => WriteGasRow ←FALSE;
ENDLOOP;
IF WriteGasRow = FALSE THEN EXIT;
ENDLOOP;
IF WriteGasRow=TRUE THEN BEGIN
rp.gas[g] ← newRow;
rp.interval[i].gasTempControl ← 10*tt +g;
-- set temp and gas control row
END
ELSE BEGIN
rp.interval[i].gasTempControl ← 10*tt +m;
-- set temp and gas control row
g ← g -1;
END;
WriteGasRow ←TRUE;
WriteTempRow ←TRUE;
newRow ← ALL[0];
tempnewRow ← ALL[0];
-- End of temp and gas set.
-- The following codes take care of the temp and gas alarm in
-- the interval table.
rp.interval[i].gasAlarm ← 13; -- disable gas alarm
rp.interval[i].controlCoeff ← 0; -- default the control coefficients
rp.interval[i].abortGroup ← 0; -- don't allow any aborts
-- End of temp and gas alarm.
-- The following codes take care of the boat conditions.
IF ParseToLeadingCharOnLine[ph] THEN BEGIN
w:STRING ← [10];
boatRate:CARDINAL;
ParseWord[ph,w];
IF w[0] IN ['0..'9] OR w[0] ='. OR w[0] ='@ THEN ERROR
ParseFault
["Please specify the boat condition here by the word Push or Pull.",
ph.currentChar-w.length -1, ph.currentChar-w.length -1];
DO
IF w[w.length-1] IN ['0..'9] THEN BEGIN
w.length ← w.length -1;
ph.currentChar ← ph.currentChar - 1;
END ELSE EXIT;
ENDLOOP;
IF ~String.EquivalentString["Push"L,w] AND
~String.EquivalentString["Pull"L,w]
THEN ERROR ParseFault
["Wrong word - Please specify the boat condition here by the word Push or Pull.", ph.currentChar-w.length, ph.currentChar];
IF String.EquivalentString["Push"L,w] THEN BEGIN
IF pushed THEN ERROR ParseFault["Can't push more than once",
ph.currentChar-4, ph.currentChar];
pushed ← TRUE;
END ELSE BEGIN
IF pulled THEN ERROR ParseFault["Can't pull more than once",
ph.currentChar-4, ph.currentChar];
IF pulled = FALSE AND pushed = FALSE THEN ERROR
ParseFault["You have to PUSH boat in FIRST then pull it out.",
ph.currentChar-4,ph.currentChar];
pulled ← TRUE;
END;
-- Get boat speed and check for boat safety.
ParseToNonWhite[ph];
[] ← ParseCharIfCharIs[ph, '@];
MissingValueCheck[ph];
[boatRate, bNumberOfDigits]
← ParseScaledNumber[ph, boatScaledFactor,
boatSpeedLoBound, boatSpeedHiBound];
IF boatRate > boatSpeedSafetyLimit THEN BEGIN
String.AppendString[boatSpeedSafetyString,
"Boat speed is too fast -- > "];
AppendScaleNumber[boatSpeedSafetyLimit,boatScaledFactor,
boatSpeedSafetyString];
String.AppendString[boatSpeedSafetyString,
" inches per min. May destroy your wafers."];
exD.DisplayBothExceptionLines
[boatSpeedSafetyString, 0, "", 0, FALSE];
-- There are only two lines in the bottom message window.
BruceDefs.WarningCarat
[ph.currentChar -bNumberOfDigits, ph.currentChar];
IF ~inD.Confirm[2] THEN
ERROR ParseFault[" Change boat speed and compile again.",
ph.currentChar -bNumberOfDigits, ph.currentChar];
boatSpeedSafetyString.length ← 0;
END; --of boat speed and safety check.
-- Set interval table for boat in and out and fill boat table.
IF String.EquivalentString["Push"L,w] THEN BEGIN
rp.boat.in [1][1].speed
← (BoatRateConversion[ ph, boatRate,bNumberOfDigits]+5)/10;
rp.boat.in [2][1].speed
← (BoatRateConversion[ ph, boatRate,bNumberOfDigits]+5)/10;
rp.boat.in [1][1].distance ← 99;
rp.boat.in [2][1].distance ← 99;
rp.interval[i].function2[2] ← 1;
rp.interval[i].function2[1] ← 0;
rp.interval[i].function2[3] ← 0;
--This set function switch 10 to 1 when the boat is pushing in.
END ELSE BEGIN
rp.boat.out[1][1].speed
← (BoatRateConversion[ ph, boatRate,bNumberOfDigits]+5)/10;
rp.boat.out[2][1].speed
← (BoatRateConversion[ ph, boatRate,bNumberOfDigits]+5)/10;
rp.boat.out[1][1].distance ← 0;
rp.interval[i].function2[2] ← 0;
rp.interval[i].function2[1] ← 1;
rp.interval[i].function2[3] ← 1;
END;-- interval table for boat in and out and fill boat table.
ParseToNonWhite[ph]; CheckChar[ph,'"];
ParseToNonWhite[ph]; CheckChar[ph,'/];
ParseWord[ph,w];
IF ~String.EquivalentString["min"L,w] THEN ERROR
ParseFault["Boat rates must given as ""/min",
ph.currentChar-w.length, ph.currentChar];
ParseToEndOfLine[ph];
END ELSE BEGIN
rp.interval[i].function2[2] ← 0;
rp.interval[i].function2[1] ← 0;
END;-- of the codes that take care of the boat conditions.
ENDLOOP;
-- corresponds to the DO in the WHILE ParseToLeadingChar[ph] statement.
IF pulled = FALSE THEN BEGIN
IF pushed = TRUE THEN ERROR
ParseFault["You have forgotten to pull the boat out.",
ph.currentChar, ph.currentChar]
ELSE ERROR
ParseFault
["You have forgotten to push the boat in AND pull the boat out.",
ph.currentChar, ph.currentChar];
END;
END; -- of ParseRecipe
END.