-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- DeviceInfo.mesa, HGM, 10-Jun-85 22:11:05

DIRECTORY
  Format USING [],  -- needed by Put.Date and friends
  Heap USING [systemZone],
  Menu USING [ItemObject, MCRType, Create, Instantiate],
  Process USING [Yield],
  Put USING [
    CR, Char, Date, HostNumber, Line, LongDecimal, LongNumber, NetworkNumber, Number, Text],
  String USING [AppendChar, AppendNumber, AppendString],
  System USING [HostNumber, localHostNumber, NetworkNumber, nullHostNumber],
  Time USING [Current],
  UserInput USING [GetDefaultWindow],
  Window USING [Handle],

  EthernetDriverFriends USING [EtherStatsInfo],
  PhoneNetFriends USING [PhoneNetInfo],
  PhoneNetExtras USING [
    leafBytesSend, leafDupsFiltered, leafPktsSend,
    nsBytesSend, nsCongestion, nsDupsFiltered, nsTooGreedy,
    pupBytesSend, pupCongestion, pupDupsFiltered, pupTooGreedy],
  Sla USING [
    DriverStatistics, LineInfo, longHop, noPartner, RoutingTableEntry, SlaHost],
  Driver USING [GetDeviceChain, Network];

DeviceInfo: PROGRAM
  IMPORTS Heap, Menu, Process, Put, String, System, Time, UserInput, Driver =
  BEGIN
  
  z: UNCOUNTED ZONE = Heap.systemZone;

  DoMenu: Menu.MCRType =
    BEGIN
    firstNetwork: Driver.Network ← Driver.GetDeviceChain[];
    device: CARDINAL ← 0;
    FOR network: Driver.Network ← firstNetwork, network.next UNTIL network =
      NIL DO
      IF index = device THEN
        BEGIN
        SELECT network.device FROM
          ethernet, ethernetOne => ShowEthernet[NIL, network];
          sla => ShowSla[NIL, network];
          phonenet => ShowPhoneNet[NIL, network];
          ENDCASE => ERROR;
        RETURN;
        END;
      device ← device + 1;
      ENDLOOP;
    END;

  ShowEthernet: PROCEDURE [wh: Window.Handle, network: Driver.Network] =
    BEGIN
    stats: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.statsLevel0;
    PrintHeader[wh, "Ethernet"L, FALSE];
    IF network.device = ethernetOne THEN Put.Text[wh, "One"L];
    Put.Text[wh, " Statistics for "L];
    Put.Number[wh, network.pupNetNumber, [8, FALSE, TRUE, 0]];
    Put.Text[wh, "#"L];
    Put.Number[wh, network.pupHostNumber, [8, FALSE, TRUE, 0]];
    Put.Text[wh, "#, NS net "L];
    PutNetNumbers[wh, network.netNumber];
    Put.CR[wh];
    IF stats = NIL THEN RETURN;
    Put.Text[wh, "Rcv: pkts "L];
    Put.LongDecimal[wh, stats.packetsRecv];
    Put.Text[wh, ", words "L];
    Put.LongDecimal[wh, stats.wordsRecv];
    Put.Text[wh, ", bad "L];
    Put.LongDecimal[wh, stats.badRecvStatus];
    Put.Text[wh, ", missed "L];
    Put.LongDecimal[wh, stats.packetsMissed];
    Put.CR[wh];
    IF stats.badRecvStatus # 0 OR stats.okButDribble # 0 THEN
      BEGIN
      Put.Text[wh, "  crc "L];
      Put.LongDecimal[wh, stats.badCrc];
      Put.Text[wh, ", bad alignment but ok crc "L];
      Put.LongDecimal[wh, stats.badAlignmentButOkCrc];
      Put.Text[wh, ", crc and bad alignment "L];
      Put.LongDecimal[wh, stats.crcAndBadAlignment];
      Put.CR[wh];
      Put.Text[wh, "  ok but dribble "L];
      Put.LongDecimal[wh, stats.okButDribble];
      Put.Text[wh, ", too long "L];
      Put.LongDecimal[wh, stats.packetTooLong];
      Put.Text[wh, ", overrun "L];
      Put.LongDecimal[wh, stats.overrun];
      Put.Text[wh, ", idle "L];
      Put.LongDecimal[wh, stats.idleInput];
      Put.CR[wh];
      END;
    Put.Text[wh, "Xmit: pkts "L];
    Put.LongDecimal[wh, stats.packetsSent];
    Put.Text[wh, ", words "L];
    Put.LongDecimal[wh, stats.wordsSent];
    Put.Text[wh, ", bad "L];
    Put.LongDecimal[wh, stats.badSendStatus];
    Put.CR[wh];
    IF stats.badSendStatus # 0 OR stats.tooManyCollisions # 0 THEN
      BEGIN
      Put.Text[wh, "  underrun "L];
      Put.LongDecimal[wh, stats.underrun];
      Put.Text[wh, ", stuck "L];
      Put.LongDecimal[wh, stats.stuckOutput];
      Put.Text[wh, ", too many collisions "L];
      Put.LongDecimal[wh, stats.tooManyCollisions];
      Put.CR[wh];
      END;
    Put.Text[wh, "Lds:"L];
    FOR i: CARDINAL IN [0..16) DO
      Put.Char[wh, ' ]; Put.LongDecimal[wh, stats.loadTable[i]]; ENDLOOP;
    Put.CR[wh];
    END;

  ShowSla: PROCEDURE [wh: Window.Handle, network: Driver.Network] =
    BEGIN OPEN Sla;
    stats: LONG POINTER TO DriverStatistics ← network.statsLevel0;
    lineInfo: LONG POINTER TO ARRAY [0..0) OF LineInfo ← stats.info;
    routingTable: LONG POINTER TO ARRAY SlaHost OF RoutingTableEntry ←
      stats.routingTable;
    PrintHeader[wh, "SLA Statistics for "L, FALSE];
    Put.Number[wh, network.pupNetNumber, [8, FALSE, TRUE, 0]];
    Put.Text[wh, "#"L];
    Put.Number[wh, network.pupHostNumber, [8, FALSE, TRUE, 0]];
    Put.Text[wh, "#, NS net "L];
    PutNetNumbers[wh, network.netNumber];
    Put.CR[wh];
    BEGIN
    LD6: PROCEDURE [n: LONG CARDINAL] =
      BEGIN Put.LongNumber[wh, n, [10, FALSE, TRUE, 6]]; END;
    LD8: PROCEDURE [n: LONG CARDINAL] =
      BEGIN Put.LongNumber[wh, n, [10, FALSE, TRUE, 8]]; END;
    LD12: PROCEDURE [n: LONG CARDINAL] =
      BEGIN Put.LongNumber[wh, n, [10, FALSE, TRUE, 12]]; END;
    Put.Line[
      wh,
      "        ---Packets---    --------Bytes-------    -------Errors-------
Ln To    Sent    Recv        Sent        Recv    Ov   CRC  Sync Cntrl  Send  Recv  State"L];
    FOR line: CARDINAL IN [0..stats.lines) DO
      info: LONG POINTER TO LineInfo ← @lineInfo[line];
      Put.Number[wh, line, [8, FALSE, TRUE, 2]];
      IF info.partner = noPartner THEN Put.Text[wh, "  ?"L]
      ELSE Put.Number[wh, info.partner, [8, FALSE, TRUE, 3]];
      LD8[info.packetsSent];
      LD8[info.packetsRecv];
      LD12[info.bytesSent];
      LD12[info.bytesRecv];
      LD6[info.overrun];
      LD6[info.crcErrors];
      LD6[info.syncErrors];
      LD6[info.controlErrors];
      LD6[info.sendErrors];
      LD6[info.recvErrors];
      Put.Text[wh, "  "L];
      SELECT info.state FROM
        down => Put.Line[wh, "Down"L];
        loopedBack => Put.Line[wh, "Looped"L];
        halfUp => Put.Line[wh, "Half Up"L];
        up => Put.Line[wh, "Up"L];
        ENDCASE => Put.Line[wh, "  ??"L];
      IF info.deaths # 0 OR info.stuck # 0 OR info.timeout # 0
        OR info.garbagePackets # 0 THEN
        BEGIN
        Put.Text[wh, "Deaths: "L];
        Put.LongDecimal[wh, info.deaths];
        Put.Text[wh, ", Send Stuck: "L];
        Put.LongDecimal[wh, info.stuck];
        Put.Text[wh, ", Recv Timeout: "L];
        Put.LongDecimal[wh, info.timeout];
        Put.Text[wh, ", GarbagePackets: "L];
        Put.LongDecimal[wh, info.garbagePackets];
        Put.Line[wh, "."L];
        END;
      DoSomeYields[];
      ENDLOOP;
    END;
    BEGIN
    O4: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 4]]; END;
    D5: PROCEDURE [n: CARDINAL] =
      BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 5]]; END;
    k: CARDINAL ← 0;
    Put.Line[wh, "SLA Routing Table:"L];
    FOR host: SlaHost IN SlaHost DO
      rte: LONG POINTER TO RoutingTableEntry = @routingTable[host];
      IF rte.hops = longHop THEN LOOP;
      IF k # 0 THEN Put.Text[wh, "    "L];
      O4[host];
      D5[rte.line];
      D5[rte.hops];
      IF (k ← k + 1) = 4 THEN BEGIN Put.CR[wh]; k ← 0; END;
      ENDLOOP;
    IF k # 0 THEN Put.CR[wh];
    END;
    END;

  ShowPhoneNet: PROCEDURE [wh: Window.Handle, network: Driver.Network] =
    BEGIN
    CrapForINRLoophole: TYPE = MONITORED RECORD [
      clientData: LONG UNSPECIFIED,
      lineNumber: CARDINAL];
    foo: LONG POINTER TO CrapForINRLoophole = network.statsLevel0;
    stats: LONG POINTER TO PhoneNetFriends.PhoneNetInfo ← foo.clientData;
    PrintHeader[wh, "PhoneNet"L, FALSE];
    Put.Text[wh, " Statistics for "L];
    Put.Number[wh, network.pupNetNumber, [8, FALSE, TRUE, 0]];
    Put.Text[wh, "#"L];
    Put.Number[wh, network.pupHostNumber, [8, FALSE, TRUE, 0]];
    Put.Text[wh, "#, NS net "L];
    PutNetNumbers[wh, network.netNumber];
    IF stats = NIL THEN RETURN;
    Put.Text[wh, ", Line "L];
    Put.LongDecimal[wh, stats.lineNumber];
    Put.Text[wh, ", "L];
    Put.LongDecimal[wh, stats.speed];
    Put.Text[wh, "KB"L];
    SELECT TRUE FROM
      stats.remoteHostNumber = System.nullHostNumber => Put.Text[wh, "  Down"L];
      stats.remoteHostNumber = System.localHostNumber => Put.Text[wh, "  Looped"L];
      ENDCASE =>
        BEGIN
        Put.CR[wh];
        Put.Text[wh, "Up to "L];
        PutHostNumbers[wh, stats.remoteHostNumber];
        END;
    Put.CR[wh];
    Put.Text[wh, "Recv: pkts "L];
    Put.LongDecimal[wh, stats.stats[pktsReceived]];
    Put.Text[wh, ", bytes "L];
    Put.LongDecimal[wh, stats.stats[bytesReceived]];
    Put.Text[wh, ", rejected "L];
    Put.LongDecimal[wh, stats.stats[pktsRejected]];
    Put.Text[wh, ", missed "L];
    Put.LongDecimal[wh, stats.stats[rcvErrorNoGet]];
    Put.Text[wh, ", idle "L];
    Put.LongDecimal[wh, stats.stats[tooLongSinceLastReceive]];
    Put.CR[wh];
    Put.Text[wh, "  Bad crc "L];
    Put.LongDecimal[wh, stats.stats[rcvErrorCRC]];
    Put.Text[wh, ", data lost "L];
    Put.LongDecimal[wh, stats.stats[rcvErrorDataLost]];
    Put.Text[wh, ", device error "L];
    Put.LongDecimal[wh, stats.stats[rcvDeviceError]];
    Put.Text[wh, ", timeout "L];
    Put.LongDecimal[wh, stats.stats[rcvErrorFrameTimeout]];
    Put.Text[wh, ", other error "L];
    Put.LongDecimal[wh, stats.stats[rcvErrorUnknown]];
    Put.CR[wh];
    Put.Text[wh, "Send: pkts "L];
    Put.LongDecimal[wh, stats.stats[pktsSent]];
    Put.Text[wh, ", bytes "L];
    Put.LongDecimal[wh, stats.stats[bytesSent]];
    Put.CR[wh];
    Put.Text[wh, "  NS "L];
    Put.LongDecimal[wh, stats.stats[nsSent]];
    Put.Text[wh, " "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.nsBytesSend]];
    Put.Text[wh, ", Pups "L];
    Put.LongDecimal[wh, stats.stats[pupSent]];
    Put.Text[wh, " "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.pupBytesSend]];
    Put.Text[wh, ", Leaf "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.leafPktsSend]];
    Put.Text[wh, " "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.leafBytesSend]];
    Put.CR[wh];
    IF stats.stats[sendErrorBadStatus] # 0 OR stats.stats[queueTooOld] # 0 THEN {
      Put.Text[wh, "  Bad "L];
      Put.LongDecimal[wh, stats.stats[sendErrorBadStatus]];
      Put.Text[wh, ", stuck "L];
      Put.LongDecimal[wh, stats.stats[queueTooOld]];
      Put.CR[wh]; };
    Put.Text[wh, "  Queue too long "L];
    Put.LongDecimal[wh, stats.stats[congestion]];
    Put.Text[wh, ", NS "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.nsCongestion]];
    Put.Text[wh, "  Pup "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.pupCongestion]];
    Put.CR[wh];
    Put.Text[wh, "  Conn too greedy "L];
    Put.LongDecimal[wh, stats.stats[connTooGreedy]];
    Put.Text[wh, ", NS "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.nsTooGreedy]];
    Put.Text[wh, ", Pup "L];
    Put.LongDecimal[wh, stats.stats[PhoneNetExtras.pupTooGreedy]];
    Put.CR[wh];
    IF stats.stats[PhoneNetExtras.nsDupsFiltered] # 0 THEN {
      Put.Text[wh, "    NS duplicates discarded: "L];
      Put.LongDecimal[wh, stats.stats[PhoneNetExtras.nsDupsFiltered]];
      Put.CR[wh]; };
    IF stats.stats[PhoneNetExtras.pupDupsFiltered] # 0 THEN {
      Put.Text[wh, "    BSP probes/duplicates discarded: "L];
      Put.LongDecimal[wh, stats.stats[PhoneNetExtras.pupDupsFiltered]];
      Put.CR[wh]; };
    IF stats.stats[PhoneNetExtras.leafDupsFiltered] # 0 THEN {
      Put.Text[wh, "    Leaf duplicates discarded: "L];
      Put.LongDecimal[wh, stats.stats[PhoneNetExtras.leafDupsFiltered]];
      Put.CR[wh]; };
    END;

  PutHostNumbers: PROCEDURE [wh: Window.Handle, net: System.HostNumber] =
    BEGIN
    Put.HostNumber[wh, net, productSoftware];
    Put.Text[wh, "="L];
    Put.HostNumber[wh, net, octal];
    END;
      
  PutNetNumbers: PROCEDURE [wh: Window.Handle, net: System.NetworkNumber] =
    BEGIN
    Put.NetworkNumber[wh, net, productSoftware];
    Put.Text[wh, "="L];
    Put.NetworkNumber[wh, net, octal];
    END;
      
  PrintHeader: 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;

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


  SetupDeviceMenu: PUBLIC PROCEDURE =
    BEGIN
    Items: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF Menu.ItemObject];
    items: LONG POINTER TO Items;
    firstNetwork: Driver.Network ← Driver.GetDeviceChain[];
    devices: CARDINAL ← 0;
    FOR network: Driver.Network ← firstNetwork, network.next UNTIL network =
      NIL DO devices ← devices + 1; ENDLOOP;
    items ← z.NEW[Items[devices]];
    devices ← 0;
    FOR network: Driver.Network ← firstNetwork, network.next UNTIL network =
      NIL DO
      tag: STRING = [30];
      new: LONG STRING;
      SELECT network.device FROM
        ethernetOne => String.AppendString[tag, "Ether1"L];
        ethernet => String.AppendString[tag, "Ether"L];
        sla => String.AppendString[tag, "SLA"L];
        phonenet => String.AppendString[tag, "PhoneNet"L];
        ENDCASE => ERROR;
      String.AppendChar[tag, '-];
      String.AppendNumber[tag, network.pupNetNumber, 8];
      new ← z.NEW[StringBody[tag.length]];
      String.AppendString[new, tag];
      items[devices] ← [new, DoMenu];
      devices ← devices + 1;
      ENDLOOP;
    Menu.Instantiate[
      Menu.Create[DESCRIPTOR[items, devices], "DeviceInfo"],
      UserInput.GetDefaultWindow[]];
    END;


  -- Initialization

  SetupDeviceMenu[];
  END.