-- File: TimeServersParmTajo.mesa - last edit:
-- WIrish               5-Feb-88 13:31:42
-- HGM                 15-Jun-85 15:52:11
-- Copyright (C) 1983, 1984, 1985, 1988 by Xerox Corporation. All rights reserved.
 
DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  FormSW USING [
    ClientItemsProcType, Display, ProcType, AllocateItemDescriptor, newLine,
    CommandItem, NumberItem],
  Inline USING [LowHalf],
  Process USING [Detach, Pause, SecondsToTicks, Ticks],
  Put USING [Text],
  Runtime USING [GetBcdTime],
  String USING [AppendChar, AppendDecimal, AppendString, AppendLongDecimal],
  StringLookUp USING [noMatch, TableDesc],
  System USING [
    GetLocalTimeParameters, LocalTimeParameters, NetworkAddress, nullNetworkAddress, WestEast],
  TemporarySetGMT USING [adjustableClockRate, SetClockRate],
  Time USING [Append, AppendCurrent, Unpack],
  Token USING [Boolean, Decimal, LongDecimal],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle],

  Indirect USING [Close, NextValue, OpenSection],
  PupTimeServer USING [PupTimeServerOff, PupTimeServerOn],
  PupTimeServerOps USING [parmsOk, correction],
  TimeServerOps USING [SetClockDrift, SetClockWobble, Start, StartReset, Stop],
  Trouble USING [Bug];

