-- File: LineWatcher.mesa,  Last Edit: HGM  December 17, 1980  4:18 AM
-- Please don't forget to update the herald....

DIRECTORY
  Process USING [Detach, SetTimeout, MsecToTicks, Yield],
  Runtime USING [IsBound],
  String USING [AppendChar, AppendString, AppendNumber],
  Time USING [AppendCurrent],

  System USING [GreenwichMeanTime, GetGreenwichMeanTime],
  Put USING [Line],
  Tool USING [Create, MakeSWsProc, MakeMsgSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle],

  AltoSlaDefs;

LineWatcher: MONITOR
  IMPORTS Process, Runtime, String, Time, Put, System, Tool, AltoSlaDefs =
  BEGIN OPEN AltoSlaDefs;

  seconds: CARDINAL ← 5;

  activeLines: CARDINAL;
  lineInfo: POINTER TO ARRAY Line OF LineInfoBlock;
  slaRouting: POINTER TO ARRAY SlaHost OF RoutingTableEntry;

  oldSlaState: ARRAY Line OF LineState;
  oldPartner: ARRAY Line OF (0..maxSlaHost + 1];
  -- leave room for an illegal entry

  msg: Window.Handle ← NIL;
  pleaseStop, running: BOOLEAN ← FALSE;

  Init: PROCEDURE =
    BEGIN
    IF ~Runtime.IsBound[GetSlaInfoBlocks] THEN RETURN;
    IF BASE[GetSlaInfoBlocks[]] = NIL THEN RETURN;
    [] ← Tool.Create[
      name: "Line Watcher of December 17, 1980"L, makeSWsProc: MakeSWs,
      clientTransition: ClientTransition];
    Process.Detach[FORK Watch[]];
    END;

  Watch: ENTRY PROCEDURE =
    BEGIN
    time: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
    pause: CONDITION;
    Process.SetTimeout[@pause, Process.MsecToTicks[500]];
    InitializeThings[];
    UNTIL pleaseStop DO
      WHILE (System.GetGreenwichMeanTime[] - time) < seconds DO
	WAIT pause; ENDLOOP;
      time ← System.GetGreenwichMeanTime[];
      IF lineInfo # NIL THEN CheckLineState[];
      THROUGH [0..100) DO Process.Yield[]; ENDLOOP;
      ENDLOOP;
    END;

  InitializeThings: PROCEDURE =
    BEGIN
    myLineInfo: DESCRIPTOR FOR ARRAY OF LineInfoBlock ← GetSlaInfoBlocks[];
    activeLines ← LENGTH[myLineInfo];
    lineInfo ← LOOPHOLE[BASE[myLineInfo]];
    slaRouting ← GetSlaRoutingTable[];
    IF lineInfo = NIL THEN RETURN;
    FOR line: Line IN [0..activeLines) DO
      oldPartner[line] ← LOOPHOLE[maxSlaHost + 1];
      oldSlaState[line] ← LOOPHOLE[-1];
      ENDLOOP;
    END;

  CheckLineState: PROCEDURE =
    BEGIN
    FOR line: Line IN [0..activeLines) DO
      lib: LineInfoBlock = lineInfo[line];
      changed: BOOLEAN ← FALSE;
      IF oldSlaState[line] # lib.state THEN changed ← TRUE;
      IF oldPartner[line] # lib.partner THEN changed ← TRUE;
      IF ~changed THEN LOOP;
      oldSlaState[line] ← lib.state;
      oldPartner[line] ← lib.partner;
      BEGIN
      text: STRING = [100];
      Time.AppendCurrent[text];
      String.AppendString[text, "  Line "L];
      String.AppendNumber[text, line, 8];
      SELECT oldSlaState[line] FROM
	up =>
	  BEGIN
	  String.AppendString[text, " is now up to host "L];
	  String.AppendNumber[text, oldPartner[line], 8];
	  END;
	down => String.AppendString[text, " is now down"L];
	loopedBack => String.AppendString[text, " is now looped back"L];
	missing => String.AppendString[text, " is missing"L];
	halfUp =>
	  BEGIN
	  String.AppendString[text, " is now half up to host "L];
	  String.AppendNumber[text, oldPartner[line], 8];
	  END;
	passwordOk =>
	  BEGIN
	  String.AppendString[text, " got a password from "L];
	  String.AppendNumber[text, oldPartner[line], 8];
	  END;
	needPassword =>
	  BEGIN
	  String.AppendString[text, " needs a password from host "L];
	  String.AppendNumber[text, oldPartner[line], 8];
	  END;
	badPassword =>
	  BEGIN
	  String.AppendString[text, " received a BAD password from host "L];
	  String.AppendNumber[text, oldPartner[line], 8];
	  END;
	ENDCASE => String.AppendString[text, "??"L];
      String.AppendChar[text, '.];
      LogString[text];
      END;
      THROUGH [0..100) DO Process.Yield[]; ENDLOOP;
      ENDLOOP;
    END;

  LogString: PROCEDURE [text: STRING] =
    BEGIN IF msg # NIL THEN Put.Line[msg, text]; Put.Line[NIL, text]; END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN msg ← Tool.MakeMsgSW[window: window, lines: 5]; END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN IF new = inactive THEN msg ← NIL; END;

  Init[];
  END.