-- File: LineUse.mesa,  Last Edit: HGM  January 30, 1981  3:37 PM

DIRECTORY
  Event USING [Item, Reason, AddNotifier],
  Format USING [], -- Needed by Put.Number
  FormSW USING [
    ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem,
    StringItem, NumberItem],
  Inline USING [LowHalf, HighHalf],
  MsgSW USING [Post],
  Process USING [Detach, MsecToTicks, SetTimeout, Yield],
  Put USING [Char, CR, Date, Line, Number, Text],
  String USING [AppendLongNumber, AppendString],
  System USING [GreenwichMeanTime, GetGreenwichMeanTime, AdjustGreenwichMeanTime],
  Time USING [AppendCurrent, Current],
  Tool USING [
    Create, MakeSWsProc, UnusedLogName, MakeMsgSW, MakeFormSW, MakeFileSW,
    AddThisSW],
  ToolWindow USING [TransitionProcType, DisplayProcType, CreateSubwindow],
  Window USING [Handle, Box, DisplayData, DisplayInvert, DisplayWhite],

  AltoSlaDefs,
  GateControlDefs USING [gateControlExamine, gateControlStatsAck],
  PupDefs USING [
    AppendPupAddress, PupPackageMake, PupPackageDestroy, PupBuffer,
    GetFreePupBuffer, ReturnFreePupBuffer, PupAddress, GetPupAddress,
    PupNameTrouble, GetPupContentsBytes, SetPupContentsWords, PupSocket,
    PupSocketDestroy, PupSocketMake, MsToTocks],
  PupTypes USING [fillInSocketID],
  CommUtilDefs USING [magicMemoryLocation],
  DriverDefs;

