-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- GateInfo.mesa, HGM,  6-Aug-85 21:11:13

DIRECTORY
  Cursor USING [Defined, GetInfo, Set, Type],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  Format USING [HostNumber, NetworkNumber],
  Menu USING [Handle, ItemObject, MCRType, Create, Instantiate],
  Process USING [Yield],
  Put USING [
    CR, Char, Date, Decimal, Line, LongNumber, Number, Text],
  Runtime USING [UnboundProcedure],
  String USING [AppendChar, AppendString],
  Supervisor USING [EnumerationAborted, NotifyDirectSubsystems],
  System USING [HostNumber, NetworkNumber, nullNetworkNumber, switches],
  TemporaryBooting USING [BootFromPhysicalVolume, BootFromVolume],
  Time USING [Current],
  UserInput USING [
    GetDefaultWindow, UserAbort, WaitForConfirmation, WaitNoButtons],
  Volume USING [GetAttributes, systemID],
  Window USING [Handle],

  BootServerDefs USING [
    statFileSent, statFileSentSlow, statBootNew, statMicrocodeBooted],
  Buffer USING [],
  BufferOps USING [GetStatistics, Statistics],
  Driver USING [Network, GetDeviceChain],
  EchoServer USING [GetCounters],
  ForwarderDefs USING [
    Counters, GetPointerToBadPupStats, GetPointerToPupGateStats, statGateInfoReplies],
  GateDefs USING [GetVersionText, typescript],
  CpuIdle USING [GetSmoothedCpuUtilization],
  Inr USING [ForwardingStats],
  InrFriends USING [DriverDetails, GetRouteInfo],
  NameServerDefs USING [
    GetNewDirectoryVersion, GetOldDirectoryVersion, statName, statAddress, statXlation,
    statSend],
  Protocol1 USING [GetContext],
  PupRouterDefs USING [NetworkContext, RoutingTableEntry, EnumerateRoutingTable],
  PupTimeServer USING [GetAltoTimeRequests],
  Router USING [
    endEnumeration, EnumerateRoutingTable,
    infinity, NoTableEntryForNet, startEnumeration],
  Stats USING [
    StatCounterIndex, StatGetCounter, StatPrintCurrent, StatSince, StatUpdate],
  TimeServerOps USING [GetOldTimeRequests, GetTimeRequests],
  Trouble USING [ForceOutTypescriptFile];

