-- Copyright (C) 1983, 1985 by Xerox Corporation. All rights reserved. -- EchoUserTool.mesa, HGM, 7-Nov-85 12:05:21 -- Please don't forget to update the herald.... DIRECTORY Display USING [Bitmap, Invert, replaceFlags, White], FormSW USING [ ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, Display, Enumerated, EnumeratedItem, FindItem, BooleanItem, CommandItem, LongNumberItem, NumberItem, StringItem], Heap USING [systemZone], Inline USING [BITNOT, BITAND, HighHalf, LowHalf], MsgSW USING [Post], Process USING [SetPriority], Put USING [Char, CR, Text, Line, LongDecimal], String USING [AppendString, AppendChar, AppendNumber], Time USING [AppendCurrent, Current], Tool USING [ Create, MakeSWsProc, UnusedLogName, MakeMsgSW, MakeFormSW, MakeFileSW, AddThisSW], ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType], UserInput USING [UserAbort], Window USING [Handle, Box], Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer], PupDefs USING [ PupPackageMake, PupPackageDestroy, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, MsToTocks, SetPupContentsWords, GetPupContentsBytes, DataWordsPerPupBuffer, AppendPupAddress, GetPupAddress, PupNameTrouble], PupTypes USING [PupAddress, fillInSocketID, echoSoc, maxDataWordsPerGatewayPup]; EchoUserTool: PROGRAM IMPORTS Display, FormSW, Heap, Inline, MsgSW, Process, Put, String, Time, Tool, ToolWindow, UserInput, Buffer, PupDefs = BEGIN OPEN PupDefs, PupTypes; z: UNCOUNTED ZONE = Heap.systemZone; msg, form, boxes, log: Window.Handle; defaultWaitTime: CARDINAL = 2000; -- ms Pattern: TYPE = { ignore, zeros, ones, alternating, pairs, oneTwentyFive, countBytes, countWords, constant, longConstant}; patternChoices: ARRAY Pattern OF FormSW.Enumerated ¬ [ ignore: ["Ignore", Pattern[ignore]], zeros: ["Zeros", Pattern[zeros]], ones: ["Ones", Pattern[ones]], alternating: ["125252B", Pattern[alternating]], pairs: ["146314B", Pattern[pairs]], oneTwentyFive: ["125B", Pattern[oneTwentyFive]], countBytes: ["CountBytes", Pattern[countBytes]], countWords: ["CountWords", Pattern[countWords]], constant: ["Constant", Pattern[constant]], longConstant: ["DblWdConstant", Pattern[longConstant]]]; -- Be sure to initialize it when it is allocated!!!! data: LONG POINTER TO Data ¬ NIL; -- NIL when we are inactive Data: TYPE = RECORD [ length: CARDINAL ¬ 266, fixedLength: BOOLEAN ¬ FALSE, where: PupAddress ¬ [[0], [0], PupTypes.echoSoc], picks: ARRAY [0..16] OF CARDINAL ¬ ALL[0], drops: ARRAY [0..16] OF CARDINAL ¬ ALL[0], sent, good, missed, late, bad, horrible, error, words: LONG CARDINAL ¬ 0, pleaseStop: BOOLEAN ¬ FALSE, alignTheFink: WORD ¬ NULL, lowPriority: BOOLEAN ¬ FALSE, alignTheFinkAgain: WORD ¬ NULL, noBang: BOOLEAN ¬ FALSE, alignTheFinkStillAgain: WORD ¬ NULL, noLate: BOOLEAN ¬ FALSE, alignTheFinkSomeMore: WORD ¬ NULL, noLost: BOOLEAN ¬ FALSE, alignTheFinkYetAgain: WORD ¬ NULL, waitTime: CARDINAL ¬ defaultWaitTime, checkit: BOOLEAN ¬ TRUE, echoer: PROCESS ¬ NULL, indicator: {left, right, off} ¬ off, running: BOOLEAN ¬ FALSE, constant: LONG CARDINAL ¬ 0, pattern: Pattern ¬ countBytes, target: LONG STRING ¬ NULL]; Initialize: PROCEDURE = BEGIN herald: STRING = "Pup Echo User of 7-Nov-85 12:04:12"L; [] ¬ Tool.Create[ name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition]; END; EchoUserOn: PROCEDURE = BEGIN IF data.length > PupDefs.DataWordsPerPupBuffer[] THEN BEGIN MsgSW.Post[msg, "Length is too long."L]; RETURN; END; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Echoing to "L]; IF ~FindPath[] THEN RETURN; data.running ¬ TRUE; UpdatePicture[]; data.echoer ¬ FORK DoIt[]; END; EchoUserOff: PROCEDURE = BEGIN IF data = NIL THEN RETURN; data.pleaseStop ¬ TRUE; JOIN data.echoer[]; data.running ¬ data.pleaseStop ¬ FALSE; UpdatePicture[]; END; UpdatePicture: PROCEDURE = BEGIN FormSW.FindItem[form, startIX].flags.invisible ¬ data.running; FormSW.FindItem[form, stopIX].flags.invisible ¬ ~data.running; FormSW.Display[form]; END; FindPath: PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN data; WriteString[target]; WriteChar['=]; where ¬ [[0], [0], PupTypes.echoSoc]; GetPupAddress[ @where, target ! PupNameTrouble => BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END]; PrintPupAddress[where]; WriteLine["."L]; RETURN[TRUE]; EXITS Trouble => RETURN[FALSE]; END; ClearCounters: PROCEDURE = BEGIN OPEN data; sent ¬ good ¬ missed ¬ late ¬ bad ¬ horrible ¬ error ¬ words ¬ 0; picks ¬ ALL[0]; drops ¬ ALL[0]; END; AddToHist: PROCEDURE [ hist: LONG POINTER TO ARRAY [0..16] OF CARDINAL, bits: WORD] = BEGIN OPEN data; i: CARDINAL; IF bits = 0 THEN RETURN; SELECT bits FROM 1 => i ¬ 15; 2 => i ¬ 14; 4 => i ¬ 13; 10B => i ¬ 12; 20B => i ¬ 11; 40B => i ¬ 10; 100B => i ¬ 9; 200B => i ¬ 8; 400B => i ¬ 7; 1000B => i ¬ 6; 2000B => i ¬ 5; 4000B => i ¬ 4; 10000B => i ¬ 3; 20000B => i ¬ 2; 40000B => i ¬ 1; 100000B => i ¬ 0; ENDCASE => i ¬ 16; hist[i] ¬ hist[i] + 1; END; PrintSummary: PROCEDURE [howLong: LONG CARDINAL] = BEGIN OPEN data; WriteCR[]; WriteLongDecimal[sent]; WriteLine[" packets sent."L]; IF howLong # 0 THEN BEGIN WriteLongDecimal[sent/howLong]; WriteLine[" packets per second."L]; WriteLongDecimal[16*words/howLong]; WriteLine[" data bits per second."L]; END; IF sent # 0 THEN BEGIN ShowPercent[good, "good packets received."L]; ShowPercent[missed, "packets missed."L]; ShowPercent[late, "late (or??) packets received."L]; ShowPercent[bad, "bad packets received."L]; ShowPercent[horrible, "packets received with more than 10 words wrong."L]; ShowPercent[error, "error packets received."L]; END; IF bad # 0 THEN BEGIN i: CARDINAL; x: WORD ¬ 100000B; WriteCR[]; WriteLine[" Bit Picked Dropped"L]; FOR i IN [0..16] DO IF picks[i] # 0 OR drops[i] # 0 THEN BEGIN IF i = 16 THEN WriteString[" Other"L] ELSE O6[x]; D8[picks[i]]; D8[drops[i]]; WriteCR[]; END; x ¬ x/2; ENDLOOP; END; END; ShowPercent: PROCEDURE [n: LONG CARDINAL, s: LONG STRING] = BEGIN OPEN data; IF n = 0 THEN RETURN; WriteLongDecimal[n]; WriteString[" ("L]; WriteLongDecimal[n*100/sent]; WriteString["%) "L]; WriteLine[s]; END; DoIt: PROCEDURE = BEGIN OPEN data; pool: Buffer.AccessHandle ¬ Buffer.MakePool[send: 1, receive: 10]; soc: PupSocket ¬ PupSocketMake[fillInSocketID, where, MsToTocks[waitTime]]; k: CARDINAL; b: PupBuffer; cycle, maxLength, myLength: CARDINAL; packetNumber: CARDINAL ¬ LAST[CARDINAL]; start, stop: LONG CARDINAL; maxLength ¬ MIN[ length, PupDefs.DataWordsPerPupBuffer[], PupTypes.maxDataWordsPerGatewayPup]; IF lowPriority THEN Process.SetPriority[0]; ClearCounters[]; IF fixedLength THEN BEGIN WriteString["Packet length is "L]; WriteDecimal[maxLength]; WriteLine[" words."L]; END; SetupBoxes[]; start ¬ Time.Current[]; UNTIL pleaseStop OR UserInput.UserAbort[log] DO -- NB: No check for short buffers FOR cycle IN [0..256) UNTIL pleaseStop OR UserInput.UserAbort[log] DO b ¬ Buffer.GetBuffer[pup, pool, send]; myLength ¬ IF fixedLength THEN maxLength ELSE MIN[maxLength, cycle]; SELECT pattern FROM ignore => NULL; zeros => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ 0; ENDLOOP; ones => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ 177777B; ENDLOOP; alternating => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ 125252B; ENDLOOP; pairs => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ 146314B; ENDLOOP; oneTwentyFive => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ 125B; ENDLOOP; countBytes => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ 2*k*400B + (2*k + 1) MOD 400B; ENDLOOP; countWords => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ k; ENDLOOP; constant => FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ Inline.LowHalf[data.constant]; ENDLOOP; longConstant => BEGIN FOR k IN [0..myLength) DO b.pup.pupWords[k] ¬ IF Inline.BITAND[k, 1] = 0 THEN Inline.HighHalf[data.constant] ELSE Inline.LowHalf[data.constant]; ENDLOOP; END; ENDCASE => ERROR; b.pup.pupID.a ¬ b.pup.pupID.b ¬ (packetNumber ¬ packetNumber + 1); b.pup.pupType ¬ echoMe; SetPupContentsWords[b, myLength]; soc.put[b]; sent ¬ sent + 1; words ¬ words + myLength; UNTIL (b ¬ soc.get[]) = NIL DO -- Until timeout, or we find the expected one SELECT TRUE FROM (b.pup.pupType = error) => BEGIN error ¬ error + 1; WriteCR[]; PrintErrorPup[b]; END; ((b.pup.pupType # iAmEcho) OR (b.pup.pupID.a # packetNumber) OR (b.pup.pupID.b # packetNumber) OR (GetPupContentsBytes[b] # 2*myLength)) => BEGIN late ¬ late + 1; IF ~noLate THEN WriteChar['#]; END; ENDCASE => BEGIN hits: CARDINAL ¬ 0; FlipBoxes[]; IF data.checkit THEN FOR k IN [0..myLength) DO expected: WORD; SELECT pattern FROM ignore => EXIT; zeros => expected ¬ 0; ones => expected ¬ 177777B; alternating => expected ¬ 125252B; pairs => expected ¬ 146314B; oneTwentyFive => expected ¬ 125B; countBytes => expected ¬ 2*k*400B + (2*k + 1) MOD 400B; countWords => expected ¬ k; constant => expected ¬ Inline.LowHalf[data.constant]; longConstant => expected ¬ IF Inline.BITAND[k, 1] = 0 THEN Inline.HighHalf[ data.constant] ELSE Inline.LowHalf[data.constant]; ENDCASE => ERROR; IF b.pup.pupWords[k] # expected THEN BEGIN OPEN Inline; found, picked, dropped: WORD; IF hits = 0 THEN BEGIN WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Data compare error(s) on packet number "L]; WriteDecimal[packetNumber]; WriteLine["."L]; WriteLine["Idx Expected Found Picked Dropped"L]; END; found ¬ b.pup.pupWords[k]; picked ¬ BITAND[found, BITNOT[expected]]; dropped ¬ BITAND[expected, BITNOT[found]]; AddToHist[@picks, picked]; AddToHist[@drops, dropped]; IF hits < 10 THEN BEGIN O3[k]; O9[expected]; O9[found]; O9[picked]; O9[dropped]; WriteCR[]; END; hits ¬ hits + 1; END; ENDLOOP; IF hits = 0 THEN good ¬ good + 1 ELSE bad ¬ bad + 1; IF hits = 0 AND ~noBang THEN WriteChar['!]; IF hits > 10 THEN BEGIN horrible ¬ horrible + 1; WriteLine["...."L]; END; EXIT; -- found the expected one END; Buffer.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN Buffer.ReturnBuffer[b] ELSE BEGIN missed ¬ missed + 1; IF ~noLost THEN WriteChar['?]; END; ENDLOOP; IF ~noBang THEN WriteCR[] ELSE WriteChar['.]; ENDLOOP; stop ¬ Time.Current[]; WriteCR[]; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; SetDownBoxes[]; PrintSummary[stop - start]; IF ~pleaseStop THEN UpdatePicture[]; END; -- IO things WriteChar: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[log, c]; END; WriteCR: PROCEDURE = BEGIN Put.CR[log]; END; WriteString: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[log, s]; END; WriteLine: PROCEDURE [s: LONG STRING] = BEGIN Put.Line[log, s]; END; WriteLongDecimal: PROCEDURE [n: LONG CARDINAL] = BEGIN Put.LongDecimal[log, n]; END; WriteDecimal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 0]; END; WriteOctal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 8, 0]; END; WriteNumber: PROCEDURE [n, radix, width: CARDINAL] = INLINE BEGIN temp: STRING = [25]; String.AppendNumber[temp, n, radix]; THROUGH [temp.length..width) DO WriteChar[' ]; ENDLOOP; WriteString[temp]; END; D8: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 8]; END; O3: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O6: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 6]; END; O9: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 9]; END; WriteCurrentDateAndTime: PROCEDURE = BEGIN time: STRING = [20]; Time.AppendCurrent[time]; WriteString[time]; END; PrintPupAddress: PROCEDURE [a: PupAddress] = BEGIN temp: STRING = [40]; AppendPupAddress[temp, a]; WriteString[temp]; END; PrintErrorPup: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] = BEGIN i, len: CARDINAL; temp: STRING = [100]; String.AppendString[temp, "Error Pup, code="L]; String.AppendNumber[temp, b.pup.errorCode, 8]; String.AppendString[temp, ", from: "L]; AppendPupAddress[temp, b.pup.source]; String.AppendString[temp, ": "L]; len ¬ PupDefs.GetPupContentsBytes[b]; FOR i IN [0..len - 2*(10 + 1 + 1)) UNTIL temp.length = temp.maxlength DO String.AppendChar[temp, b.pup.errorText[i]]; ENDLOOP; WriteLine[temp]; MsgSW.Post[msg, temp]; END; indicatorBox: Window.Box = [[25, 10], [16, 16]]; DisplayBoxes: ToolWindow.DisplayProcType = BEGIN pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD; left: WORD = 177400B; right: WORD = 000377B; SELECT data.indicator FROM left => pattern ¬ [ALL[left], ALL[right]]; right => pattern ¬ [ALL[right], ALL[left]]; off => pattern ¬ [ALL[0], ALL[0]]; ENDCASE; Display.Bitmap[window, indicatorBox, [@pattern, 0, 0], 16, Display.replaceFlags] END; SetupBoxes: PROCEDURE = BEGIN data.indicator ¬ left; DisplayBoxes[boxes]; END; FlipBoxes: PROCEDURE = BEGIN SELECT data.indicator FROM left => data.indicator ¬ right; off, right => data.indicator ¬ left; ENDCASE; Display.Invert[boxes, indicatorBox]; END; SetDownBoxes: PROCEDURE = BEGIN data.indicator ¬ off; Display.White[boxes, indicatorBox]; END; MakeBoxesSW: PROCEDURE [window: Window.Handle] = BEGIN box: Window.Box ¬ ToolWindow.nullBox; box.dims.h ¬ 36; boxes ¬ ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes, box: box]; Tool.AddThisSW[window: window, sw: boxes, swType: vanilla]; END; Start: FormSW.ProcType = BEGIN EchoUserOn[]; END; Stop: FormSW.ProcType = BEGIN EchoUserOff[]; END; MakeSWs: Tool.MakeSWsProc = BEGIN logFileName: STRING = [40]; msg ¬ Tool.MakeMsgSW[window: window, lines: 5]; form ¬ Tool.MakeFormSW[window: window, formProc: MakeForm]; MakeBoxesSW[window]; Tool.UnusedLogName[logFileName, "EchoUser.log$"L]; log ¬ Tool.MakeFileSW[window: window, name: logFileName, allowTypeIn: FALSE]; END; startIX: CARDINAL = 0; stopIX: CARDINAL = 1; runningIX: CARDINAL = 2; MakeForm: FormSW.ClientItemsProcType = BEGIN nParams: CARDINAL = 13; items ¬ FormSW.AllocateItemDescriptor[nParams]; items[0] ¬ FormSW.CommandItem[ tag: "Start"L, proc: Start, place: FormSW.newLine]; items[1] ¬ FormSW.CommandItem[ tag: "Stop"L, proc: Stop, place: FormSW.newLine, invisible: TRUE]; items[2] ¬ FormSW.BooleanItem[tag: "LowPriority"L, switch: @data.lowPriority]; items[3] ¬ FormSW.NumberItem[ tag: "WaitTime(ms)"L, value: @data.waitTime, default: defaultWaitTime]; items[4] ¬ FormSW.BooleanItem[ tag: "!-Off"L, switch: @data.noBang, place: FormSW.newLine]; items[5] ¬ FormSW.BooleanItem[tag: "#-Off"L, switch: @data.noLate]; items[6] ¬ FormSW.BooleanItem[tag: "?-Off"L, switch: @data.noLost]; items[7] ¬ FormSW.BooleanItem[tag: "Checkit"L, switch: @data.checkit]; items[8] ¬ FormSW.EnumeratedItem[ tag: "Pattern"L, value: @data.pattern, choices: LOOPHOLE[LONG[DESCRIPTOR[patternChoices]]]]; items[9] ¬ FormSW.LongNumberItem[ tag: "Constant"L, value: @data.constant, default: 0, radix: octal]; items[10] ¬ FormSW.BooleanItem[ tag: "FixedLength"L, switch: @data.fixedLength, place: FormSW.newLine]; items[11] ¬ FormSW.NumberItem[tag: "(Max)Length"L, value: @data.length]; items[12] ¬ FormSW.StringItem[ tag: "Target"L, string: @data.target, place: FormSW.newLine, inHeap: TRUE]; RETURN[items, TRUE]; END; AlreadyActive: ERROR = CODE; NotActive: ERROR = CODE; ClientTransition: ToolWindow.TransitionProcType = BEGIN SELECT TRUE FROM old = inactive => BEGIN IF data # NIL THEN ERROR AlreadyActive; data ¬ z.NEW[Data]; data­ ¬ []; data.target ¬ z.NEW[StringBody[20]]; String.AppendString[data.target, "ME"L]; [] ¬ PupDefs.PupPackageMake[]; END; new = inactive => BEGIN IF data = NIL THEN ERROR NotActive; IF data.running THEN EchoUserOff[]; PupDefs.PupPackageDestroy[]; z.FREE[@data.target]; z.FREE[@data]; END; ENDCASE; END; -- Main Body Initialize[]; END.