TimeServersParmTajo: PROGRAM
  IMPORTS
    CmFile, FormSW, Inline, Process, Put, Runtime, String,
    System, TemporarySetGMT, Time, Token, Tool,
    Indirect, PupTimeServer, PupTimeServerOps, TimeServerOps, Trouble =
  BEGIN

  msg, form: PUBLIC Window.Handle ← NIL;

  Init: PROCEDURE =
    BEGIN
    herald: LONG STRING = [100];
    String.AppendString[herald, "Time Servers of "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs,
      clientTransition: ClientTransition, initialState: inactive];
    END;

  FindParameters: PROCEDURE =
    BEGIN
    ok: BOOLEAN ← TRUE;
    flakey, disableNSTimeServer: BOOLEAN ← FALSE;
    dstSpecified, zoneSpecified: BOOLEAN ← FALSE;
    driftSpecified, wobbleSpecified, correctionSpecified: BOOLEAN ← FALSE;
    parms: System.LocalTimeParameters ← System.GetLocalTimeParameters[];
    cmFile: CmFile.Handle;
    Option: TYPE = MACHINE DEPENDENT{
      correction(0), drift, wobble, dst, zone, flakey, disableNSTimeServer,
      noMatch(StringLookUp.noMatch)};
    DefinedOption: TYPE = Option [correction..disableNSTimeServer];
    CheckType: PROCEDURE [h: CmFile.Handle, table: StringLookUp.TableDesc]
      RETURNS [index: CARDINAL] = Indirect.NextValue;
    MyNextValue: PROCEDURE [
      h: CmFile.Handle,
      table: LONG DESCRIPTOR FOR ARRAY DefinedOption OF LONG STRING]
      RETURNS [index: Option] = LOOPHOLE[CheckType];
    optionTable: ARRAY DefinedOption OF LONG STRING ← [
      correction: "Correction"L, drift: "Drift"L, wobble: "Wobble"L,
      dst: "DST"L, zone: "Zone"L, flakey: "Flakey"L,
      disableNSTimeServer: "Disable NS Time Server"L];
    PupTimeServerOps.parmsOk ← FALSE;
    cmFile ← Indirect.OpenSection["TimeServer"L];
    IF cmFile = NIL THEN
      BEGIN
      Message["Can't find [TimeServer] section in parameter file"L];
      RETURN;
      END;
    DO
      text: STRING = [200];
      option: Option;
      option ← MyNextValue[cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        noMatch => EXIT;
        correction =>
          BEGIN
          correction: LONG INTEGER ← Token.LongDecimal[cmFile];
          String.AppendLongDecimal[text, correction];
          Message["The crystal correction is "L, text, " milliseconds per day"L];
          SetCorrection[correction];
          PupTimeServerOps.correction ← Inline.LowHalf[correction/1000];
          correctionSpecified ← TRUE;
          END;
        drift =>
          BEGIN
          drift: CARDINAL ← Token.Decimal[cmFile];
          String.AppendLongDecimal[text, drift];
          Message["The clock drift is "L, text, " milliseconds per day"L];
	  TimeServerOps.SetClockDrift[86400, drift];
          driftSpecified ← TRUE;
          END;
        wobble =>
          BEGIN
          wobble: CARDINAL ← Token.Decimal[cmFile];
          String.AppendLongDecimal[text, wobble];
          Message["The clock wobble is "L, text, " milliseconds per quarter day"L];
	  TimeServerOps.SetClockWobble[86400/4, wobble];
          wobbleSpecified ← TRUE;
          END;
	dst =>  -- <begin day> <end day>
          BEGIN
          first: CARDINAL ← Token.Decimal[cmFile];
          last: CARDINAL ← Token.Decimal[cmFile];
          IF first # parms.beginDST OR last # parms.endDST THEN
            BEGIN Message["The DST info on this disk is wrong"L]; ok ← FALSE; END;
          String.AppendString[text, "DST: begin="L];
          String.AppendDecimal[text, first];
          String.AppendString[text, ", last="L];
          String.AppendDecimal[text, last];
          Message[text];
          dstSpecified ← TRUE;
          END;
        zone =>  -- <sign> <hours>:<minutes>
          BEGIN
          hours: INTEGER ← Token.Decimal[cmFile];
          minutes: CARDINAL ← Token.Decimal[cmFile];
          westEast: System.WestEast ← IF hours < 0 THEN east ELSE west;
          hours ← ABS[hours];
          IF westEast # parms.direction OR hours # parms.zone
            OR minutes # parms.zoneMinutes THEN
            BEGIN
            Message["The time zone info on this disk is wrong"L];
            ok ← FALSE;
            END;
          String.AppendString[text, "ZONE: hours="L];
          String.AppendDecimal[text, hours];
          String.AppendString[
            text, IF westEast = west THEN "(west)"L ELSE "(east)"L];
          String.AppendString[text, ", minutes="L];
          String.AppendDecimal[text, minutes];
          Message[text];
          zoneSpecified ← TRUE;
          END;
        flakey =>
          BEGIN
          IF Token.Boolean[cmFile] THEN
            BEGIN
            Message["This clock is Flakey"L];
            Process.Detach[FORK Flakey[]];
	    flakey ← TRUE;
            END
          ELSE Message["This clock is not Flakey"L];
          END;
        disableNSTimeServer =>
          BEGIN
          IF Token.Boolean[cmFile] THEN
            BEGIN
            Message["Turning OFF the NS Time Server"L];
	    disableNSTimeServer ← TRUE;
            END
          ELSE Message["Didn't turn OFF the NS Time Server"L];
          END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    ok ← ok AND dstSpecified AND zoneSpecified AND correctionSpecified
      AND driftSpecified AND wobbleSpecified;
    IF ~(PupTimeServerOps.parmsOk ← (ok AND ~flakey))
      THEN Message["*** WARNING! PUP Time Server NOT started. ***"L];
    IF ok AND ~disableNSTimeServer
      THEN TimeServerOps.Start[]
      ELSE BEGIN
        TimeServerOps.Stop[];  -- Created and Started by START Trap
	Message["*** WARNING! NS Time Server NOT started. ***"L];
	END;
    END;

  SetCorrection: PROCEDURE [arg: LONG INTEGER] =
    BEGIN
    IF arg # 0 THEN
      BEGIN
      secondsPerDay: LONG CARDINAL = 86400;
      delta: LONG INTEGER ← arg;
      bottom: LONG CARDINAL ← 1000*secondsPerDay;
      IF ~TemporarySetGMT.adjustableClockRate THEN
        Trouble.Bug["The clock on this machine is not adjustable"L];
-- Yetch, what a pile of crap
-- 640 works on D0s (or at least some of them)
-- Beware, Dicentras need a smaller number: 274
      UNTIL ABS[delta] < 640 DO  -- Avoid overflow
        delta ← delta/2;
	bottom ← bottom/2;
	ENDLOOP;
      TemporarySetGMT.SetClockRate[bottom, bottom + delta];   
      END;
    END;
  
  Message: PROCEDURE [one, two, three: LONG STRING ← NIL] =
    BEGIN
    text: STRING = [100];
    Time.AppendCurrent[text];
    String.AppendString[text, "  TimeServer: "L];
    String.AppendString[text, one];
    IF two # NIL THEN String.AppendString[text, two];
    IF three # NIL THEN String.AppendString[text, three];
    LogString[text];
    END;
  
  UpdatePicture: PUBLIC PROCEDURE =
    BEGIN
    IF form = NIL THEN RETURN;
    FormSW.Display[form];
    END;

  Start: FormSW.ProcType =
    BEGIN
    PupTimeServer.PupTimeServerOn[];
    END;

  Stop: FormSW.ProcType =
    BEGIN
    PupTimeServer.PupTimeServerOff[];
    END;

  NSReset: FormSW.ProcType =
    BEGIN
    where: System.NetworkAddress = System.nullNetworkAddress;
    TimeServerOps.StartReset[where];
    END;

  LogString: PROCEDURE [text: LONG STRING] =
    BEGIN
    String.AppendChar[text, '.];
    String.AppendChar[text, Ascii.CR];
    Put.Text[NIL, text];
    IF msg # NIL THEN Put.Text[msg, text];
    END;

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

  startIX: CARDINAL = 0;
  stopIX: CARDINAL = 1;
  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 4;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "PupStart"L, proc: Start, place: FormSW.newLine];
    items[1] ← FormSW.CommandItem[tag: "PupStop"L, proc: Stop];

    items[2] ← FormSW.NumberItem[
      tag: "Correction"L, value: @PupTimeServerOps.correction, boxWidth: 50,
      place: FormSW.newLine, readOnly: TRUE];

    items[3] ← FormSW.CommandItem[
      tag: "NS Reset"L, proc: NSReset, place: FormSW.newLine];
    
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    IF new = inactive THEN msg ← form ← NIL;
    SELECT TRUE FROM
      old = inactive =>
        BEGIN
        END;
      new = inactive =>
        BEGIN
        END;
      ENDCASE;
    END;

  Flakey: PROCEDURE =
    BEGIN
    halfHour: Process.Ticks = Process.SecondsToTicks[30*60];
    DO
      Process.Pause[halfHour];
      TimeServerOps.StartReset[System.nullNetworkAddress];
      ENDLOOP;
    END;


  -- Initialization

  Init[];
  FindParameters[];
  PupTimeServer.PupTimeServerOn[];
  END.