-- File: GateInfo.mesa,  Last Edit: HGM  March 17, 1981  6:18 PM

DIRECTORY
  Cursor USING [Defined, GetInfo, Set, Type],
  Menu USING [Handle, ItemObject, MCRType, Create, Instantiate],
  Process USING [Yield],
  Put USING [
    CR, Char, Date, Decimal, Line, LongDecimal, LongNumber, Number, Text],
  TajoMisc USING [Quit],
  Time USING [Current],
  UserInput USING [GetDefaultWindow, userAbort, WaitForConfirmation],
  Window USING [Handle],

  George USING [CountFreeDiskPages],
  StatsDefs USING [
    StatCounterIndex, StatGetCounter, StatPrintCurrent, StatSince, StatUpdate],
  BootServerDefs USING [
    statFileSent, statFileSentSlow, statBootNew, statMicrocodeBooted],
  NameServerDefs USING [
    GetDirectoryVersion, statName, statAddress, statXlation, statWhoAmI, statSend],
  TimeServerDefs USING [statAlto, statPilot, statPilotOld],

  AddressTranslation USING [AppendNetworkNumber, AppendSystemElement],
  Router USING [EnumerateRoutingTable, RoutingTableEntry],
  SpecialSystem USING [
    HostNumber, GetProcessorID, NetworkAddress, NetworkNumber, ProcessorID],
  System USING [],

  BufferDefs USING [BuffersLeft],
  DriverDefs USING [Network, GetDeviceChain],
  ForwarderDefs USING [GetPointerToPupGateStats, statGateInfoReplies],
  GateDefs USING [GetVersionText, typescript],
  PupRouterDefs USING [RoutingTableEntry, EnumerateRoutingTable];

