-- File: EchoTool.mesa -- BLyon, January 16, 1981 5:58 PM -- HGM, March 14, 1981 12:25 PM -- Please don't forget to update the herald... DIRECTORY AddressTranslation USING [ AppendMyHostNumber, AppendNetworkAddress, StringToNetworkAddress], BufferDefs USING [BufferAccessHandle, OisBuffer], Echo USING [echoRequest, echoResponse], FormSW USING [ AllocateItemDescriptor, BooleanItem, ClientItemsProcType, CommandItem, ItemHandle, line0, line3, line4, line5, line6, newLine, NotifyProcType, NumberItem, ProcType, StringItem], Inline USING [BITAND, BITNOT], MsgSW USING [Post], OISCP USING [ OiscpPackageDestroy, OiscpPackageMake, GetFreeSendOisBufferFromPool, ReturnFreeOisBuffer], OISCPTypes USING [bytesPerPktHeader, bytesPerPktText], OISCPConstants USING [unknownSocketID, echoerSocket], Process USING [Yield, Detach], Put USING [Char, CR, Line, Text], Router USING [FindMyHostID, SetOisCheckit, SetOisDriverLoopback], SpecialSystem USING [HostNumber, NetworkAddress], Socket USING [ Abort, AssignNetworkAddress, Create, Delete, GetPacket, PutPacket, SetWaitTime, TimeOut, TransferStatus], SocketInternal USING [GetBufferPool, SocketHandle], StatsDefs USING [StatPrintCurrent, StatReady, StatSince], Storage USING [Free, FreeStringNil, String], String USING [AppendNumber, AppendLongNumber, AppendString], System USING [], Time USING [AppendCurrent], Tool USING [ Create, UnusedLogName, MakeFormSW, MakeFileSW, MakeMsgSW, MakeSWsProc], ToolWindow USING [TransitionProcType], Window USING [Handle, Place]; EchoTool: PROGRAM IMPORTS FormSW, Inline, MsgSW, OISCP, Process, Put, AddressTranslation, Router, Socket, SocketInternal, StatsDefs, Storage, String, Time, Tool EXPORTS Socket, System SHARES BufferDefs = BEGIN -- EXPORTED TYPE(S) NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress; ChannelHandle: PUBLIC TYPE = SocketInternal.SocketHandle; -- global variable declarations msg, log, form: Window.Handle _ NIL; thisMachineID, localAddress, remoteAddress: STRING _ NIL; localAddr: SpecialSystem.NetworkAddress; maxLength, maxEchos: CARDINAL _ 256; running, pleaseStop: BOOLEAN _ FALSE; driverLoopBack: BOOLEAN _ TRUE; useMax: BOOLEAN _ FALSE; fixedLength: BOOLEAN _ FALSE; verbose: BOOLEAN _ TRUE; checkIt: BOOLEAN _ TRUE; checksums: BOOLEAN _ FALSE; doStats: BOOLEAN _ FALSE; Init: PROCEDURE = BEGIN [] _ Tool.Create[ name: "Echo Tool of March 14, 1981"L, makeSWsProc: MakeThisTool, clientTransition: Transition]; END; MakeThisTool: Tool.MakeSWsProc = BEGIN logFileName: STRING = [40]; msg _ Tool.MakeMsgSW[window: window, lines: 1]; form _ Tool.MakeFormSW[window: window, formProc: MakeItemArray]; Tool.UnusedLogName[logFileName, "EchoTool.log$"L]; log _ Tool.MakeFileSW[window: window, name: logFileName]; END; MakeItemArray: FormSW.ClientItemsProcType = BEGIN echoStartPlace: Window.Place = [x: 2, y: FormSW.line0]; stopPlace: Window.Place = [x: 10*7, y: FormSW.line0]; useMaxPlace: Window.Place = [x: 20*7, y: FormSW.line0]; maxEchosPlace: Window.Place = [x: 30*7, y: FormSW.line0]; thisMachineIDPlace: Window.Place = [x: 2, y: FormSW.line3]; localAddressPlace: Window.Place = [x: 2, y: FormSW.line4]; remoteAddressPlace: Window.Place = [x: 2, y: FormSW.line5]; checkItPlace: Window.Place = [x: 2, y: FormSW.line6]; verbosePlace: Window.Place = [x: 15*7, y: FormSW.line6]; checksumsPlace: Window.Place = [x: 30*7, y: FormSW.line6]; driverLoopBackPlace: Window.Place = [x: 45*7, y: FormSW.line6]; nItems: CARDINAL = 17; i: INTEGER _ -1; items _ FormSW.AllocateItemDescriptor[nItems]; items[i _ i + 1] _ FormSW.CommandItem[ tag: "Echo"L, place: echoStartPlace, proc: EchoProc]; items[i _ i + 1] _ FormSW.CommandItem[ tag: "Stop"L, place: stopPlace, proc: Stop]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "UseMax"L, place: useMaxPlace, switch: @useMax]; items[i _ i + 1] _ FormSW.NumberItem[ tag: "MaxEchos"L, place: maxEchosPlace, value: @maxEchos]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "FixedLength"L, place: FormSW.newLine, switch: @fixedLength]; items[i _ i + 1] _ FormSW.NumberItem[tag: "(Max)Length"L, value: @maxLength]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "DoStats"L, switch: @doStats, place: FormSW.newLine]; items[i _ i + 1] _ FormSW.CommandItem[tag: "Ready"L, proc: Ready]; items[i _ i + 1] _ FormSW.CommandItem[tag: "Recent"L, proc: Since]; items[i _ i + 1] _ FormSW.CommandItem[tag: "Totals"L, proc: Totals]; items[i _ i + 1] _ FormSW.StringItem[ tag: "ThisMachineID"L, place: thisMachineIDPlace, string: @thisMachineID, readOnly: TRUE]; items[i _ i + 1] _ FormSW.StringItem[ tag: "LocalSocket"L, place: localAddressPlace, string: @localAddress, inHeap: TRUE, readOnly: TRUE]; items[i _ i + 1] _ FormSW.StringItem[ tag: "RemoteAddress"L, place: remoteAddressPlace, string: @remoteAddress, inHeap: TRUE]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "CheckIt"L, place: checkItPlace, switch: @checkIt]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "Verbose"L, place: verbosePlace, switch: @verbose]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "Checksums"L, place: checksumsPlace, proc: ToggleChecksums, switch: @checksums]; items[i _ i + 1] _ FormSW.BooleanItem[ tag: "DriverLoopBack"L, place: driverLoopBackPlace, proc: ToggleDriverLoopBack, switch: @driverLoopBack]; IF (i + 1) # nItems THEN ERROR; RETURN[items, TRUE]; END; Transition: ToolWindow.TransitionProcType = BEGIN IF old = inactive THEN -- tool is becomming active BEGIN myHostID: SpecialSystem.HostNumber; OISCP.OiscpPackageMake[]; myHostID _ Router.FindMyHostID[]; Router.SetOisDriverLoopback[driverLoopBack _ TRUE]; Router.SetOisCheckit[checksums _ TRUE]; useMax _ FALSE; verbose _ checkIt _ TRUE; thisMachineID _ Storage.String[1 + 6*SIZE[SpecialSystem.HostNumber]]; AddressTranslation.AppendMyHostNumber[thisMachineID]; localAddr _ Socket.AssignNetworkAddress[]; localAddress _ Storage.String[2 + 6*SIZE[SpecialSystem.NetworkAddress]]; AddressTranslation.AppendNetworkAddress[localAddress, localAddr]; END ELSE IF new = inactive THEN BEGIN thisMachineID _ Storage.FreeStringNil[thisMachineID]; localAddress _ Storage.FreeStringNil[localAddress]; remoteAddress _ Storage.FreeStringNil[remoteAddress]; OISCP.OiscpPackageDestroy[]; END; END; ToggleDriverLoopBack: FormSW.NotifyProcType = BEGIN Router.SetOisDriverLoopback[driverLoopBack]; Put.Text[log, "DriverLoopBack is now "L]; Put.Line[log, IF driverLoopBack THEN "ON."L ELSE "OFF."L]; END; ToggleChecksums: FormSW.NotifyProcType = BEGIN Router.SetOisCheckit[checksums]; Put.Text[log, "Checksums are now "L]; Put.Line[log, IF checksums THEN "ON."L ELSE "OFF."L]; END; Stop: FormSW.ProcType = BEGIN pleaseStop _ TRUE; WHILE running DO Process.Yield[]; ENDLOOP; END; Ready: FormSW.ProcType = BEGIN StatsDefs.StatReady[]; END; Since: FormSW.ProcType = BEGIN StatsDefs.StatSince[log]; END; Totals: FormSW.ProcType = BEGIN StatsDefs.StatPrintCurrent[log]; END; EchoProc: FormSW.ProcType = BEGIN p: PROCESS; useLimit, fixed: BOOLEAN; limit, size: CARDINAL; errFlag: BOOLEAN _ FALSE; myHostID: SpecialSystem.HostNumber _ Router.FindMyHostID[]; remoteAddr: SpecialSystem.NetworkAddress; cH: ChannelHandle; MsgSW.Post[msg, ""L]; IF running THEN BEGIN MsgSW.Post[msg, "An echoer is already running; use Stop to quit it."L]; errFlag _ TRUE; END; useLimit _ useMax; limit _ maxEchos; fixed _ fixedLength; size _ maxLength; remoteAddr _ AddressTranslation.StringToNetworkAddress[remoteAddress ! ANY => BEGIN MsgSW.Post[msg, "RemoteAddress is incorrectly specified; try again."L]; errFlag _ TRUE; CONTINUE; END]; IF errFlag THEN RETURN; IF remoteAddr.socket=OISCPConstants.unknownSocketID THEN remoteAddr.socket _ OISCPConstants.echoerSocket; pleaseStop _ FALSE; running _ TRUE; cH _ Socket.Create[localAddr]; Socket.SetWaitTime[cH, 1500]; -- milli-seconds p _ FORK Echoer[cH, remoteAddr, useLimit, limit, fixed, size]; Process.Detach[p]; MsgSW.Post[msg, "Echoer successfully started."L]; Process.Yield[]; END; Echoer: PROCEDURE [ cH: ChannelHandle, remoteAddr: SpecialSystem.NetworkAddress, useMaxEcho: BOOLEAN, maxEcho: CARDINAL, fixed: BOOLEAN, size: CARDINAL] = BEGIN length: CARDINAL = MIN[OISCPTypes.bytesPerPktText/2, 400B, size]; picks: ARRAY [0..16] OF CARDINAL; drops: ARRAY [0..16] OF CARDINAL; sent, good, missed, late, bad, horrible, error: LONG CARDINAL; ClearCounters: PROCEDURE = BEGIN sent _ good _ missed _ late _ bad _ horrible _ error _ 0; picks _ ALL[0]; drops _ ALL[0]; END; AddToHist: 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 = BEGIN Put.CR[log]; WriteLongDecimal[sent]; Put.Line[log, " packets sent."L]; 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]; IF bad # 0 THEN BEGIN x: WORD _ 100000B; Put.CR[log]; Put.Line[log, " 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 Put.Text[log, " Other"L] ELSE O6[x]; D8[picks[i]]; D8[drops[i]]; Put.CR[log]; END; x _ x/2; ENDLOOP; END; END; ShowPercent: PROCEDURE [n: LONG CARDINAL, s: STRING] = BEGIN IF n = 0 THEN RETURN; WriteLongDecimal[n]; Put.Text[log, " ("L]; WriteLongDecimal[n*100/sent]; Put.Text[log, "%) "L]; Put.Line[log, s]; 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]; temp.length _ 0; 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 time: STRING = [32]; Time.AppendCurrent[time, ]; Put.Text[log, time]; END; PrintSocketEchoError: PROCEDURE [ b: BufferDefs.OisBuffer, status: Socket.TransferStatus] = BEGIN temp: STRING = [100]; String.AppendString[temp, "Error OIS, code="L]; String.AppendString[ temp, SELECT status FROM pending => "pending"L, aborted => "aborted"L, noRouteToNetwork => "noRouteToNetwork"L, hardwareProblem => "hardwareProblem"L, invalidDestAddr => "invalidDestinationAddr"L, goodCompletion => "goodCompletion (?!)"L, ENDCASE => "?????"L]; String.AppendString[temp, ", from: "L]; AddressTranslation.AppendNetworkAddress[temp, b.ois.source]; Put.Line[log, temp]; END; -- sequence number counter: CARDINAL _ 0; -- send/ receive stuff myBufferAccessHandle: BufferDefs.BufferAccessHandle _ SocketInternal.GetBufferPool[cH]; sendBuf, recBuf: BufferDefs.OisBuffer; pktBody: LONG POINTER TO ARRAY [0..0) OF WORD; status: Socket.TransferStatus; ClearCounters[]; IF doStats THEN StatsDefs.StatReady[]; -- note tht are two outstanding Get's before entering this loop UNTIL pleaseStop OR ((counter >= maxEcho) AND useMaxEcho) DO cycle: CARDINAL; IF (((counter _ counter + 1) MOD 5) = 0) THEN Process.Yield[]; cycle _ IF fixed THEN length ELSE counter MOD length; sendBuf _ OISCP.GetFreeSendOisBufferFromPool[myBufferAccessHandle]; sendBuf.ois.pktLength _ OISCPTypes.bytesPerPktHeader + 2*(cycle + 1); sendBuf.ois.transCntlAndPktTp.packetType _ echo; sendBuf.ois.destination _ remoteAddr; pktBody _ @sendBuf.ois.oisWords; pktBody[0] _ Echo.echoRequest; FOR k: CARDINAL IN [0..cycle) DO pktBody[k+1] _ (k*400B + counter); ENDLOOP; Socket.PutPacket[cH, sendBuf]; sent _ sent + 1; -- now receive the echo or any back logged echos DO recBuf _ Socket.GetPacket[ cH ! Socket.TimeOut => BEGIN missed _ missed + 1; Put.Char[log, '?]; EXIT; END]; status _ LOOPHOLE[recBuf.status]; pktBody _ @recBuf.ois.oisWords; SELECT TRUE FROM (status # goodCompletion) => BEGIN -- some kind of error occurred error _ error + 1; PrintSocketEchoError[recBuf, status]; OISCP.ReturnFreeOisBuffer[recBuf]; LOOP; END; (recBuf.ois.pktLength # OISCPTypes.bytesPerPktHeader + 2*(cycle + 1)) OR (pktBody[0] # Echo.echoResponse) OR ((cycle # 0) AND (pktBody[0+1] # (0*400B + counter))) => BEGIN -- probably a late packet, but could be trash, or error late _ late + 1; Put.Char[log, '#]; OISCP.ReturnFreeOisBuffer[recBuf]; LOOP; END; ENDCASE => BEGIN -- the echo we were looking for hits: CARDINAL _ 0; IF checkIt THEN FOR k: CARDINAL IN [0..cycle) DO IF pktBody[k+1] # (k*400B + counter) THEN BEGIN OPEN Inline; expected, found, picked, dropped: WORD; IF hits = 0 THEN BEGIN Put.CR[log]; WriteCurrentDateAndTime[]; Put.Text[log, " Data compare error(s) on packet number "L]; WriteLongDecimal[sent]; Put.Line[log, "."L]; Put.Line[log, "Idx Expected Found Picked Dropped"L]; END; expected _ k + cycle*400B; found _ pktBody^[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]; Put.CR[log]; END; hits _ hits + 1; END; ENDLOOP; IF hits = 0 THEN good _ good + 1 ELSE bad _ bad + 1; IF hits = 0 AND verbose THEN Put.Char[log, '!]; IF hits > 10 THEN BEGIN horrible _ horrible + 1; Put.Line[log, "...."L]; END; OISCP.ReturnFreeOisBuffer[recBuf]; EXIT; END; ENDLOOP; ENDLOOP; -- get back the two recieve buffers Put.CR[log]; Socket.Abort[cH]; IF doStats THEN StatsDefs.StatSince[log]; Socket.Delete[cH]; PrintSummary[]; MsgSW.Post[msg, ""L]; running _ FALSE; END; Init[]; -- this gets string out of global frame END...