-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- OthelloPup.mesa, WIrish,  3-Jun-86 12:55:53

-- File: OthelloPup.mesa,  Last Edit: HGM  May 22, 1979  10:35 PM
-- File: OthelloPup.mesa,  Last Edit: Fay/BLyon August 21, 1980  3:42 PM
-- File: OthelloPup.mesa,  Last Edit: BLyon August 29, 1980  9:21 AM
-- File: OthelloPup.mesa,  Last Edit: ?? 25-Nov-81 13:22:47
-- File: OthelloPup.mesa,  Last Edit: Fay 17-Dec-81 14:22:18
-- File: OthelloPup.mesa,  Last Edit: HGM, 13-May-86  1:52:17

DIRECTORY
  Inline USING [LongCOPY],
  Process USING [Detach, GetPriority, Pause, Priority, priorityBackground, SetPriority, Yield],

  Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
  Driver USING [GetDeviceChain, Network],
  OthelloDefs,
  PhoneNetFriends USING [PhoneNetInfo, StatsPtrToStats],
  Protocol1 USING [GetContext],
  PupDefs USING [
    AppendPupAddress, DataWordsPerPupBuffer, GetPupAddress,
    GetPupContentsBytes, PupBuffer, PupNameTrouble,
    PupPackageDestroy, PupPackageMake, PupSocket, PupSocketDestroy,
    PupSocketMake, SecondsToTocks, SetPupContentsBytes],
  PupRouterDefs USING [GetRoutingTable, NetworkContext, RoutingTableEntry, RoutingTableObject],
  PupTypes USING [echoSoc, fillInSocketID, PupAddress];

