-- File: EchoTool.mesa - last edit:
-- AOF 4-Feb-88 13:05:54
-- WIrish 5-Feb-88 12:04:01
-- HGM 5-Sep-85 18:23:20
-- Copyright (C) 1984, 1985, 1988 by Xerox Corporation. All rights reserved.
DIRECTORY
AddressTranslation USING [Error, PrintError, StringToNetworkAddress],
Buffer USING [],
Driver USING [Device],
Format USING [HostNumber, NetworkAddress, NetworkNumber, StringProc],
FormSW USING [
AllocateItemDescriptor, BooleanItem, ClientItemsProcType, CommandItem,
ItemHandle, newLine, NumberItem, ProcType,
StringItem],
Heap USING [systemZone],
Inline USING [BITAND, BITNOT],
NSBuffer USING [Body, Buffer],
NSConstants USING [echoerSocket],
NSTypes USING [maxIDPDataWords],
Process USING [Detach, Pause, Yield],
Put USING [Char, CR, Line, Text],
Runtime USING [GetBcdTime],
Socket USING [
ChannelHandle, Create, Delete,
GetPacket, GetPacketBytes, GetSendBuffer, GetSource, PutPacket,
ReturnBuffer, SetDestination, SetPacketWords, SetWaitTime, TimeOut],
String USING [AppendNumber, AppendLongNumber, AppendString],
SpecialSystem USING [GetProcessorID],
System USING [
GetClockPulses, GetGreenwichMeanTime, GreenwichMeanTime, HostNumber,
NetworkAddress, NetworkNumber, nullSocketNumber,
Pulses, PulsesToMicroseconds, SocketNumber],
Time USING [Append, AppendCurrent, Unpack],
Tool USING [
Create, UnusedLogName, MakeFormSW, MakeFileSW, MakeSWsProc],
ToolWindow USING [TransitionProcType],
Unformat USING [Error, NetworkAddress],
UserInput USING [UserAbort],
Window USING [Handle];
EchoTool: PROGRAM
IMPORTS
Format, FormSW, Heap, Inline, Process, Put, Runtime,
SpecialSystem, String, System, Time, Tool, UserInput, Unformat,
AddressTranslation, Socket
EXPORTS Buffer =
BEGIN
Device: PUBLIC TYPE = Driver.Device;
z: UNCOUNTED ZONE = Heap.systemZone;
-- global variable declarations
log, form: Window.Handle ← NIL;
thisMachineID, remoteAddress: LONG STRING ← NIL;
maxLength: CARDINAL ← NSTypes.maxIDPDataWords;
maxClumpSize: CARDINAL = 50;
clumpSize: CARDINAL ← 50;
running, pleaseStop: BOOLEAN ← FALSE;
fixedLength: BOOLEAN ← FALSE;
noBang, noLate, noLost: BOOLEAN ← FALSE;
checkIt: BOOLEAN ← TRUE;
timeout: CARDINAL ← 3000; -- ms
Init: PROCEDURE =
BEGIN
herald: LONG STRING = [100];
String.AppendString[herald, "Echo Tool of "L];
Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
[] ← Tool.Create[
name: herald, makeSWsProc: MakeThisTool, clientTransition: Transition];
END;
MakeThisTool: Tool.MakeSWsProc =
BEGIN
logFileName: STRING = [40];
form ← Tool.MakeFormSW[window: window, formProc: MakeItemArray];
Tool.UnusedLogName[logFileName, "EchoTool.log$"L];
log ← Tool.MakeFileSW[window: window, name: logFileName, allowTypeIn: FALSE];
END;
MakeItemArray: FormSW.ClientItemsProcType =
BEGIN
nItems: CARDINAL = 15;
i: INTEGER ← -1;
items ← FormSW.AllocateItemDescriptor[nItems];
items[i ← i + 1] ← FormSW.CommandItem[tag: "Stop"L, proc: Stop, place: FormSW.newLine];
items[i ← i + 1] ← FormSW.CommandItem[tag: "PokeOnce"L, proc: PokeOnce];
items[i ← i + 1] ← FormSW.CommandItem[tag: "Echo"L, proc: Echo];
items[i ← i + 1] ← FormSW.CommandItem[tag: "Clumps"L, proc: Clumps];
items[i ← i + 1] ← FormSW.BooleanItem[tag: "FixedLength"L, switch: @fixedLength];
items[i ← i + 1] ← FormSW.NumberItem[tag: "(Max)Length"L, value: @maxLength, boxWidth: 25];
items[i ← i + 1] ← FormSW.NumberItem[tag: "clumpSize"L, value: @clumpSize];
items[i ← i + 1] ← FormSW.CommandItem[tag: "Stats"L, proc: Stats, place: FormSW.newLine];
items[i ← i + 1] ← FormSW.StringItem[
tag: "ThisMachineID"L, string: @thisMachineID, readOnly: TRUE, boxWidth: 100];
items[i ← i + 1] ← FormSW.StringItem[
tag: "RemoteAddress"L, string: @remoteAddress, inHeap: TRUE, boxWidth: 150];
items[i ← i + 1] ← FormSW.BooleanItem[
tag: "CheckIt"L, switch: @checkIt, place: FormSW.newLine];
items[i ← i + 1] ← FormSW.BooleanItem[tag: "!-Off"L, switch: @noBang];
items[i ← i + 1] ← FormSW.BooleanItem[tag: "#-Off"L, switch: @noLate];
items[i ← i + 1] ← FormSW.BooleanItem[tag: "?-Off"L, switch: @noLost];
items[i ← i + 1] ← FormSW.NumberItem[tag: "Timeout(ms)"L, value: @timeout];
IF (i + 1) # nItems THEN ERROR;
RETURN[items, TRUE];
END;
Transition: ToolWindow.TransitionProcType =
BEGIN
SELECT TRUE FROM
old = inactive =>
BEGIN
AppendMe: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
BEGIN String.AppendString[thisMachineID, s]; END;
checkIt ← TRUE;
noBang ← noLate ← noLost ← FALSE;
thisMachineID ← z.NEW[StringBody[50]];
Format.HostNumber[AppendMe, LOOPHOLE[SpecialSystem.GetProcessorID[]], productSoftware];
remoteAddress ← z.NEW[StringBody[50]];
END;
new = inactive =>
BEGIN
z.FREE[@thisMachineID];
z.FREE[@remoteAddress];
END;
ENDCASE;
END;
Stop: FormSW.ProcType =
BEGIN StopIt[]; END;
StopIt: PROCEDURE =
BEGIN pleaseStop ← TRUE; WHILE running DO Process.Pause[1]; ENDLOOP; END;
Stats: FormSW.ProcType = {};
<<
BEGIN
firstDevice: Driver.Device ← Driver.GetDeviceChain[];
FOR network: Driver.Device ← firstDevice, network.next UNTIL network = NIL DO
IF UserInput.UserAbort[log] THEN EXIT;
SELECT network.device FROM
ethernet, ethernetOne =>
BEGIN
pup: PupRouterDefs.NetworkContext ← Protocol1.GetContext[network, pup];
ns: RoutingTable.NetworkContext ← Protocol1.GetContext[network, ns];
stats: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.stats;
Put.Text[log, "Ethernet"L];
IF network.device = ethernetOne THEN Put.Text[log, "One"L];
Put.Text[log, " Statistics for "L];
IF pup # NIL THEN {
Put.Text[log, " Pup "L];
WriteNumber[pup.pupNetNumber, 8, 0];
Put.Text[log, "#"L];
WriteNumber[pup.pupHostNumber, 8, 0];
Put.Text[log, "#, NS net "L]; };
IF ns # NIL THEN WriteNetNumbers[ns.netNumber];
Put.Line[log, "."L];
IF stats = NIL THEN
BEGIN
Put.Line[log, "*** NO DRIVER STATS ***"L];
LOOP;
END;
Put.Text[log, " Rcv: pkts "L];
Put.LongDecimal[log, stats.packetsRecv];
Put.Text[log, ", words "L];
Put.LongDecimal[log, stats.wordsRecv];
Put.Text[log, ", bad "L];
Put.LongDecimal[log, stats.badRecvStatus];
Put.Text[log, ", missed "L];
Put.LongDecimal[log, stats.packetsMissed];
IF stats.idleInput # 0 THEN
BEGIN
Put.Text[log, ", idle "L];
Put.LongDecimal[log, stats.idleInput];
END;
Put.CR[log ];
IF stats.badRecvStatus # 0 OR stats.okButDribble # 0 THEN
BEGIN
Put.Text[log, " crc "L];
Put.LongDecimal[log, stats.badCrc];
Put.Text[log, ", bad alignment but ok crc "L];
Put.LongDecimal[log, stats.badAlignmentButOkCrc];
Put.Text[log, ", crc and bad alignment "L];
Put.LongDecimal[log, stats.crcAndBadAlignment];
Put.CR[log ];
Put.Text[log, " ok but dribble "L];
Put.LongDecimal[log, stats.okButDribble];
Put.Text[log, ", too long "L];
Put.LongDecimal[log, stats.packetTooLong];
Put.Text[log, ", overrun "L];
Put.LongDecimal[log, stats.overrun];
Put.CR[log ];
END;
Put.Text[log, " Xmit: pkts "L];
Put.LongDecimal[log, stats.packetsSent];
Put.Text[log, ", words "L];
Put.LongDecimal[log, stats.wordsSent];
Put.Text[log, ", bad "L];
Put.LongDecimal[log, stats.badSendStatus];
Put.CR[log ];
IF stats.stuckOutput # 0 OR stats.badSendStatus # 0
OR stats.tooManyCollisions # 0 THEN
BEGIN
Put.Text[log, " underrun "L];
Put.LongDecimal[log, stats.underrun];
Put.Text[log, ", stuck "L];
Put.LongDecimal[log, stats.stuckOutput];
Put.Text[log, ", too many collisions "L];
Put.LongDecimal[log, stats.tooManyCollisions];
Put.CR[log ];
END;
Put.Text[log, " Lds:"L];
FOR i: CARDINAL IN [0..16) DO
Put.Char[log, ' ]; Put.LongDecimal[log, stats.loadTable[i]]; ENDLOOP;
Put.CR[log ];
END;
phonenet =>
BEGIN
pup: PupRouterDefs.NetworkContext ← Protocol1.GetContext[network, pup];
ns: RoutingTable.NetworkContext ← Protocol1.GetContext[network, ns];
stats: PhoneNetFriends.PhoneNetInfo = PhoneNetFriends.StatsPtrToStats[network.stats];
Put.Text[log, "PhoneNet Statistics for "L];
IF pup # NIL THEN {
Put.Text[log, "Pup "L];
WriteNumber[pup.pupNetNumber, 8, 0];
Put.Text[log, "#"L];
WriteNumber[pup.pupHostNumber, 8, 0]; };
IF ns # NIL THEN {
Put.Text[log, "#, NS net "L];
WriteNetNumbers[ns.netNumber]; };
Put.Text[log, ", Line "L];
Put.LongDecimal[log, stats.lineNumber];
Put.Text[log, ", "L];
Put.LongDecimal[log, stats.speed];
Put.Text[log, "KB"L];
SELECT stats.remoteHostNumber FROM
System.nullHostNumber =>
Put.Text[log, ", Down"L];
System.localHostNumber =>
Put.Text[log, ", Looped"L];
ENDCASE => {
Put.CR[log ];
Put.Text[log, " Up to "L];
WriteHostNumbers[stats.remoteHostNumber]; };
Put.CR[log ];
Put.Text[log, " Recv: pkts "L];
Put.LongDecimal[log, stats.stats[pktsReceived]];
Put.Text[log, ", bytes "L];
Put.LongDecimal[log, stats.stats[bytesReceived]];
Put.Text[log, ", rejected "L];
Put.LongDecimal[log, stats.stats[pktsRejected]];
Put.Text[log, ", missed "L];
Put.LongDecimal[log, stats.stats[rcvErrorNoGet]];
Put.Text[log, ", idle "L];
Put.LongDecimal[log, stats.stats[tooLongSinceLastReceive]];
Put.CR[log ];
Put.Text[log, " Bad crc "L];
Put.LongDecimal[log, stats.stats[rcvErrorCRC]];
Put.Text[log, ", data lost "L];
Put.LongDecimal[log, stats.stats[rcvErrorDataLost]];
Put.Text[log, ", device error "L];
Put.LongDecimal[log, stats.stats[rcvDeviceError]];
Put.Text[log, ", timeout "L];
Put.LongDecimal[log, stats.stats[rcvErrorFrameTimeout]];
Put.Text[log, ", other error "L];
Put.LongDecimal[log, stats.stats[rcvErrorUnknown]];
Put.CR[log ];
Put.Text[log, " Send: pkts "L];
Put.LongDecimal[log, stats.stats[pktsSent]];
Put.Text[log, ", bytes "L];
Put.LongDecimal[log, stats.stats[bytesSent]];
Put.CR[log ];
Put.Text[log, " NS "L];
Put.LongDecimal[log, stats.stats[nsSent]];
Put.Text[log, " "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.nsBytesSend]];
Put.Text[log, ", Pups "L];
Put.LongDecimal[log, stats.stats[pupSent]];
Put.Text[log, " "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.pupBytesSend]];
Put.Text[log, ", Leaf "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.leafPktsSend]];
Put.Text[log, " "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.leafBytesSend]];
Put.CR[log ];
IF stats.stats[sendErrorBadStatus] # 0 OR stats.stats[queueTooOld] # 0 THEN {
Put.Text[log, " Bad "L];
Put.LongDecimal[log, stats.stats[sendErrorBadStatus]];
Put.Text[log, ", stuck "L];
Put.LongDecimal[log, stats.stats[queueTooOld]];
Put.CR[log ]; };
Put.Text[log, " Queue too long "L];
Put.LongDecimal[log, stats.stats[congestion]];
Put.Text[log, ", NS "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.nsCongestion]];
Put.Text[log, ", Pup "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.pupCongestion]];
Put.CR[log ];
Put.Text[log, " Conn too greedy "L];
Put.LongDecimal[log, stats.stats[connTooGreedy]];
Put.Text[log, ", NS "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.nsTooGreedy]];
Put.Text[log, ", Pup "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.pupTooGreedy]];
Put.CR[log ];
IF stats.stats[PhoneNetExtras.nsDupsFiltered] # 0 THEN {
Put.Text[log, " NS duplicates discarded: "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.nsDupsFiltered]];
Put.CR[log ]; };
IF stats.stats[PhoneNetExtras.pupDupsFiltered] # 0 THEN {
Put.Text[log, " BSP probes/duplicates discarded: "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.pupDupsFiltered]];
Put.CR[log ]; };
IF stats.stats[PhoneNetExtras.leafDupsFiltered] # 0 THEN {
Put.Text[log, " Leaf duplicates discarded: "L];
Put.LongDecimal[log, stats.stats[PhoneNetExtras.leafDupsFiltered]];
Put.CR[log ]; };
END;
ENDCASE => NULL;
ENDLOOP;
END;
>>
WriteHostNumbers: PROCEDURE [net: System.HostNumber] =
BEGIN
Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
BEGIN Put.Text[log, s]; END;
Format.HostNumber[Push, net, productSoftware];
Put.Text[log, "="L];
Format.HostNumber[Push, net, octal];
END;
WriteNetNumbers: PROCEDURE [net: System.NetworkNumber] =
BEGIN
Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
BEGIN Put.Text[log, s]; END;
Format.NetworkNumber[Push, net, productSoftware];
Put.Text[log, "="L];
Format.NetworkNumber[Push, net, octal];
END;
Echo: FormSW.ProcType =
BEGIN
fixed: BOOLEAN;
size: CARDINAL;
errFlag: BOOLEAN ← FALSE;
remoteAddr: System.NetworkAddress;
StopIt[];
IF fixed AND maxLength < 1 THEN
BEGIN
WriteLine["Need at least 2 words."L];
RETURN;
END;
IF maxLength > NSTypes.maxIDPDataWords THEN
BEGIN
WriteLine["(max)Length is too big."L];
RETURN;
END;
fixed ← fixedLength;
size ← maxLength;
WriteCR[];
WriteString["Echoing to "L];
WriteString[remoteAddress];
WriteString[" = "L];
remoteAddr ← GetAddress[remoteAddress, NSConstants.echoerSocket !
Trouble =>
BEGIN
WriteLine[reason];
errFlag ← TRUE;
CONTINUE;
END];
IF errFlag THEN RETURN;
WriteNetworkAddressVerbose[remoteAddr];
WriteLine["."L];
pleaseStop ← FALSE;
running ← TRUE;
Process.Detach[FORK Worker[remoteAddr, fixed, size, 1]];
END;
DelayRange: TYPE = {
d1, d1a, d2, d2a, d3, d3a, d4, d4a,
d5, d5a, d6, d6a, d7, d7a, d8, d8a, d9, d9a,
d10, d14, d20, d28, d50, d70,
d100, d140, d200, d280, d500, d700,
d1000, d1400, d2000, d2800, d5000, d7000,
d10000, d14000, d20000, d28000, more};
delayTime: ARRAY DelayRange OF LONG CARDINAL = [
d1: 1000, d1a: 1500, d2: 2000, d2a: 2500,
d3: 3000, d3a: 3500, d4: 4000, d4a: 4500,
d5: 5000, d5a: 5500, d6: 6000, d6a: 6500,
d7: 7000, d7a: 7500, d8: 8000, d8a: 8500,
d9: 9000, d9a: 9500,
d10: 10000, d14: 14000, d20: 20000, d28: 28000, d50: 50000, d70: 70000,
d100: 100000, d140: 140000, d200: 200000, d280: 280000, d500: 500000, d700: 700000,
d1000: 1000000, d1400: 1400000, d2000: 2000000, d2800: 2800000, d5000: 5000000, d7000: 7000000,
d10000: 10000000, d14000: 14000000, d20000: 20000000, d28000: 28000000, more: LAST[LONG CARDINAL]];
Worker: PROCEDURE [
remoteAddr: System.NetworkAddress, fixed: BOOLEAN,
wordsPerPacket, packetsPerClump: CARDINAL] =
BEGIN
length: CARDINAL = MAX[2, MIN[NSTypes.maxIDPDataWords, wordsPerPacket]];
delay: ARRAY DelayRange OF LONG CARDINAL ← ALL[0];
minDelay: LONG CARDINAL ← LAST[LONG CARDINAL];
maxDelay: LONG CARDINAL ← FIRST[LONG CARDINAL];
picks: ARRAY [0..16] OF CARDINAL ← ALL[0];
drops: ARRAY [0..16] OF CARDINAL ← ALL[0];
sent, good, missed, late, bad, horrible, trash, words: LONG CARDINAL ← 0;
start, stop: System.Pulses;
micro: LONG CARDINAL;
startSec, stopSec: System.GreenwichMeanTime;
AddToDelayHist: PROCEDURE [micro: LONG CARDINAL] =
BEGIN
FOR d: DelayRange IN DelayRange DO -- Slow but clean
IF delayTime[d] < micro THEN LOOP;
delay[d] ← delay[d] + 1;
EXIT;
REPEAT FINISHED => ERROR;
ENDLOOP;
minDelay ← MIN[minDelay, micro];
maxDelay ← MAX[maxDelay, micro];
END;
PrintDelayHist: PROCEDURE =
BEGIN
total: LONG CARDINAL ← 0;
IF good = 0 THEN RETURN;
FOR d: DelayRange IN DelayRange DO
IF delay[d] = 0 THEN LOOP;
total ← total + delay[d];
WriteLongNumber[delay[d], 10, 8];
WriteLongNumber[delay[d]*100/good, 10, 4];
WriteString["%"L];
WriteLongNumber[total, 10, 8];
WriteLongNumber[total*100/good, 10, 4];
WriteString["% packets with delay less than "L];
WriteLongDecimal[delayTime[d]];
WriteLine[" microseconds."L];
ENDLOOP;
WriteString["The min delay was "L];
WriteLongDecimal[minDelay];
WriteLine[" microseconds."L];
WriteString["The max delay was "L];
WriteLongDecimal[maxDelay];
WriteLine[" microseconds."L];
END;
AddToErrorHist: PROCEDURE [
hist: POINTER TO ARRAY [0..16] OF CARDINAL, bits: WORD] =
BEGIN
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
IF howLong # 0 THEN
BEGIN
WriteLongNumber[sent/howLong, 10, 8];
WriteLine[" packets per second."L];
WriteLongNumber[16*words/howLong, 10, 8];
WriteLine[" data bits per second."L];
END;
END;
PrintErrorHist: PROCEDURE =
BEGIN
ShowPercent[good, sent, "good packets received."L];
ShowPercent[missed, sent, "packets missed."L];
ShowPercent[late, sent, "late (or??) packets received."L];
ShowPercent[bad, sent, "bad packets received."L];
ShowPercent[horrible, sent, "packets received with more than 10 words wrong."L];
ShowPercent[trash, sent, "trashy packets received."L];
IF bad # 0 THEN
BEGIN
x: WORD ← 100000B;
WriteCR[];
WriteLine[" Bit Picked Dropped"L];
FOR i: CARDINAL 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;
soc: Socket.ChannelHandle;
sendSequenceNumber, recvSequenceNumber: CARDINAL ← 0;
buffers: ARRAY [0..maxClumpSize) OF NSBuffer.Buffer;
b: NSBuffer.Buffer;
body: NSBuffer.Body;
pktBody: LONG POINTER TO ARRAY [0..0) OF WORD;
soc ← Socket.Create[
socket: System.nullSocketNumber, send: packetsPerClump, receive: packetsPerClump];
Socket.SetWaitTime[soc, timeout];
startSec ← System.GetGreenwichMeanTime[];
UNTIL pleaseStop OR UserInput.UserAbort[log] DO
FOR i: CARDINAL IN [0..packetsPerClump) DO
cycle: CARDINAL;
IF (((sendSequenceNumber ← sendSequenceNumber + 1) MOD 5) = 0) THEN Process.Yield[];
IF (sendSequenceNumber MOD NSTypes.maxIDPDataWords) = 0 THEN
BEGIN
IF ~noBang THEN WriteLine[""L] ELSE WriteChar['.];
END;
cycle ← IF fixed THEN wordsPerPacket - 1 ELSE sendSequenceNumber MOD length;
IF cycle = 0 THEN cycle ← 1;
body ← (b ← Socket.GetSendBuffer[soc]).ns;
Socket.SetDestination[b, remoteAddr];
Socket.SetPacketWords[b, cycle + 1]; -- 1 for echoType
words ← words + cycle + 1;
body.packetType ← echo;
body.echoType ← echoRequest;
pktBody ← @body.echoWords;
FOR k: CARDINAL IN [0..cycle) DO
pktBody[k] ← (k*400B + sendSequenceNumber); ENDLOOP;
buffers[i] ← b;
ENDLOOP;
start ← System.GetClockPulses[];
FOR i: CARDINAL IN [0..packetsPerClump) DO
Socket.PutPacket[soc, buffers[i]];
sent ← sent + 1;
ENDLOOP;
-- now receive the echo or any back logged echos
DO
cycle: CARDINAL;
diff: INTEGER;
b ← Socket.GetPacket[
soc !
Socket.TimeOut =>
BEGIN
UNTIL sendSequenceNumber = recvSequenceNumber DO
missed ← missed + 1;
IF ~noLost THEN WriteChar['?];
recvSequenceNumber ← recvSequenceNumber + 1;
ENDLOOP;
EXIT;
END];
body ← b.ns;
pktBody ← @body.echoWords;
IF body.packetType # echo OR body.echoType # echoResponse THEN
BEGIN
trash ← trash + 1;
WriteChar['%];
Socket.ReturnBuffer[b];
LOOP;
END;
recvSequenceNumber ← recvSequenceNumber + 1;
cycle ← IF fixed THEN length - 1 ELSE recvSequenceNumber MOD length;
IF cycle = 0 THEN cycle ← 1;
diff ← pktBody[0] - recvSequenceNumber;
IF diff < 0 THEN
BEGIN
late ← late + 1;
IF ~noLate THEN WriteChar['#];
Socket.ReturnBuffer[b];
recvSequenceNumber ← recvSequenceNumber - 1;
LOOP;
END;
WHILE diff > 0 DO
missed ← missed + 1;
IF ~noLost THEN WriteChar['?];
recvSequenceNumber ← recvSequenceNumber + 1;
cycle ← IF fixed THEN length - 1 ELSE recvSequenceNumber MOD length;
IF cycle = 0 THEN cycle ← 1;
diff ← pktBody[0] - recvSequenceNumber;
ENDLOOP;
SELECT TRUE FROM
(Socket.GetPacketBytes[b] # 2*(cycle + 1))
OR (pktBody[0] # recvSequenceNumber) =>
BEGIN
trash ← trash + 1;
WriteChar['%];
Socket.ReturnBuffer[b];
LOOP;
END;
ENDCASE =>
BEGIN -- the echo we were looking for
hits: CARDINAL ← 0;
stop ← System.GetClockPulses[];
micro ← System.PulsesToMicroseconds[[stop - start]];
AddToDelayHist[micro];
IF checkIt THEN
FOR k: CARDINAL IN [0..cycle) DO
IF pktBody[k] # (k*400B + recvSequenceNumber) THEN
BEGIN OPEN Inline;
expected, found, picked, dropped: WORD;
IF hits = 0 THEN
BEGIN
WriteCR[];
WriteCurrentDateAndTime[];
WriteString[" Data compare error(s) on packet number "L];
WriteLongDecimal[sent];
WriteLine["."L];
WriteLine["Idx Expected Found Picked Dropped"L];
END;
expected ← k*400B + recvSequenceNumber;
found ← pktBody↑[k];
picked ← BITAND[found, BITNOT[expected]];
dropped ← BITAND[expected, BITNOT[found]];
AddToErrorHist[@picks, picked];
AddToErrorHist[@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;
Socket.ReturnBuffer[b];
IF recvSequenceNumber = sendSequenceNumber THEN EXIT;
END;
ENDLOOP;
ENDLOOP;
stopSec ← System.GetGreenwichMeanTime[];
WriteCR[];
Socket.Delete[soc];
WriteLongNumber[sent, 10, 8];
WriteLine[" packets sent."L];
PrintSummary[stopSec-startSec];
PrintErrorHist[];
PrintDelayHist[];
running ← FALSE;
END;
PokeOnce: FormSW.ProcType =
BEGIN
sequenceNumber: CARDINAL ← 0;
b: NSBuffer.Buffer;
body : NSBuffer.Body;
errFlag: BOOLEAN ← FALSE;
remoteAddr: System.NetworkAddress;
soc: Socket.ChannelHandle;
hit: CARDINAL ← 0;
WriteCR[];
WriteString["Poking "L];
WriteString[remoteAddress];
WriteString[" = "L];
remoteAddr ← GetAddress[remoteAddress, NSConstants.echoerSocket !
Trouble =>
BEGIN
WriteString["AddressTranslation troubles: "L];
WriteLine[reason];
errFlag ← TRUE;
CONTINUE;
END];
IF errFlag THEN RETURN;
WriteNetworkAddressVerbose[remoteAddr];
WriteLine["."L];
soc ← Socket.Create[socket: System.nullSocketNumber, receive: 20];
Socket.SetWaitTime[soc, timeout];
body ← (b ← Socket.GetSendBuffer[soc]).ns;
Socket.SetDestination[b, remoteAddr];
Socket.SetPacketWords[b, 1];
body.packetType ← echo;
body.echoType ← echoRequest;
Socket.PutPacket[soc, b];
DO
b ← Socket.GetPacket[soc ! Socket.TimeOut => EXIT];
body ← b.ns;
SELECT TRUE FROM
(Socket.GetPacketBytes[b] # 2*1) OR (body.echoType # echoResponse) =>
BEGIN
WriteChar['#];
Socket.ReturnBuffer[b];
LOOP;
END;
ENDCASE =>
BEGIN
hit ← hit + 1;
WriteString["Response number "L];
WriteNumber[hit, 10, 2];
WriteString[" from "L];
WriteNetworkAddressVerbose[Socket.GetSource[b]];
WriteLine["."L];
Socket.ReturnBuffer[b];
END;
ENDLOOP;
WriteCR[];
Socket.Delete[soc];
END;
Clumps: FormSW.ProcType =
BEGIN
fixed: BOOLEAN;
size, clumps: CARDINAL;
errFlag: BOOLEAN ← FALSE;
remoteAddr: System.NetworkAddress;
StopIt[];
IF fixed AND maxLength < 1 THEN
BEGIN
WriteLine["Need at least 2 word."L];
RETURN;
END;
IF maxLength > NSTypes.maxIDPDataWords THEN
BEGIN
WriteLine["(max)Length is too big."L];
RETURN;
END;
IF clumpSize > maxClumpSize THEN
BEGIN
WriteLine["ClumpSize is too big."L];
RETURN;
END;
IF clumpSize < 1 THEN
BEGIN
WriteLine["Need at least 1 packet per clump."L];
RETURN;
END;
fixed ← fixedLength;
size ← maxLength;
clumps ← clumpSize;
WriteCR[];
WriteString["Echoing Clumps to "L];
WriteString[remoteAddress];
WriteString[" = "L];
remoteAddr ← GetAddress[remoteAddress, NSConstants.echoerSocket !
Trouble =>
BEGIN
WriteString["AddressTranslation troubles: "L];
WriteLine[reason];
errFlag ← TRUE;
CONTINUE;
END];
IF errFlag THEN RETURN;
WriteNetworkAddressVerbose[remoteAddr];
WriteLine["."L];
pleaseStop ← FALSE;
running ← TRUE;
Process.Detach[FORK Worker[remoteAddr, fixed, size, clumps]];
END;
ShowPercent: PROCEDURE [n, sent: LONG CARDINAL, s: LONG STRING] =
BEGIN
IF n = 0 THEN RETURN;
WriteLongNumber[n, 10, 8];
WriteLongNumber[n*100/sent, 10, 4];
WriteString["% "L];
WriteLine[s];
END;
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;
WriteLongNumber: PROCEDURE [n: LONG CARDINAL, radix, width: CARDINAL] = INLINE
BEGIN
temp: STRING = [25];
String.AppendLongNumber[temp, n, radix];
THROUGH [temp.length..width) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, temp];
END;
WriteNumber: PROCEDURE [n, radix, width: CARDINAL] = INLINE
BEGIN
temp: STRING = [25];
String.AppendNumber[temp, n, radix];
THROUGH [temp.length..width) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, temp];
END;
WriteLongDecimal: PROCEDURE [n: LONG UNSPECIFIED] =
BEGIN
temp: STRING = [32];
String.AppendLongNumber[temp, n, 10];
Put.Text[log, 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, 3]; END;
O9: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 9]; END;
WriteCurrentDateAndTime: PROCEDURE =
BEGIN
temp: STRING = [32];
Time.AppendCurrent[temp];
Put.Text[log, temp];
END;
WriteNetworkAddressVerbose: PROCEDURE [address: System.NetworkAddress] =
BEGIN
temp: STRING = [100];
Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
BEGIN String.AppendString[temp, s]; END;
Format.NetworkAddress[Append, address, octal];
String.AppendString[temp, " = "L];
Format.NetworkAddress[Append, address, productSoftware];
Put.Text[log, temp];
END;
Trouble: ERROR [reason: LONG STRING] = CODE;
GetAddress: PROCEDURE [host: LONG STRING, socket: System.SocketNumber]
RETURNS [addr: System.NetworkAddress] =
BEGIN
localFailed: BOOLEAN ← FALSE;
IF host = NIL THEN ERROR Trouble["NIL => Address Fault"L];
addr ← Unformat.NetworkAddress[host, octal !
Unformat.Error => BEGIN localFailed ← TRUE; CONTINUE; END ];
IF localFailed THEN
BEGIN
addr ← AddressTranslation.StringToNetworkAddress[host !
AddressTranslation.Error =>
BEGIN
temp: STRING = [200];
proc: Format.StringProc = {String.AppendString[temp, s]};
AddressTranslation.PrintError[errorRecord, proc];
ERROR Trouble[temp];
END].addr;
addr.socket ← socket; -- CH returns trash in socket
END;
IF addr.socket = System.nullSocketNumber THEN addr.socket ← socket;
END;
Init[]; -- this gets string out of global frame
END...