GateInfo: PROGRAM
  IMPORTS
    Cursor, Event, Format, Menu, Process, Put, Runtime,
    String, Supervisor, System, TemporaryBooting, Time, UserInput, Volume,
    BootServerDefs, BufferOps, Driver, Protocol1,
    EchoServer, ForwarderDefs, GateDefs, CpuIdle, Inr, InrFriends, NameServerDefs, PupRouterDefs,
    PupTimeServer, Router, Stats, TimeServerOps, Trouble
  EXPORTS Buffer =
  BEGIN
  
  Network: PUBLIC TYPE = Driver.Network;

  items: ARRAY [0..6] OF Menu.ItemObject ← [
    ["Gate", DoInfo], ["Pup RT", DoInfo], ["NS RT", DoInfo], ["Recent", DoInfo], [
    "Total", DoInfo], ["Restart", DoInfo], ["Quit", DoInfo]];

  gate: CARDINAL = 0;
  pupRoute: CARDINAL = 1;
  nsRoute: CARDINAL = 2;
  recent: CARDINAL = 3;
  total: CARDINAL = 4;
  restart: CARDINAL = 5;
  quit: CARDINAL = 6;

  DoInfo: Menu.MCRType =
    BEGIN
    wh: Window.Handle = GateDefs.typescript;
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN Put.Line[wh, s]; END;
    SELECT index FROM
      gate => PrintGateStats[wh];
      pupRoute => PrintPupRoutingTable[wh];
      nsRoute => PrintNSRoutingTable[wh];
      recent => Stats.StatSince[Push];
      total => Stats.StatPrintCurrent[Push];
      restart => Restart[];
      quit => Quit[];
      ENDCASE => ERROR;
    END;

  SetupGateMenu: PROCEDURE =
    BEGIN
    menu: Menu.Handle ← Menu.Create[DESCRIPTOR[items], "Gate"];
    Menu.Instantiate[menu, UserInput.GetDefaultWindow[]];
    Menu.Instantiate[menu, GateDefs.typescript];
    END;

  version: LONG STRING = GateDefs.GetVersionText[];
  firstNetwork: Driver.Network = Driver.GetDeviceChain[];
  packets: LONG POINTER TO ForwarderDefs.Counters;
  bytes: LONG POINTER TO ForwarderDefs.Counters;
  bad: LONG POINTER TO ForwarderDefs.Counters;
  nets: CARDINAL;

  PrintGateStats: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    PupForwardingStats: PROCEDURE RETURNS [p, b: LONG CARDINAL ← 0] =
      BEGIN
      FOR from: Driver.Network ← firstNetwork, from.next UNTIL from = NIL DO
        FOR to: Driver.Network ← firstNetwork, to.next UNTIL to = NIL DO
          p ← p + packets[from.index + to.index*nets];
          b ← b + bytes[from.index + to.index*nets];
	  ENDLOOP;
        ENDLOOP;
      END;
    PrintPacketsForwarded: PROCEDURE =
      BEGIN
      Line["Pups forwarded:"L];
      Text["       Discard"L];
      FOR to: Driver.Network ← firstNetwork, to.next UNTIL to = NIL DO
        toContext: PupRouterDefs.NetworkContext ← Protocol1.GetContext[to, pup];
        Text["      "L];
	O4[toContext.pupNetNumber];
	ENDLOOP;
      IF bad # NIL THEN
        FOR network: Driver.Network ← firstNetwork, network.next UNTIL network = NIL DO
	IF bad[network.index] # 0 THEN BEGIN Text["       Bad"L]; EXIT; END;
	ENDLOOP;
      CR[];
      FOR from: Driver.Network ← firstNetwork, from.next UNTIL from = NIL DO
        fromContext: PupRouterDefs.NetworkContext ← Protocol1.GetContext[from, pup];
        O4[fromContext.pupNetNumber];
        Text[IF from.alive THEN " "L ELSE "*"L];
        LD10Dash[packets[from.index + 0*nets]];
        FOR to: Driver.Network ← firstNetwork, to.next UNTIL to = NIL DO
          LD10Dash[packets[from.index + to.index*nets]];
	  ENDLOOP;
        IF bad # NIL AND (bad[from.index] # 0) THEN LD10Dash[bad[from.index]];
        CR[];
        ENDLOOP;
      END;
    PrintBytesForwarded: PROCEDURE =
      BEGIN
      Line["KBytes forwarded:"L];
      Text["       Discard"L];
      FOR to: Driver.Network ← firstNetwork, to.next UNTIL to = NIL DO
        toContext: PupRouterDefs.NetworkContext ← Protocol1.GetContext[to, pup];
        Text["      "L];
	O4[toContext.pupNetNumber];
	ENDLOOP;
      CR[];
      FOR from: Driver.Network ← firstNetwork, from.next UNTIL from = NIL DO
        fromContext: PupRouterDefs.NetworkContext ← Protocol1.GetContext[from, pup];
        O4[fromContext.pupNetNumber];
        Text[IF from.alive THEN " "L ELSE "*"L];
        LD10Dash[bytes[from.index + 0*nets]/1000];
        FOR to: Driver.Network ← firstNetwork, to.next UNTIL to = NIL DO
          LD10Dash[bytes[from.index + to.index*nets]/1000];
	  ENDLOOP;
        CR[];
        ENDLOOP;
      END;
    PrintMaybe: PROCEDURE [s: LONG STRING, x: Stats.StatCounterIndex] =
      BEGIN
      n: LONG CARDINAL = Stats.StatGetCounter[x];
      IF n = 0 THEN RETURN;
      Text[s];
      Text[": "L];
      LD[n];
      CR[];
      END;
    O4: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 4]]; END;
    D: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 0]]; END;
    LD: PROCEDURE [n: LONG CARDINAL] = BEGIN Put.LongNumber[wh, n, [10, FALSE, TRUE, 0]]; END;
    LDS: PROCEDURE [x: Stats.StatCounterIndex] =
      BEGIN LD[Stats.StatGetCounter[x]]; END;
    LD10Dash: PROCEDURE [n: LONG CARDINAL] =
      BEGIN
      IF n = 0 THEN Put.Text[wh, "        - "L]
      ELSE Put.LongNumber[wh, n, [10, FALSE, TRUE, 10]];
      END;
    PrintUpTime: PROCEDURE =
      BEGIN
      sec: LONG INTEGER ← Stats.StatGetCounter[statSeconds];
      min: LONG INTEGER;
      hours: LONG INTEGER;
      hours ← sec/3600;
      sec ← sec - hours*3600;
      min ← sec/60;
      sec ← sec - min*60;
      LD[hours];
      Char[':];
      LD[min];
      Char[':];
      LD[sec];
      END;
    Line: PROCEDURE [s: LONG STRING] = BEGIN Put.Line[wh, s]; END;
    Text: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[wh, s]; END;
    Char: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[wh, c]; END;
    CR: PROCEDURE = BEGIN Put.CR[wh]; END;
    Stats.StatUpdate[];  -- be sure time is up to date
    PutHeader[wh, version, FALSE];
    CR[];
    Text["UP = "L];
    PrintUpTime[];
    Text[", CPU = "];
    D[CpuIdle.GetSmoothedCpuUtilization[]];
    Text["%"];
    CR[];
    BEGIN
    packets, bytes: LONG CARDINAL;
    [packets, bytes] ← EchoServer.GetCounters[];
    Text["Echo: "L];
    LD[packets];
    Text[" packets, "L];
    LD[bytes];
    Text[" bytes"L];
    Text[", Time: "L];
    LD[TimeServerOps.GetTimeRequests[]];
    Text[", Old Time: "L];
    LD[TimeServerOps.GetOldTimeRequests[]];
    CR[];
    END;
    BEGIN
    seconds: LONG CARDINAL = Stats.StatGetCounter[statSeconds];
    packets, bytes: LONG CARDINAL;
    [packets, bytes] ← Inr.ForwardingStats[];
    Text["NS: "L];
    LD[packets];
    Text[" packets, "L];
    LD[bytes];
    Text[" bytes"L];
    IF seconds # 0 THEN
      BEGIN
      Text[", "L];
      LD[packets/seconds];
      Text[" packets/second, "L];
      LD[bytes/(seconds/8)];
      Text[" bits/second"L];
      END;
    CR[];
    [packets, bytes] ← PupForwardingStats[];
    Text["Pup: "L];
    LD[packets];
    Text[" packets, "L];
    LD[bytes];
    Text[" bytes"L];
    IF seconds # 0 THEN
      BEGIN
      Text[", "L];
      LD[packets/seconds];
      Text[" packets/second, "L];
      LD[bytes/(seconds/8)];
      Text[" bits/second"L];
      END;
    CR[];
    END;
    Text["PupEcho: "L];
    LDS[pupsEchoed];
    Text[", Route: "L];
    LDS[ForwarderDefs.statGateInfoReplies];
    Text[", Boot: "L];
    LDS[BootServerDefs.statFileSent];
    Text[", MicroBoot: "L];
    LDS[BootServerDefs.statMicrocodeBooted];
    Text[", Alto Time: "L];
    LD[PupTimeServer.GetAltoTimeRequests[]];
    CR[];
    Text["Name=>Address: "L];
    LDS[NameServerDefs.statName];
    Text[", Address=>Name: "L];
    LDS[NameServerDefs.statAddress];
    Text[", 48=>8: "L];
    LDS[NameServerDefs.statXlation];
    CR[];
    DoSomeYields[];
    BEGIN
    version: CARDINAL;
    inTransit: BOOLEAN;
    Text["Directory version: "L];
    [version, inTransit] ← NameServerDefs.GetOldDirectoryVersion[];
    D[version];
    IF inTransit THEN Text["(In Transit)"L];
    [version, inTransit] ← NameServerDefs.GetNewDirectoryVersion[];
    Text["/"L];
    D[version];
    END;
    BEGIN
    info: ARRAY BufferOps.Statistics OF CARDINAL ← BufferOps.GetStatistics[];
    Text[", Buffers: "L];
    D[info[available]];
    Text["/"L];
    D[info[allocated]];
    Text["/"L];
    D[info[requested]];
    END;
    Text[", Free Disk Pages: "L];
    LD[Volume.GetAttributes[Volume.systemID].freePageCount];
    CR[];
    DoSomeYields[];
    PrintMaybe["Pup Network Directories sent"L, NameServerDefs.statSend];
    PrintMaybe["Slow boot files sent"L, BootServerDefs.statFileSentSlow];
    PrintMaybe["New boot files retrieved"L, BootServerDefs.statBootNew];
    CR[];
    DoSomeYields[];
    IF packets # NIL THEN PrintPacketsForwarded[];
    CR[];
    DoSomeYields[];
    IF bytes # NIL THEN PrintBytesForwarded[];
    DoSomeYields[];
    END;

  PrintPupRoutingTable: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    O3Z: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, TRUE, TRUE, 3]]; END;
    O4: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 4]]; END;
    D4: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 4]]; END;
    PrintOne: PROCEDURE [rte: PupRouterDefs.RoutingTableEntry] =
      BEGIN
      context: PupRouterDefs.NetworkContext ← rte.context;
      IF context = NIL THEN RETURN;
      nets ← nets + 1;
      IF UserInput.UserAbort[wh] THEN RETURN;
      IF k = 0 THEN Put.Char[wh, '|];
      O4[rte.net];
      O4[context.pupNetNumber];
      Put.Char[wh, '#];
      IF rte.hop # 0 THEN O3Z[rte.route] ELSE O3Z[context.pupHostNumber];
      Put.Char[wh, '#];
      D4[rte.hop];
      Put.Text[wh, "  |"L];
      IF (k ← k + 1) = 4 THEN BEGIN Put.CR[wh]; k ← 0; DoSomeYields[]; END;
      END;
    k, nets: CARDINAL ← 0;
    PutHeader[wh, "  Local Routing Table."L];
    Put.Line[
      wh, "|  Net   Via   Hops |  Net   Via   Hops |  Net   Via   Hops |  Net   Via   Hops |"L];
    Put.Line[
      wh, "|-------------------|-------------------|-------------------|-------------------|"L];
    PupRouterDefs.EnumerateRoutingTable[PrintOne];
    IF k # 0 THEN Put.CR[wh];
    IF nets > 1 THEN
      BEGIN
      Put.Text[wh, "There are "L];
      Put.Decimal[wh, nets - 1];
      Put.Line[wh, " active networks."L];
      END;
    END;

  PrintNSRoutingTable: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    O: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, FALSE, FALSE, 0]]; END;
    D4: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 4]]; END;
    PrintOne: PROCEDURE [net: System.NetworkNumber, delay: CARDINAL] =
      BEGIN
      hickup: BOOLEAN ← FALSE;
      delay2: CARDINAL;
      details: InrFriends.DriverDetails;
      [delay2, details] ← InrFriends.GetRouteInfo[net !
        Runtime.UnboundProcedure, Router.NoTableEntryForNet =>
	  BEGIN hickup ← TRUE; CONTINUE; END];
      IF hickup THEN duds ← duds + 1 ELSE nets ← nets + 1;
      IF UserInput.UserAbort[wh] THEN RETURN;
      IF k = 0 THEN Put.Char[wh, '|];
      PutProductNetNumber[wh, net];
      PutNetNumber[wh, net];
      Put.Char[wh, ' ];
      SELECT TRUE FROM
        hickup => Put.Text[wh, "          ?????          "];
        ENDCASE => PutNetAndHost[wh, details.driverNetwork, details.via.host];
      D4[delay];
      IF delay # delay2 THEN Put.Char[wh,'?] ELSE Put.Char[wh,' ];
      Put.Text[wh, "|"L];
      IF (k ← k + 1) = 2 THEN BEGIN Put.CR[wh]; k ← 0; END;
      DoSomeYields[];
      END;
    k, nets, duds: CARDINAL ← 0;
    PutHeader[wh, "  Local NS Routing Table."L];
    Put.Line[
      wh, "|       Net                 Via         Dly |       Net                 Via         Dly |"L];
    Put.Line[
      wh, "|-------------------------------------------|-------------------------------------------|"L];
    BEGIN
    net: System.NetworkNumber ← Router.startEnumeration;
    PrintOne[System.nullNetworkNumber, 0];  -- Enumeration skips net 0
    FOR delay: CARDINAL IN [0..Router.infinity) DO
      net: System.NetworkNumber ← Router.startEnumeration;
      DO
        net ← Router.EnumerateRoutingTable[net, delay];
        IF net = Router.endEnumeration THEN EXIT;
        PrintOne[net, delay];
        ENDLOOP;
      ENDLOOP;
    END;
    IF k # 0 THEN Put.CR[wh];
    IF nets > 1 THEN
      BEGIN
      Put.Text[wh, "There are "L];
      Put.Decimal[wh, nets - 1];
      Put.Line[wh, " active networks."L];
      END;
    IF duds > 0 THEN
      BEGIN
      Put.Text[wh, "There are "L];
      Put.Decimal[wh, duds];
      Put.Line[wh, " dead slots."L];
      END;
    END;

  PutHeader: PROCEDURE [wh: Window.Handle, s: LONG STRING, cr: BOOLEAN ← TRUE] =
    BEGIN
    Put.CR[wh];
    Put.Date[wh, Time.Current[], dateTime];
    Put.Text[wh, "  "L];
    Put.Text[wh, s];
    IF cr THEN Put.CR[wh];
    END;

  PutProductNetNumber: PROCEDURE [wh: Window.Handle, net: System.NetworkNumber] =
    BEGIN
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN
      THROUGH [s.length..6) DO Put.Char[wh, ' ]; ENDLOOP;
      Put.Text[wh, s];
      END;
    Format.NetworkNumber[Push, net, productSoftware];
    END;

  PutNetNumber: PROCEDURE [wh: Window.Handle, net: System.NetworkNumber] =
    BEGIN
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN
      THROUGH [s.length..6) DO Put.Char[wh, ' ]; ENDLOOP;
      Put.Text[wh, s];
      END;
    Format.NetworkNumber[Push, net, octal];
    END;

  PutNetAndHost: PROCEDURE [
    wh: Window.Handle, net: System.NetworkNumber, host: System.HostNumber] =
    BEGIN
    temp: STRING = [50];
    Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN String.AppendString[temp, s]; END;
    Format.NetworkNumber[Append, net, productSoftware];
    String.AppendChar[temp, '.];
    Format.HostNumber[Append, host, productSoftware];
    String.AppendChar[temp, '.];
    THROUGH [temp.length..25) DO Put.Char[wh, ' ]; ENDLOOP;
    Put.Text[wh, temp];
    END;

  DoSomeYields: PROCEDURE =
    BEGIN THROUGH [0..100) DO Process.Yield[]; ENDLOOP; END;

  Restart: PROCEDURE =
    BEGIN
    oldType: Cursor.Type = Cursor.GetInfo[].type;
    IF ~Confirm[] THEN RETURN;
    Cursor.Set[hourGlass];
    BEGIN
    ENABLE Supervisor.EnumerationAborted => CONTINUE;
    Supervisor.NotifyDirectSubsystems[
      event: [EventTypes.aboutToBoot], which: clients, subsystem: Event.aboutToSwap];
    Trouble.ForceOutTypescriptFile[];
    TemporaryBooting.BootFromVolume[Volume.systemID, System.switches];
    END;
    IF oldType IN Cursor.Defined THEN Cursor.Set[oldType];
    END;

  Quit: PROCEDURE =
    BEGIN
    oldType: Cursor.Type = Cursor.GetInfo[].type;
    IF ~Confirm[] THEN RETURN;
    Cursor.Set[hourGlass];
    BEGIN
    ENABLE Supervisor.EnumerationAborted => CONTINUE;
    Supervisor.NotifyDirectSubsystems[
      event: [EventTypes.aboutToBootPhysicalVolume],
      which: clients, subsystem: Event.aboutToSwap];
    Trouble.ForceOutTypescriptFile[];
    TemporaryBooting.BootFromPhysicalVolume[Volume.systemID, System.switches];
    END;
    IF oldType IN Cursor.Defined THEN Cursor.Set[oldType];
    END;

  Confirm: PROCEDURE RETURNS [okay: BOOLEAN] =
    BEGIN
    oldType: Cursor.Type = Cursor.GetInfo[].type;
    Cursor.Set[mouseRed];
    okay ← UserInput.WaitForConfirmation[].okay;
    IF oldType IN Cursor.Defined THEN Cursor.Set[oldType];
    UserInput.WaitNoButtons[];
    END;


  -- Initialization
  [packets, bytes, nets] ← ForwarderDefs.GetPointerToPupGateStats[];
  bad ← ForwarderDefs.GetPointerToBadPupStats[];
  SetupGateMenu[];
  END.