LineUse: MONITOR
  IMPORTS
    Event, Inline, FormSW, MsgSW, Process, Put, String, System, Time, Tool,
    ToolWindow, Window, PupDefs
  SHARES DriverDefs =
  BEGIN OPEN PupDefs;

  tool, msg, form, boxes, log: Window.Handle;
  eventItem: Event.Item ← [eventMask: 177777B, eventProc: Broom];

  running: BOOLEAN ← FALSE;
  pleaseStop: BOOLEAN ← FALSE;
  indicator: {left, right, off} ← off;
  seconds: CARDINAL ← 60;
  target: STRING ← [30];
  where: PupAddress ← [[0], [0], [31415, 9265]];

  soc: PupSocket ← NIL;

  giantVector: DriverDefs.GiantVector;
  giantVectorLoc: POINTER TO DriverDefs.GiantVector;


  Go: FormSW.ProcType =
    BEGIN
    IF running THEN
      BEGIN MsgSW.Post[msg, "Somebody is already running..."L]; RETURN; END;
    Put.CR[log];
    Put.Date[log, Time.Current[], dateTime];
    Put.Text[log, "  Watching SLA lines on "L];
    IF ~FindPath[] THEN RETURN;
    running ← TRUE;
    Process.Detach[FORK Watch[]];
    END;

  Stop: FormSW.ProcType = BEGIN Off[]; END;

  Off: PROCEDURE =
    BEGIN
    IF ~running THEN RETURN;
    pleaseStop ← TRUE;
    WHILE running DO Process.Yield[]; ENDLOOP;
    pleaseStop ← FALSE;
    END;

  FindPath: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    Put.Text[log, target];
    Put.Char[log, '=];
    GetPupAddress[
      @where, target !
      PupNameTrouble =>
	BEGIN MsgSW.Post[msg, e]; Put.Line[log, e]; GOTO Trouble; END];
    PrintPupAddress[where];
    Put.Line[log, "."L];
    RETURN[TRUE];
    EXITS Trouble => RETURN[FALSE];
    END;


  Watch: ENTRY PROCEDURE =
    BEGIN
    time: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
    pause: CONDITION;
    MakeConnection[];
    SetupBoxes[];
    Process.SetTimeout[@pause, Process.MsecToTicks[500]];
    GetInfo[];
    oldLineInfo ← newLineInfo;
    FOR line: AltoSlaDefs.Line IN [0..activeLines) DO
      oldDest[line] ← 177777B; oldLineInfo[line].state ← missing; ENDLOOP;
    CheckLineState[];
    oldLineInfo ← newLineInfo;
    Put.CR[log];
    PrintHeader[];
    UNTIL pleaseStop DO
      UNTIL pleaseStop OR (System.GetGreenwichMeanTime[] - time) >= seconds DO
	WAIT pause; ENDLOOP;
      time ← System.AdjustGreenwichMeanTime[time, seconds];
      GetInfo[];
      PrintTime[];
      Put.CR[log];
      PrintInfo[];
      CheckLineState[];
      oldLineInfo ← newLineInfo;
      ENDLOOP;
    SetDownBoxes[];
    KillConnection[];
    running ← FALSE;
    END;

  activeLines: CARDINAL;
  hisLineInfo: LONG POINTER TO ARRAY AltoSlaDefs.Line OF
    AltoSlaDefs.LineInfoBlock;
  hisSlaRouting: LONG POINTER TO ARRAY AltoSlaDefs.SlaHost OF
    AltoSlaDefs.RoutingTableEntry;

  oldLineInfo, newLineInfo: ARRAY AltoSlaDefs.Line OF AltoSlaDefs.LineInfoBlock;
  newSlaRoutingTable: ARRAY AltoSlaDefs.SlaHost OF AltoSlaDefs.RoutingTableEntry;
  oldDest: ARRAY AltoSlaDefs.Line OF CARDINAL;

  GetInfo: PROCEDURE =
    BEGIN OPEN AltoSlaDefs;
    GetBig[
      to: @newLineInfo, from: hisLineInfo, size: activeLines*SIZE[LineInfoBlock]];
    GetBig[
      to: @newSlaRoutingTable, from: hisSlaRouting,
      size: maxSlaHost*SIZE[RoutingTableEntry]];
    END;


  PrintHeader: PROCEDURE =
    BEGIN OPEN AltoSlaDefs;
    Put.Line[
      log,
      "
    Q       Rej         Packets           Bytes         Bits/Sec      Delay Ms
L  H L  C  H  S     Hi   Sent   Recv   Sent   Recv   Sent   Recv     Hi   Send
"L];
    END;

  PrintInfo: PROCEDURE =
    BEGIN
    FOR line: AltoSlaDefs.Line IN [0..activeLines) DO
      lib: AltoSlaDefs.LineInfoBlock;
      temp: LONG INTEGER;
      O1[line];
      D3[newLineInfo[line].hiPriQueue.length];
      D2[newLineInfo[line].lowPriQueue.length];
      lib.connRejections ←
	newLineInfo[line].connRejections - oldLineInfo[line].connRejections;
      lib.hiRejections ←
	newLineInfo[line].hiRejections - oldLineInfo[line].hiRejections;
      lib.rejections ←
	newLineInfo[line].rejections - oldLineInfo[line].rejections;
      LD3[lib.connRejections];
      LD3[lib.hiRejections];
      LD3[lib.rejections];
      lib.packetsSent ←
	newLineInfo[line].packetsSent - oldLineInfo[line].packetsSent;
      lib.packetsRecv ←
	newLineInfo[line].packetsRecv - oldLineInfo[line].packetsRecv;
      lib.bytesSent ← newLineInfo[line].bytesSent - oldLineInfo[line].bytesSent;
      lib.bytesRecv ← newLineInfo[line].bytesRecv - oldLineInfo[line].bytesRecv;
      lib.queueDelay ←
	newLineInfo[line].queueDelay - oldLineInfo[line].queueDelay;
      lib.hiPacketsSent ←
	newLineInfo[line].hiPacketsSent - oldLineInfo[line].hiPacketsSent;
      lib.hiBytesSent ←
	newLineInfo[line].hiBytesSent - oldLineInfo[line].hiBytesSent;
      lib.hiQueueDelay ←
	newLineInfo[line].hiQueueDelay - oldLineInfo[line].hiQueueDelay;
      LD7[lib.hiPacketsSent];
      LD7[lib.packetsSent];
      LD7[lib.packetsRecv];
      LD7[lib.bytesSent];
      LD7[lib.bytesRecv];
      temp ← lib.bytesSent + AltoSlaDefs.overheadPerPacket*lib.packetsSent;
      LD7[temp*8/seconds];
      temp ← lib.bytesRecv + AltoSlaDefs.overheadPerPacket*lib.packetsRecv;
      LD7[temp*8/seconds];
      LD7[(lib.hiQueueDelay*39)/lib.hiPacketsSent];
      LD7[(lib.queueDelay*39)/lib.packetsSent];
      Put.CR[log];
      ENDLOOP;
    END;

  CheckLineState: PROCEDURE =
    BEGIN
    changed: BOOLEAN;
    FOR line: AltoSlaDefs.Line IN [0..activeLines) DO
      changed ← FALSE;
      -- find out who this line is connected to
      FOR host: AltoSlaDefs.SlaHost IN AltoSlaDefs.SlaHost DO
	rte: POINTER TO AltoSlaDefs.RoutingTableEntry;
	rte ← @newSlaRoutingTable[host];
	IF rte.line = line AND rte.hops = 1 THEN
	  BEGIN
	  IF oldDest[line] # host THEN changed ← TRUE;
	  oldDest[line] ← host;
	  EXIT;
	  END;
	ENDLOOP;
      IF oldLineInfo[line].state # newLineInfo[line].state THEN changed ← TRUE;
      IF ~changed THEN LOOP;
      PrintTime[];
      Put.Text[log, "  Line "];
      O[line];
      Put.Text[log, " is "];
      SELECT newLineInfo[line].state FROM
	up => BEGIN Put.Text[log, "up to host "]; O[oldDest[line]]; END;
	down => Put.Text[log, "down"L];
	loopedBack => Put.Text[log, "looped back"L];
	ENDCASE => Put.Line[log, "??"L];
      Put.Char[log, '.];
      Put.CR[log];
      ENDLOOP;
    END;

  Read: PROCEDURE [p: LONG POINTER] RETURNS [x: UNSPECIFIED] =
    BEGIN Get[to: @x, from: p, size: 1]; END;

  GetBig: PROCEDURE [to: POINTER, from: LONG POINTER, size: CARDINAL] =
    BEGIN
    end: CARDINAL ← 0;
    hunk: CARDINAL;
    UNTIL end = size DO
      hunk ← MIN[250, size - end];
      Get[to + end, from + end, hunk];
      end ← end + hunk;
      ENDLOOP;
    END;

  -- get a block of info from the "right" address space

  thisID: CARDINAL ← 0;
  Get: PROCEDURE [to: POINTER, from: LONG POINTER, size: CARDINAL] =
    BEGIN
    b: PupBuffer;
    i: CARDINAL;
    IF size > 250 THEN ERROR;
    IF Inline.HighHalf[from] # 0 THEN ERROR;
    thisID ← thisID + 1;
    DO
      -- until we get the answer
      b ← GetFreePupBuffer[];
      b.pupID ← [27182, thisID];
      b.pupWords[0] ← LOOPHOLE[Inline.LowHalf[from]];
      b.pupWords[1] ← size;
      b.pupType ← GateControlDefs.gateControlExamine;
      SetPupContentsWords[b, 2];
      soc.put[b];
      -- The loop structure here is not very nice.....
      FlipBoxes[];
      b ← soc.get[];
      IF b = NIL THEN LOOP;
      IF b.pupType = error OR b.pupID.b # thisID THEN
	BEGIN ReturnFreePupBuffer[b]; LOOP; END;
      IF b.pupType = GateControlDefs.gateControlStatsAck AND GetPupContentsBytes[
	b] = 2*(2 + size) THEN EXIT;
      ERROR;
      ENDLOOP;
    FlipBoxes[];
    FOR i IN [0..size) DO (to + i)↑ ← b.pupWords[2 + i]; ENDLOOP;
    ReturnFreePupBuffer[b];
    END;

  PrintTime: PROCEDURE =
    BEGIN text: STRING = [20]; Time.AppendCurrent[text]; Put.Text[log, text]; END;

  PrintPupAddress: PROCEDURE [a: PupAddress] =
    BEGIN
    text: STRING = [20];
    AppendPupAddress[text, a];
    Put.Text[log, text];
    END;

  O: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[log, n, [8, FALSE, TRUE, 0]]; END;

  O1: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[log, n, [8, FALSE, TRUE, 1]]; END;

  O3: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[log, n, [8, FALSE, TRUE, 3]]; END;

  D: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[log, n, [10, FALSE, TRUE, 0]]; END;

  D2: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[log, n, [10, FALSE, TRUE, 2]]; END;

  D3: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[log, n, [10, FALSE, TRUE, 3]]; END;

  D4: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[log, n, [10, FALSE, TRUE, 4]]; END;

  LD2: PROCEDURE [num: LONG INTEGER] =
    BEGIN
    s: STRING = [20];
    String.AppendLongNumber[s, num, 10];
    THROUGH [s.length..2) DO Put.Char[log, ' ]; ENDLOOP;
    Put.Text[log, s];
    END;

  LD3: PROCEDURE [num: LONG INTEGER] =
    BEGIN
    s: STRING = [20];
    String.AppendLongNumber[s, num, 10];
    THROUGH [s.length..3) DO Put.Char[log, ' ]; ENDLOOP;
    Put.Text[log, s];
    END;

  LD7: PROCEDURE [num: LONG INTEGER] =
    BEGIN
    s: STRING = [20];
    String.AppendLongNumber[s, num, 10];
    THROUGH [s.length..7) DO Put.Char[log, ' ]; ENDLOOP;
    Put.Text[log, s];
    END;

  MakeConnection: PROCEDURE =
    BEGIN
    soc ← PupSocketMake[PupTypes.fillInSocketID, where, MsToTocks[1000]];
    giantVectorLoc ← Read[CommUtilDefs.magicMemoryLocation];
    Get[
      to: @giantVector, from: giantVectorLoc, size: SIZE[DriverDefs.GiantVector]];
    BEGIN OPEN AltoSlaDefs;
    p: LONG POINTER ← giantVector.slaThings;
    IF p = NIL THEN ERROR;
    p ← p + maxByte; -- skip CRC Table
    activeLines ← Read[p];
    p ← 8 + p + maxLine*SIZE[LineTableEntry];
    hisSlaRouting ← p;
    p ← p + maxSlaHost*SIZE[RoutingTableEntry];
    hisLineInfo ← p;
    IF activeLines > maxLine THEN ERROR;
    END;
    END;

  KillConnection: PROCEDURE = BEGIN PupSocketDestroy[soc]; soc ← NIL; END;

  indicatorBox: Window.Box = [[25, 10], [16, 16]];
  DisplayBoxes: ToolWindow.DisplayProcType =
    BEGIN
    pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD;
    left: WORD = 177400B;
    right: WORD = 000377B;
    SELECT indicator FROM
      left => pattern ← [ALL[left], ALL[right]];
      right => pattern ← [ALL[right], ALL[left]];
      off => pattern ← [ALL[0], ALL[0]];
      ENDCASE;
    Window.DisplayData[window, indicatorBox, @pattern, 1]
    END;

  SetupBoxes: PROCEDURE = BEGIN indicator ← left; DisplayBoxes[boxes]; END;

  FlipBoxes: PROCEDURE =
    BEGIN
    SELECT indicator FROM
      left => indicator ← right;
      off, right => indicator ← left;
      ENDCASE;
    Window.DisplayInvert[boxes, indicatorBox];
    END;

  SetDownBoxes: PROCEDURE =
    BEGIN indicator ← off; Window.DisplayWhite[boxes, indicatorBox]; END;

  MakeBoxesSW: PROCEDURE [window: Window.Handle] =
    BEGIN
    boxes ← ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes];
    boxes.box.dims.h ← 36;
    Tool.AddThisSW[window: window, sw: boxes, swType: vanilla];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    logFileName: STRING = [40];
    msg ← Tool.MakeMsgSW[window: window, lines: 5];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    MakeBoxesSW[window];
    Tool.UnusedLogName[logFileName, "LineUse.log$"L];
    log ← Tool.MakeFileSW[window: window, name: logFileName];
    END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 4;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "Stop"L, proc: Stop, place: FormSW.newLine];
    items[1] ← FormSW.CommandItem[tag: "Go"L, proc: Go];
    items[2] ← FormSW.NumberItem[tag: "Seconds"L, value: @seconds, default: 60];
    items[3] ← FormSW.StringItem[tag: "Target"L, string: @target];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive =>
	BEGIN String.AppendString[target, "ME"L]; PupDefs.PupPackageMake[]; END;
      new = inactive =>
	BEGIN IF running THEN Off[]; PupDefs.PupPackageDestroy[]; END;
      ENDCASE;
    END;

  Broom: PROCEDURE [why: Event.Reason] =
    BEGIN
    SELECT why FROM
      makeImage, makeCheck =>
	BEGIN IF running THEN Off[]; PupDefs.PupPackageDestroy[]; END;
      startImage, restartCheck, continueCheck =>
	BEGIN PupDefs.PupPackageMake[]; END;
      ENDCASE => NULL;
    END;


  Init: PROCEDURE =
    BEGIN
    herald: STRING = "Line Use of January 30, 1981"L;
    tool ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    END;

  -- Main Body

  Event.AddNotifier[@eventItem];
  END.