-- File: EchoUserTool.mesa - last edit:
-- AOF 3-Feb-88 18:18:49
-- WIrish 5-Feb-88 12:05:04
-- HGM 7-Nov-85 12:05:21
-- Copyright (C) 1983, 1985, 1988 by Xerox Corporation. All rights reserved.
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],
Runtime USING [GetBcdTime],
String USING [AppendString, AppendChar, AppendNumber],
Time USING [Append, AppendCurrent, Current, Unpack],
Tool USING [
Create, MakeSWsProc, UnusedLogName, MakeMsgSW, MakeFormSW, MakeFileSW,
AddThisSW],
ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType],
UserInput USING [UserAbort],
Window USING [Handle, Box],
PupDefs USING [
PupPackageMake, PupPackageDestroy,
PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, MsToTocks,
SetPupContentsWords, GetPupContentsBytes, DataWordsPerPupBuffer,
AppendPupAddress, GetPupAddress, PupNameTrouble,
AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
PupTypes USING [PupAddress, fillInSocketID, echoSoc, maxDataWordsPerGatewayPup];
EchoUserTool: PROGRAM
IMPORTS
Display, FormSW, Heap, Inline, MsgSW, Process, Put, Runtime,
String, Time, Tool, ToolWindow, UserInput, 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: LONG STRING = [100];
String.AppendString[herald, "Pup Echo User Tool of "L];
Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
[] ← 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: PupDefs.AccessHandle ← PupDefs.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 ← PupDefs.GetBuffer[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;
PupDefs.ReturnBuffer[b];
ENDLOOP;
IF b # NIL THEN PupDefs.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];
PupDefs.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.