OthelloPup: MONITOR
  IMPORTS
    Inline, Process,
    Buffer, Driver, PhoneNetFriends, Protocol1,
    PupDefs, PupRouterDefs, OthelloDefs
  EXPORTS Buffer =
  BEGIN OPEN OthelloDefs;

  Network: PUBLIC TYPE = Driver.Network;

  PrintErrorPup:  PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    source: PupTypes.PupAddress ← b.pup.source;
    NewLine[];
    IF b.pup.pupType = error THEN
      BEGIN
      len: CARDINAL = PupDefs.GetPupContentsBytes[b];
      WriteString["[Error Pup, code="L];
      WriteOctal[LOOPHOLE[b.pup.errorCode]];
      WriteString[", from: "L];
      PrintPupAddress[@source];
      WriteString["] "L];
      FOR i: CARDINAL IN [0..len - 2*(10 + 1 + 1)) DO
        WriteChar[b.pup.errorText[i]] ENDLOOP;
      END
    ELSE
      BEGIN
      WriteString[" ***** "L];
      WriteString["Funny PupType = "L];
      WriteOctal[LOOPHOLE[b.pup.pupType]];
      WriteString[" ***** "L];
      END;
    NewLine[];
    END;
      
  EchoUser: PROCEDURE =
    BEGIN OPEN PupDefs, PupTypes;
    bytesPerBuffer: CARDINAL;
    funny, late: LONG CARDINAL ← 0;
    recv, sent: LONG CARDINAL ← 0;
    wrong: LONG CARDINAL ← 0;
    me, where: PupAddress ← [, , echoSoc];
    mySoc: PupSocket;
    pool: Buffer.AccessHandle;
    packetNumber: CARDINAL ← 0;
    routing: PupRouterDefs.RoutingTableEntry;
    pleaseStop: BOOLEAN ← FALSE;
    Watch: PROCEDURE = 
      BEGIN
      [] ← ReadChar[ ! ABORTED => CONTINUE];
      pleaseStop ← TRUE;
      END;
    GetName["Echo to: "L, @echoName];
    GetPupAddress[
      @where, echoName ! PupNameTrouble => AbortingCommand[e]];
    routing ← @PupRouterDefs.GetRoutingTable[][where.net];
    IF routing=NIL OR routing.context = NIL THEN AbortingCommand["Can't reach that network"L];
    mySoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[5]];
    pool ← Buffer.MakePool[send: 3, receive: 5];
    me ← mySoc.getLocalAddress[];
    PrintPupAddress[@me];
    WriteString[" => "L];
    IF routing.hop # 0 THEN {
      WriteOctal[routing.context.pupNetNumber];
      WriteChar['#];
      WriteOctal[routing.route];
      WriteChar['#];
      WriteString[" => "L]; };
    PrintPupAddress[@where];
    NewLine[];
    Process.Detach[FORK Watch[]];
    bytesPerBuffer ← 2*MIN[DataWordsPerPupBuffer[], newMaxDataWordsPerGatewayPup];
    UNTIL pleaseStop DO
      FOR len: CARDINAL IN [0..bytesPerBuffer] UNTIL pleaseStop DO
        b: PupBuffer;
	b ← Buffer.GetBuffer[pup, pool, send, fullBuffer];
        b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1);
        b.pup.pupType ← echoMe;
        SetPupContentsBytes[b, len];
        Inline.LongCOPY[to: @b.pup.pupBytes, from: @packet, nwords: (len+1)/2];
        mySoc.put[b];
        sent ← sent + 1;
        Process.Yield[];  -- be sure we don't hog machine
        UNTIL (b ← mySoc.get[]) = NIL DO
          SELECT TRUE FROM
            (b.pup.pupType # iAmEcho) => {funny ← funny + 1; PrintErrorPup[b]};
            ((b.pup.pupID.a # packetNumber) OR (b.pup.pupID.b # packetNumber)
              OR (len # GetPupContentsBytes[b])) => {
              WriteChar['#]; late ← late + 1};
            ENDCASE => {
              FOR i: CARDINAL IN [0..len) DO
                IF b.pup.pupBytes[i] # (i MOD 400B) THEN {
                  wrong ← wrong + 1; WriteChar['~]; GOTO Wrong};
                ENDLOOP;
              WriteChar['!];
              recv ← recv + 1;
              EXIT};
          Buffer.ReturnBuffer[b];
          REPEAT Wrong => NULL;
          ENDLOOP;
        IF b # NIL THEN Buffer.ReturnBuffer[b] ELSE WriteChar['?];
        ENDLOOP;
      NewLine[];
      ENDLOOP;
    WriteLine["."L];
    WriteString["Out: "L];
    WriteLongNumber[sent];
    WriteString[", In: "L];
    WriteLongNumber[recv];
    IF sent # 0 THEN {
      WriteString[" ("L];
      WriteLongNumber[(recv*100)/sent];
      WriteLine["%)"L];
      IF late # 0 THEN {
        WriteString["Late: "L];
        WriteLongNumber[late];
        WriteString[" ("L];
        WriteLongNumber[(late*100)/sent];
        WriteLine["%)"L]; }; };
    IF funny # 0 THEN {
      WriteLongNumber[funny];
      WriteLine[" funny"L]; };
    IF wrong # 0 THEN {
      WriteLongNumber[wrong];
      WriteLine[" wrong data"L]; };
    Buffer.DestroyPool[pool];
    PupSocketDestroy[mySoc];
    END;

  buffersInFlight: CARDINAL = 3;
  PupSender: PROCEDURE =
    BEGIN OPEN PupDefs, PupTypes;
    bytesPerBuffer: CARDINAL ← 2*MIN[DataWordsPerPupBuffer[], newMaxDataWordsPerGatewayPup];
    where, me: PupTypes.PupAddress ← [, , echoSoc];
    mySoc: PupSocket;
    sent, wait, echo, full, other: LONG CARDINAL ← 0;
    pleaseStop: BOOLEAN ← FALSE;
    snarfer: PROCESS;
    pool: Buffer.AccessHandle;
    priority: Process.Priority = Process.GetPriority[];
    routing: PupRouterDefs.RoutingTableEntry;
    pause: CONDITION;
    Kick: ENTRY PROCEDURE = { NOTIFY pause; };
    Wait: ENTRY PROCEDURE = { WAIT pause; };
    Watch: PROCEDURE = 
      BEGIN
      [] ← ReadChar[ ! ABORTED => CONTINUE];
      pleaseStop ← TRUE;
      Kick[];
      END;
    Snarf: PROCEDURE = 
      BEGIN
      UNTIL pleaseStop DO
        b: PupBuffer ← mySoc.get[];
	IF b = NIL THEN { wait ← sent; Kick[]; LOOP; };
        IF b.pup.pupType = iAmEcho THEN
	  BEGIN
	  wait ← LOOPHOLE[b.pup.pupID];
	  echo ← echo + 1;
	  Kick[];
	  IF (echo MOD 100) = 0 THEN WriteChar['r];
	  END
        ELSE
          BEGIN
          IF b.pup.pupType = error AND b.pup.errorCode # resourceLimitsPupErrorCode THEN
	    BEGIN
	    other ← other + 1;
	    PrintErrorPup[b];
	    END
          ELSE full ← full + 1;
          END;
	Buffer.ReturnBuffer[b];
        ENDLOOP;
      Kick[];
      END;
    GetName["Send to: "L, @echoName];
    GetPupAddress[
      @where, echoName ! PupNameTrouble => AbortingCommand[e]];
    routing ← @PupRouterDefs.GetRoutingTable[][where.net];
    IF routing=NIL OR routing.context = NIL THEN AbortingCommand["Can't reach that network"L];
    mySoc ← PupSocketMake[fillInSocketID, where, PupDefs.SecondsToTocks[1]];
    pool ← Buffer.MakePool[send: buffersInFlight, receive: 2*buffersInFlight];
    me ← mySoc.getLocalAddress[];
    PrintPupAddress[@me];
    WriteString[" => "L];
    IF routing.hop # 0 THEN
      BEGIN
      WriteOctal[routing.context.pupNetNumber];
      WriteChar['#];
      WriteOctal[routing.route];
      WriteChar['#];
      WriteString[" => "L];
      END;
    PrintPupAddress[@where];
    NewLine[];
    Process.SetPriority[Process.priorityBackground];
    Process.Detach[FORK Watch[]];
    snarfer ← FORK Snarf[];
    UNTIL pleaseStop DO
      b: PupBuffer ← Buffer.GetBuffer[pup, pool, send, fullBuffer];
      b.pup.pupID ← LOOPHOLE[sent];
      b.pup.pupType ← echoMe;
      Inline.LongCOPY[to: @b.pup.pupBytes, from: @packet, nwords: (bytesPerBuffer+1)/2];
      SetPupContentsBytes[b, bytesPerBuffer];
      mySoc.put[b];
      sent ← sent + 1;
      IF (sent-wait) >= buffersInFlight THEN Wait[];
      IF (sent MOD 100) = 0 THEN {
        WriteChar['s];
	Process.Pause[2]; };
      ENDLOOP;
    NewLine[];
    WriteString["Sent "L];
    WriteLongNumber[sent];
    WriteString[", Echoed "L];
    WriteLongNumber[echo];
    IF sent # 0 THEN {
      WriteString[" ("L];
      WriteLongNumber[(echo*100)/sent];
      WriteString["%)"L]; };
    IF full # 0 THEN
      BEGIN
      WriteString[", BufferFull "L];
      WriteLongNumber[full];
      END;
    IF other # 0 THEN
      BEGIN
      WriteString[", Other "L];
      WriteLongNumber[other];
      END;
    WriteLine["."L];
    JOIN snarfer;
    Buffer.DestroyPool[pool];
    PupSocketDestroy[mySoc];
    Process.SetPriority[priority];
    END;

  PupPhoneSender: PROCEDURE [network: Driver.Network] =
    BEGIN OPEN PupDefs, PupTypes;
    bytesPerBuffer: CARDINAL;
    where, me: PupTypes.PupAddress;
    mySoc: PupSocket;
    sent, wait, echo, full, other: LONG CARDINAL ← 0;
    pleaseStop: BOOLEAN ← FALSE;
    snarfer: PROCESS;
    pup: PupRouterDefs.NetworkContext;
    stats: PhoneNetFriends.PhoneNetInfo;
    seconds: CARDINAL;
    pool: Buffer.AccessHandle;
    priority: Process.Priority = Process.GetPriority[];
    pause: CONDITION;
    Kick: ENTRY PROCEDURE = { NOTIFY pause; };
    Wait: ENTRY PROCEDURE = { WAIT pause; };
    Watch: PROCEDURE [network: Driver.Network] = 
      BEGIN
      PupPhoneSender[network];
      pleaseStop ← TRUE;
      Kick[];
      END;
    Snarf: PROCEDURE = 
      BEGIN
      UNTIL pleaseStop DO
        b: PupBuffer ← mySoc.get[];
	IF b = NIL THEN { wait ← sent; Kick[]; LOOP; };
        IF b.pup.pupType = iAmEcho THEN
	  BEGIN
	  wait ← LOOPHOLE[b.pup.pupID];
	  echo ← echo + 1;
	  Kick[];
	  IF (echo MOD 100) = 0 THEN WriteChar['r];
	  END
        ELSE
          BEGIN
          IF b.pup.pupType = error AND b.pup.errorCode # resourceLimitsPupErrorCode THEN
	    BEGIN
	    other ← other + 1;
	    PrintErrorPup[b];
	    END
          ELSE full ← full + 1;
          END;
	Buffer.ReturnBuffer[b];
        ENDLOOP;
      Kick[];
      END;
    DO
      IF network = NIL THEN {
        -- No more phone lines. Wait for typein.
        [] ← ReadChar[ ! ABORTED => CONTINUE];
        RETURN; };
      IF network.device = phonenet THEN {
        pup ← Protocol1.GetContext[network, pup];
	IF pup.pupNetNumber # 0 THEN EXIT; };
      network ← network.next;
      ENDLOOP;
    stats ← PhoneNetFriends.StatsPtrToStats[network.stats];
    SELECT stats.speed FROM -- Packet makes 4 trips: out, back, echo out, echo back
      0 => seconds ← 2; -- Argh: Kluger's phone driver. Guess!
      1 => seconds ← 20; -- Yetch. Where did this come from?
      2, 3 => seconds ← 12; -- 2400, dial up (2 seconds per packet)
      4, 5, 6 => seconds ← 8;
      7, 8 => seconds ← 4;
      9 => seconds ← 2; -- 9600 is 1/2 second per packet
      ENDCASE => seconds ← 1;
    where ← [[pup.pupNetNumber], [pup.pupHostNumber], PupTypes.echoSoc];
    mySoc ← PupSocketMake[fillInSocketID, where, PupDefs.SecondsToTocks[seconds]];
    pool ← Buffer.MakePool[send: buffersInFlight, receive: 2*buffersInFlight];
    me ← mySoc.getLocalAddress[];
    WriteString["Line "L];
    OthelloDefs.WriteLongNumber[stats.lineNumber];
    WriteString[": "L];
    PrintPupAddress[@me];
    WriteString[" to "L];
    PrintPupAddress[@where];
    NewLine[];
    Process.SetPriority[Process.priorityBackground];
    Process.Detach[FORK Watch[network.next]];
    snarfer ← FORK Snarf[];
    bytesPerBuffer ← (2*MIN[DataWordsPerPupBuffer[], newMaxDataWordsPerGatewayPup]) - stats.lineNumber;
    UNTIL pleaseStop DO
      b: PupBuffer ← Buffer.GetBuffer[pup, pool, send, fullBuffer];
      b.pup.pupID ← LOOPHOLE[sent];
      b.pup.pupType ← echoMe;
      Inline.LongCOPY[to: @b.pup.pupBytes, from: @packet, nwords: (bytesPerBuffer+1)/2];
      SetPupContentsBytes[b, bytesPerBuffer];
      mySoc.put[b];
      sent ← sent + 1;
      IF (sent-wait) >= buffersInFlight THEN Wait[];
      IF (sent MOD 100) = 0 THEN {
        WriteChar['s];
	Process.Pause[2]; };
      ENDLOOP;
    NewLine[];
    WriteString["Line "L];
    OthelloDefs.WriteLongNumber[stats.lineNumber];
    WriteString[": "L];
    WriteString["Sent "L];
    WriteLongNumber[sent];
    WriteString[", Echoed "L];
    WriteLongNumber[echo];
    IF sent # 0 THEN {
      WriteString[" ("L];
      WriteLongNumber[(echo*100)/sent];
      WriteString["%)"L]; };
    IF full # 0 THEN
      BEGIN
      WriteString[", BufferFull "L];
      WriteLongNumber[full];
      END;
    IF other # 0 THEN
      BEGIN
      WriteString[", Other "L];
      WriteLongNumber[other];
      END;
    WriteLine["."L];
    JOIN snarfer;
    Buffer.DestroyPool[pool];
    PupSocketDestroy[mySoc];
    Process.SetPriority[priority];
    END;

  PupBlaster: PROCEDURE =
    BEGIN OPEN PupDefs, PupTypes;
    bytesPerBuffer: CARDINAL;
    me, where: PupAddress ← [, , echoSoc];
    mySoc: PupSocket;
    pool: Buffer.AccessHandle;
    sent, echo, full, other: LONG CARDINAL ← 0;
    routing: PupRouterDefs.RoutingTableEntry;
    pleaseStop: BOOLEAN ← FALSE;
    snarfer: PROCESS;
    Watch: PROCEDURE = 
      BEGIN
      [] ← ReadChar[ ! ABORTED => CONTINUE];
      pleaseStop ← TRUE;
      END;
    Snarf: PROCEDURE = 
      BEGIN
      UNTIL pleaseStop DO
        b: PupBuffer ← mySoc.get[];
	IF b = NIL THEN LOOP;
        IF b.pup.pupType = iAmEcho THEN
	  BEGIN
	  echo ← echo + 1;
	  IF (echo MOD 100) = 0 THEN WriteChar['r];
	  END
        ELSE
          BEGIN
          IF b.pup.pupType = error AND b.pup.errorCode # resourceLimitsPupErrorCode THEN
	    BEGIN
	    other ← other + 1;
	    PrintErrorPup[b];
	    END
          ELSE full ← full + 1;
          END;
	Buffer.ReturnBuffer[b];
        ENDLOOP;
      END;
    GetName["Blast to: "L, @echoName];
    GetPupAddress[
      @where, echoName ! PupNameTrouble => AbortingCommand[e]];
    routing ← @PupRouterDefs.GetRoutingTable[][where.net];
    IF routing=NIL OR routing.context = NIL THEN AbortingCommand["Can't reach that network"L];
    mySoc ← PupSocketMake[fillInSocketID, where, PupDefs.SecondsToTocks[1]];
    pool ← Buffer.MakePool[send: 3, receive: 5];
    me ← mySoc.getLocalAddress[];
    PrintPupAddress[@me];
    WriteString[" => "L];
    IF routing.hop # 0 THEN
      BEGIN
      WriteOctal[routing.context.pupNetNumber];
      WriteChar['#];
      WriteOctal[routing.route];
      WriteChar['#];
      WriteString[" => "L];
      END;
    PrintPupAddress[@where];
    NewLine[];
    Process.Detach[FORK Watch[]];
    snarfer ← FORK Snarf[];
    bytesPerBuffer ← 100;
    UNTIL pleaseStop DO
      b: PupBuffer;
      FOR i: CARDINAL IN [0..3) DO
        b ← Buffer.GetBuffer[pup, pool, send, fullBuffer];
        b.pup.pupID.a ← b.pup.pupID.b ← i;
        b.pup.pupType ← echoMe;
        Inline.LongCOPY[to: @b.pup.pupBytes, from: @packet, nwords: (bytesPerBuffer+1)/2];
        SetPupContentsBytes[b, bytesPerBuffer];
        mySoc.put[b];
        sent ← sent + 1;
        IF (sent MOD 100) = 0 THEN BEGIN WriteChar['s]; Process.Pause[1]; END;
        ENDLOOP;
      Process.Yield[];  -- be sure we don't hog machine
      ENDLOOP;
    NewLine[];
    WriteString["Sent: "L];
    WriteLongNumber[sent];
    WriteString[", Echoed: "L];
    WriteLongNumber[echo];
    IF sent # 0 THEN {
      WriteString[" ("L];
      WriteLongNumber[(echo*100)/sent];
      WriteString["%)"L]; };
    IF full # 0 THEN
      BEGIN
      WriteString[", BufferFull: "L];
      WriteLongNumber[full];
      END;
    IF other # 0 THEN
      BEGIN
      WriteString[", Other: "L];
      WriteLongNumber[other];
      END;
    WriteLine["."L];
    JOIN snarfer;
    Buffer.DestroyPool[pool];
    PupSocketDestroy[mySoc];
    END;

  PrintLocalPupRoutingTable: PROCEDURE =
    BEGIN
    pupRt: LONG DESCRIPTOR FOR ARRAY OF PupRouterDefs.RoutingTableObject;
    k: CARDINAL ← 0;
    WriteLine["|  Net   Via Hops |  Net   Via Hops |  Net   Via Hops |  Net   Via Hops |"L];
    WriteLine["|-----------------|-----------------|-----------------|-----------------|"L];
    pupRt ← PupRouterDefs.GetRoutingTable[];
    FOR i: CARDINAL IN [0..LENGTH[pupRt]) DO
      D3: PROC [n: CARDINAL] = INLINE {WriteFixedWidthNumber[n, 3, 10]};
      O4: PROC [n: CARDINAL] = INLINE {WriteFixedWidthNumber[n, 4, 8]};
      O3Z: PROC [n: CARDINAL] = {
        n ← n MOD 1000B;
	THROUGH [0..3) DO WriteChar['0 + (n/100B) MOD 10B]; n ← n*10B ENDLOOP};
      rte: PupRouterDefs.RoutingTableEntry = @pupRt[i];
      context: PupRouterDefs.NetworkContext = rte.context;
      OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
      IF context = NIL THEN LOOP;
      IF k = 0 THEN WriteChar['|];
      O4[i];
      O4[context.pupNetNumber];
      WriteChar['#];
      O3Z[IF rte.hop # 0 THEN rte.route ELSE context.pupHostNumber];
      WriteChar['#];
      D3[rte.hop];
      WriteString[" |"L];
      IF (k ← k + 1) = 4 THEN BEGIN NewLine[]; k ← 0; Process.Yield[]; END;
      ENDLOOP;
    IF k # 0 THEN NewLine[];
    END;

  PrintPupAddress: PROC [a: POINTER TO PupTypes.PupAddress] = {
    buffer: STRING ← [50];
    PupDefs.AppendPupAddress[buffer, a↑]; WriteString[buffer]};

  echoName: LONG STRING ← NIL;
  Commands: PROC [index: CARDINAL] =
    BEGIN
    SELECT index FROM
      0 => {
        MyNameIs[myNameIs: "Pup Blaster"L, myHelpIs: "Send lots of echo Pups (no wait)"L];
	[] ← PupDefs.PupPackageMake[];
	PupBlaster[! UNWIND => PupDefs.PupPackageDestroy[]]};
      1 => {
        MyNameIs[myNameIs: "Pup Echo User"L, myHelpIs: "Pup echo user (one at a time)"L];
	[] ← PupDefs.PupPackageMake[];
	EchoUser[! UNWIND => PupDefs.PupPackageDestroy[]]};
      2 => {
        MyNameIs[myNameIs: "Pup PhoneLine Sender"L, myHelpIs: "Send Echo Pups on all Phone lines"L];
	[] ← PupDefs.PupPackageMake[];
	PupPhoneSender[Driver.GetDeviceChain[] ! UNWIND => PupDefs.PupPackageDestroy[]]};
      3 => {
        MyNameIs[myNameIs: "Pup Sender"L, myHelpIs: "Send echo Pups (3 in flight at once)"L];
	[] ← PupDefs.PupPackageMake[];
	PupSender[! UNWIND => PupDefs.PupPackageDestroy[]]};
      4 => {
        MyNameIs[
	  myNameIs: "Pup Routing Tables"L,
	  myHelpIs: "Show pup network routing tables"L];
	[] ← PupDefs.PupPackageMake[];
	PrintLocalPupRoutingTable[! UNWIND => PupDefs.PupPackageDestroy[]]};
      ENDCASE => IndexTooLarge;
    PupDefs.PupPackageDestroy[];
    END;
  
  newMaxBytesPerPacket: CARDINAL = 1478;
  newMaxDataWordsPerGatewayPup: CARDINAL = newMaxBytesPerPacket/2;
    
  packet: PACKED ARRAY [0..newMaxBytesPerPacket) OF [0..256);
   
  commandProcessor: CommandProcessor ← [Commands];
  RegisterCommandProc[@commandProcessor];
  
  FOR i: CARDINAL IN [0..newMaxBytesPerPacket) DO
    packet[i] ← i;
    ENDLOOP;
  END.....