GateInfo: PROGRAM
  IMPORTS
    Cursor, Menu, Process, Put, TajoMisc, Time, UserInput, George, StatsDefs,
    BootServerDefs, NameServerDefs, TimeServerDefs, BufferDefs, DriverDefs,
    ForwarderDefs, GateDefs, PupRouterDefs,
    AddressTranslation, Router, SpecialSystem
  EXPORTS System =
  BEGIN

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

  gate: CARDINAL = 0;
  pupRoute: CARDINAL = 1;
  oiscpRoute: CARDINAL = 2;
  recent: CARDINAL = 3;
  total: CARDINAL = 4;
  quit: CARDINAL = 5;

  DoInfo: Menu.MCRType =
    BEGIN
    SELECT index FROM
      gate => PrintGateStats[NIL];
      pupRoute => PrintPupRoutingTable[NIL];
      oiscpRoute => PrintOiscpRoutingTable[NIL];
      recent => StatsDefs.StatSince[NIL];
      total => StatsDefs.StatPrintCurrent[NIL];
      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: STRING = GateDefs.GetVersionText[];
  firstNetwork: DriverDefs.Network = DriverDefs.GetDeviceChain[];
  packets: POINTER TO ARRAY [0..0) OF LONG CARDINAL;
  bytes: POINTER TO ARRAY [0..0) OF LONG CARDINAL;
  nets: CARDINAL;

  PrintGateStats: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    PrintPacketsForwarded: PROCEDURE =
      BEGIN
      Line["Packets forwarded:"L];
      Text["       Discard"L];
      FOR to: DriverDefs.Network ← firstNetwork, to.next UNTIL to = NIL DO
	Text["      "L]; O4[to.netNumber.b]; ENDLOOP;
      CR[];
      FOR from: DriverDefs.Network ← firstNetwork, from.next UNTIL from = NIL DO
	O4[from.netNumber.b];
	Text[IF from.alive THEN " "L ELSE "*"L];
	LD10Dash[packets[from.index + 0*nets]];
	FOR to: DriverDefs.Network ← firstNetwork, to.next UNTIL to = NIL DO
	  LD10Dash[packets[from.index + to.index*nets]]; ENDLOOP;
	CR[];
	ENDLOOP;
      END;
    PrintBytesForwarded: PROCEDURE =
      BEGIN
      Line["KBytes forwarded:"L];
      Text["       Discard"L];
      FOR to: DriverDefs.Network ← firstNetwork, to.next UNTIL to = NIL DO
	Text["      "L]; O4[to.netNumber.b]; ENDLOOP;
      CR[];
      FOR from: DriverDefs.Network ← firstNetwork, from.next UNTIL from = NIL DO
	O4[from.netNumber.b];
	Text[IF from.alive THEN " "L ELSE "*"L];
	LD10Dash[bytes[from.index + 0*nets]/1000];
	FOR to: DriverDefs.Network ← firstNetwork, to.next UNTIL to = NIL DO
	  LD10Dash[bytes[from.index + to.index*nets]/1000]; ENDLOOP;
	CR[];
	ENDLOOP;
      END;
    PrintMaybe: PROCEDURE [s: STRING, x: StatsDefs.StatCounterIndex] =
      BEGIN
      n: LONG CARDINAL = StatsDefs.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.Decimal[wh, n]; END;
    LD: PROCEDURE [n: LONG CARDINAL] = BEGIN Put.LongDecimal[wh, n]; END;
    LDS: PROCEDURE [x: StatsDefs.StatCounterIndex] =
      BEGIN LD[StatsDefs.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 ← StatsDefs.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: STRING] = BEGIN Put.Line[wh, s]; END;
    Text: PROCEDURE [s: STRING] = BEGIN Put.Text[wh, s]; END;
    Char: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[wh, c]; END;
    CR: PROCEDURE = BEGIN Put.CR[wh]; END;
    StatsDefs.StatUpdate[]; -- be sure time is up to date
    PutHeader[wh, version, FALSE];
    Text[" up "L];
    PrintUpTime[];
    CR[];
    Text["Echo: "L];
    LDS[pupsEchoed];
    Text[", Route: "L];
    LDS[ForwarderDefs.statGateInfoReplies];
    Text[", Boot: "L];
    LDS[BootServerDefs.statFileSent];
    Text[", MicroBoot: "L];
    LDS[BootServerDefs.statMicrocodeBooted];
    CR[];
    Text["Name=>Address: "L];
    LDS[NameServerDefs.statName];
    Text[", Address=>Name: "L];
    LDS[NameServerDefs.statAddress];
    Text[", 48=>8: "L];
    LDS[NameServerDefs.statXlation];
    Text[", Old 48=>8: "L];
    LDS[NameServerDefs.statWhoAmI];
    CR[];
    Text["Alto Time: "L];
    LDS[TimeServerDefs.statAlto];
    Text[", Pilot Time: "L];
    LDS[TimeServerDefs.statPilot];
    Text[", Old Pilot Time: "L];
    LDS[TimeServerDefs.statPilotOld];
    CR[];
    DoSomeYields[];
    Text["Directory version: "L];
    D[NameServerDefs.GetDirectoryVersion[]];
    Text[", FreePBIs: "L];
    D[BufferDefs.BuffersLeft[] + 2];
    Text[", Free Disk Pages: "L];
    D[George.CountFreeDiskPages[]];
    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;
    PrintOne: PROCEDURE [rte: PupRouterDefs.RoutingTableEntry] =
      BEGIN
      network: DriverDefs.Network ← rte.network;
      IF network = NIL THEN RETURN;
      nets ← nets + 1;
      IF UserInput.userAbort THEN RETURN;
      IF k = 0 THEN Put.Char[wh, '|];
      O4[rte.net];
      O4[network.netNumber.b];
      Put.Char[wh, '#];
      IF rte.hop # 0 THEN O3Z[rte.route] ELSE O3Z[network.hostNumber];
      Put.Char[wh, '#];
      O4[rte.hop];
      Put.Text[wh, "  |"L];
      IF (k ← k + 1) = 3 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 |"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;

  NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;
  PrintOiscpRoutingTable: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    O: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, FALSE, FALSE, 0]]; END;
    O4: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 4]]; END;
    PrintOne: PROCEDURE [rte: Router.RoutingTableEntry] =
      BEGIN
      network: DriverDefs.Network ← rte.network;
      IF network = NIL THEN duds ← duds + 1
      ELSE nets ← nets + 1;
      IF UserInput.userAbort THEN RETURN;
      PutNetNumber[wh, rte.destNetwork];
      Put.Char[wh,' ];
      IF rte.delay # 1 THEN PutNetAndHost[wh,network.netNumber, rte.route]
      ELSE PutNetAndHost[wh, network.netNumber, me];
      O4[rte.delay];
      O4[rte.timeUnits];
      IF rte.network=NIL THEN Put.Char[wh,'~] ELSE Put.Char[wh,' ];
      Put.CR[wh];
      DoSomeYields[];
      END;
    nets, duds: CARDINAL ← 0;
    me: SpecialSystem.ProcessorID = SpecialSystem.GetProcessorID[];
    PutHeader[wh,"  Local OISCP Routing Table."L];
    Put.Line[wh,"  Net             Via           Dly Tim"L];
    Router.EnumerateRoutingTable[PrintOne];
    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: 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;

  PutNetNumber: PROCEDURE [wh: Window.Handle, net: SpecialSystem.NetworkNumber] =
    BEGIN
    temp: STRING = [50];
    AddressTranslation.AppendNetworkNumber[temp, net];
    temp.length ← temp.length - 1;  -- flush #
    THROUGH [temp.length..5) DO Put.Char[wh, ' ]; ENDLOOP;
    Put.Text[wh, temp];
    END;

  PutNetAndHost: PROCEDURE [
      wh: Window.Handle,
      net: SpecialSystem.NetworkNumber,
      host: SpecialSystem.HostNumber] =
    BEGIN
    temp: STRING = [50];
    AddressTranslation.AppendSystemElement[
      temp, SpecialSystem.NetworkAddress[net,host,[0]]];
    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;

  Quit: PROCEDURE =
    BEGIN IF ~Confirm[] THEN RETURN; Cursor.Set[hourGlass]; TajoMisc.Quit[]; 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];
    END;


  -- Initialization

  [packets, bytes, nets] ← ForwarderDefs.GetPointerToPupGateStats[];
  SetupGateMenu[];
  END.