-- File: [Thyme]<Thyme>System>CSIM01>spOutput.mesa
-- Last editted:
-- Wilhelm April 6, 1982  11:36 AM, reformated by Barth and stored under
--   [Cherry]<Barth>Thyme>1.97> .
DIRECTORY spGlobals, AltoDefs, CWF, Real, RealFns, plotDefs, DisplayDefs,
          StreamDefs, TimeDefs, StringDefs, AbsAllocDefs;
spOutput:  PROGRAM
  IMPORTS spGlobals, CWF, P:  plotDefs, DisplayDefs, Str:  StreamDefs,
          TD:  TimeDefs, StringDefs, AbsAllocDefs
  EXPORTS spGlobals =
  BEGIN
    OPEN spGlobals;

    t0:  REAL;

    argumentPtr:  TYPE = LONG POINTER TO argument;
    argument:     TYPE = RECORD[node:       nodePtr,
                                branch:     branchPtr,
                                getComVal:  BOOLEAN,
                                dvdt:       BOOLEAN,
                                current:    BOOLEAN];

    printBlkPtr:  TYPE = LONG POINTER TO printBlk;
    printBlk:  TYPE = RECORD[nextBlk:      printBlkPtr,
                             relativeTo:   REAL,
                             arg:          argument];

    plotBlkPtr:  TYPE = LONG POINTER TO plotBlk;
    plotBlk:  TYPE = RECORD[nextBlk:     plotBlkPtr,
                            graph:       P.GraphHandle,
                            relativeTo:  REAL,
                            arg:         argument];

    maxPrints:  CARDINAL = 10;
    numPrints:  CARDINAL ← 0;
    prints:  ARRAY [0..maxPrints) OF RECORD[printList:       printBlkPtr,
                                            printTimeRelTo:  REAL,
                                            printStream:  Str.StreamHandle];

    maxPlots:  CARDINAL = 10;
    numPlots:  CARDINAL ← 0;
    plots:  ARRAY [0..maxPlots) OF RECORD[plotList:       plotBlkPtr,
                                          title:          LONG STRING,
                                          plotTimeRelTo:  REAL,
                                          yMin,
                                          yMax:           REAL,
                                          plotCount:      CARDINAL];

    displayBlanked:  BOOLEAN;
    systemWindow:    P.TWHandle ← NIL;
    thymeLog:        Str.StreamHandle ← NIL;
    time:            STRING = [40];
    bwDisplay:       BOOLEAN ← FALSE;

    makePrintBlk:  PROCEDURE RETURNS[pb:  printBlkPtr] =
      BEGIN
        pb ← AbsAllocDefs.Allocate[SIZE[printBlk]];
        pb↑ ← [NIL, 1.0, [NIL, NIL, FALSE, FALSE, FALSE]]
      END;

    makePlotBlk:  PROCEDURE RETURNS[pb:  plotBlkPtr] =
      BEGIN
        pb ← AbsAllocDefs.Allocate[SIZE[plotBlk]];
        pb↑ ← [NIL, NIL, 1.0, [NIL, NIL, FALSE, FALSE, FALSE]]
      END;

    restoreDisplay:  PUBLIC PROCEDURE =
      BEGIN
        IF displayBlanked THEN DisplayDefs.DisplayOn[]
      END;

    killPlot:  PUBLIC PROCEDURE =
      BEGIN
        i:  CARDINAL;
        time:  STRING = [40];

        time.length ← 0;
        TD.AppendDayTime[time, TD.UnpackDT[TD.CurrentDayTime[]]];
        CWF.FWF1[thymeLog, "*n*n%s", time];
        CWF.WF1["*n*n%s", time];
        IF thymeLog # NIL THEN thymeLog.destroy[thymeLog];
        FOR i IN [0..numPrints) DO
          prints[i].printStream.destroy[prints[i].printStream]
        ENDLOOP;
        P.CloseWindow[]
      END;

    openSysWindow:  PUBLIC PROCEDURE[herald:  STRING] =
      BEGIN
        logName:  STRING = [40];

        CWF.SWF1[logName, "%s.log", inputFileName];
        thymeLog ← Str.NewByteStream[logName, Str.WriteAppend];
        thymeLog.reset[thymeLog];
        time.length ← 0;
        TD.AppendDayTime[time, TD.UnpackDT[TD.CurrentDayTime[]]];
        CWF.FWF2[thymeLog, "%s*n*n%s*n*n", herald, time];
        CWF.WF2["%s*n*n%s*n*n", herald, time] 
      END;

    printSysWindow:  PUBLIC PROCEDURE[s:  STRING] =
      BEGIN
        IF systemWindow # NIL THEN P.WriteLine[s, systemWindow];
        CWF.FWF1[thymeLog, "%s*n", s]
      END;

    initPlot:  PUBLIC PROCEDURE[tMin, tMax:  REAL, blank:  BOOLEAN] =
      BEGIN
        window:  P.WindowHandle;
        plotWindow:  P.PWHandle;
        plotColor:  P.ColorHandle;
        i, color:  CARDINAL;
        pl:  plotBlkPtr;
        cy, cyTitle, h, w:  INTEGER;
        w2, wTitle:  P.TWHandle;
        s:  STRING = [20];

        displayBlanked ← blank AND ~bwDisplay;
        IF displayBlanked THEN DisplayDefs.DisplayOff[white];
        t0 ← tMin;
        P.SetTempFileName[inputFileName];
        FOR i IN [0..numPlots) DO
          window ← P.OpenWindow[NIL, TRUE, ~bwDisplay, FALSE];
          [w, h] ← P.GetWindowSize[window];
          cy ← P.HeightOfLines[MAX[plots[i].plotCount, 6]];
          cyTitle ← P.HeightOfLines[3];
          w2 ← P.GetTextWindow[1500, h - cy, w/2, h - 15,,,window];
          wTitle ← P.GetTextWindow[w/2, h - cyTitle, w, h - 15,,,window];
          P.WriteString[inputFileName, wTitle];
          P.WriteString["   ", wTitle];
          P.WriteLine[time, wTitle];
          IF plots[i].title # NIL THEN P.WriteLine[plots[i].title, wTitle];
          plotWindow ← P.GetPlotWindow[0, 0, w, h - cy - 50,
                                       tMin/plots[i].plotTimeRelTo,
                                       tMax/plots[i].plotTimeRelTo,
                                       plots[i].yMin, plots[i].yMax,
                                       , window];
          IF i = 0 THEN
            systemWindow ← P.GetTextWindow[w/2, h - cy, w, h - cyTitle,,
                                           FALSE];
          color ← 0;
          pl  ← plots[i].plotList;
          UNTIL pl = NIL DO
            plotColor ← P.GetStandardColor[color];
            P.SetWindowColor[plotColor, w2];
            pl↑.graph ← 
              SELECT color/8 FROM
                0 =>
                  P.GetGraphHandle[plotWindow, plotColor],
                1 =>
                  P.GetGraphHandle[plotWindow, plotColor,,, dashed],
              ENDCASE =>
                  P.GetGraphHandle[plotWindow, plotColor,,, dotted];
            color ← color + 1;
            IF pl↑.arg.getComVal THEN P.WriteString["Value of ", w2]
            ELSE
              IF pl↑.arg.current THEN
                P.WriteString["Current through ", w2]
              ELSE
                IF pl↑.arg.dvdt THEN
                  P.WriteString["Derivative at ", w2]
                ELSE P.WriteString["Voltage at ", w2];
            P.WriteString[makeStringNB[pl↑.arg.node, pl↑.arg.branch],
                          w2];
            IF pl↑.relativeTo # 1.0 THEN
              BEGIN
                CWF.SWF1[s, "/%-9.2f", @pl↑.relativeTo];
                P.WriteString[s, w2]
              END;
            P.WriteLine["", w2];
            pl ← pl↑.nextBlk
          ENDLOOP
        ENDLOOP
      END;

    evaluateArgument:  PROCEDURE[a:  argumentPtr] RETURNS[iv:  REAL] =
      BEGIN
        b:  branchPtr;
        IF a↑.getComVal THEN RETURN[a↑.branch↑.comVal];
        IF a↑.node # NIL THEN
          IF a↑.dvdt THEN iv ← a↑.node↑.nHist.f0
          ELSE iv ← a↑.node↑.nHist.y
        ELSE
          BEGIN
            b ← a.branch;
            IF a↑.current THEN
              WITH b↑ SELECT FROM
                resistor  =>
                  iv ← (posNode↑.nHist.y - negNode↑.nHist.y)/comVal;
                capacitor =>
                  iv ← (posNode↑.nHist.f0 - negNode↑.nHist.f0)*comVal;
                inductor  => iv ← iHist.y/comVal;
                vSource   => iv ← vsCurrent;
                iSource   => iv ← comVal
              ENDCASE
            ELSE iv ← b↑.posNode↑.nHist.y - b↑.negNode↑.nHist.y
          END
      END;

    plotFromList:  PUBLIC PROCEDURE[t:  REAL] =
      BEGIN
        i:  CARDINAL;
        p:  plotBlkPtr;
        r:  REAL;

        FOR i IN [0..numPlots) DO
          p ← plots[i].plotList;
          UNTIL p = NIL DO
            r ← evaluateArgument[@p↑.arg]/p↑.relativeTo;
            IF t > t0 THEN P.DrawGraph[p↑.graph,
                                       t/plots[i].plotTimeRelTo, r]
            ELSE P.PositionPen[p↑.graph, t0/plots[i].plotTimeRelTo, r];
            p ← p↑.nextBlk
          ENDLOOP
        ENDLOOP
      END;

    printFromList:  PUBLIC PROCEDURE[ni:  CARDINAL, t:  REAL,
                                     printStep:  BOOLEAN] =
      BEGIN
        i:  CARDINAL;
        p:  printBlkPtr;
        r, trel:  REAL;

        FOR i IN [0..numPrints) DO
          p ← prints[i].printList;
          trel ← t/prints[i].printTimeRelTo;
          CWF.FWF1[prints[i].printStream, "%9.3f", @trel];
          IF printStep THEN CWF.FWF1[prints[i].printStream, "(%2d)", @ni];
          UNTIL p = NIL DO
            r ← evaluateArgument[@p↑.arg]/p↑.relativeTo;
            CWF.FWF1[prints[i].printStream, " %9.3f", @r];
            p ← p↑.nextBlk
          ENDLOOP;
          CWF.FWFCR[prints[i].printStream]
        ENDLOOP
      END;

    scale:  PROCEDURE RETURNS[s:  REAL ← 1.0] =
      BEGIN
        IF item = colon THEN
          BEGIN
            next[];
            s ← getSignedNumber[]
          END
      END;

    makePrintList:  PUBLIC PROCEDURE =
      BEGIN
        I, D, cv:  BOOLEAN;
        pb, pb2, pb3:  printBlkPtr;
        n:  nodePtr;
        b:  branchPtr;
        name:  STRING = [40];

        IF numPrints >= maxPrints THEN
          BEGIN
            error[631];
            numPrints ← numPrints - 1
          END;
        IF item = leftB THEN next[] ELSE error[600];
        IF item = colon THEN
          BEGIN
            prints[numPrints].printTimeRelTo ← scale[];
            IF item = comma THEN next[] ELSE error[603,, FALSE]
          END
        ELSE prints[numPrints].printTimeRelTo ← 1.0;
        prints[numPrints].printList ← NIL;
        UNTIL item # name DO
          [n, b] ← findNodeOrBranch[];
          IF n = NIL AND b = NIL THEN error[620]
          ELSE
            BEGIN
              I ← b # NIL AND item = upArrow;
              D ← n # NIL AND item = quote;
              cv ← b # NIL AND item = atSign;
              IF I OR D OR cv THEN next[];
              pb ← makePrintBlk[];
              pb↑ ← [prints[numPrints].printList, scale[],
                     [n, b, cv, D, I]];
              prints[numPrints].printList ← pb
            END;
          IF item = comma THEN next[] ELSE EXIT
        ENDLOOP;
        pb ← NIL;
        pb3 ← prints[numPrints].printList;
        UNTIL pb3 = NIL DO
          pb2 ← pb3;
          pb3 ← pb3↑.nextBlk;
          pb2↑.nextBlk ← pb;
          pb ← pb2
        ENDLOOP;
        prints[numPrints].printList ← pb;
        CWF.SWF2[name, "%s.out%d", inputFileName, @numPrints];
        prints[numPrints].printStream ←
          Str.NewByteStream[name, Str.WriteAppend];
        prints[numPrints].printStream.reset[prints[numPrints].printStream];
        numPrints ← numPrints + 1;
        IF item = rightB THEN next[] ELSE error[601, TRUE]
      END;

    getPlotNum:  PROCEDURE RETURNS[r:  REAL ← 1.0] =
      BEGIN
        r ← getSignedNumber[];
        IF item = comma THEN next[] ELSE error[603,, FALSE]
      END;

    makePlotList:  PUBLIC PROCEDURE[bw:  BOOLEAN] =
      BEGIN
        I, D, cv:  BOOLEAN;
        pb:  plotBlkPtr;
        n:  nodePtr;
        b:  branchPtr;

        bwDisplay ← bw;
        IF numPlots >= maxPlots THEN
          BEGIN
            error[632, FALSE];
            numPlots ← numPlots - 1
          END;
        IF item = leftB THEN next[] ELSE error[600];
        IF item = string THEN
          BEGIN
            plots[numPlots].title ← newString;
            next[];
            IF item = comma THEN next[] ELSE error[603,, FALSE]
          END
        ELSE plots[numPlots].title ← NIL;
        IF item = colon THEN
          BEGIN
            plots[numPlots].plotTimeRelTo ← scale[];
            IF item = comma THEN next[] ELSE error[603,, FALSE]
          END
        ELSE plots[numPlots].plotTimeRelTo ← 1.0;
        plots[numPlots].yMin ← getPlotNum[];
        plots[numPlots].yMax ← getPlotNum[];
        IF plots[numPlots].yMin >= plots[numPlots].yMax THEN
          error[630, FALSE];
        plots[numPlots].plotList ← NIL;
        plots[numPlots].plotCount ← 0;
        UNTIL item # name DO
          plots[numPlots].plotCount ← plots[numPlots].plotCount + 1;
          [n, b] ← findNodeOrBranch[];
          IF n = NIL AND b = NIL THEN error[620]
          ELSE
            BEGIN
              I ← b # NIL AND item = upArrow;
              D ← n # NIL AND item = quote;
              cv ← b # NIL AND item = atSign;
              IF I OR D OR cv THEN next[];
              pb ← makePlotBlk[];
              pb↑ ← [plots[numPlots].plotList, NIL, scale[],
                     [n, b, cv, D, I]];
              plots[numPlots].plotList ← pb
            END;
          IF item = comma THEN next[] ELSE EXIT
        ENDLOOP;
        IF item = rightB THEN next[] ELSE error[601, TRUE];
        numPlots ← numPlots + 1
      END;

    dumpAll:  PUBLIC PROCEDURE[t:  REAL] =
      BEGIN
        dump:   Str.StreamHandle;
        dname:  STRING = [40];
        nodes:  nodePtr ← nodeList;
        inds:   inductorPtr ← inductorList;

        dname.length ← 0;
        StringDefs.AppendString[dname, "Dumped "];
        TD.AppendDayTime[dname, TD.UnpackDT[TD.CurrentDayTime[]]];
        printSysWindow[dname];
        CWF.SWF1[dname, "%s.dump", inputFileName];
        dump ← Str.NewByteStream[dname, Str.WriteAppend];
        dump.reset[dump];
        CWF.FWF1[dump, "ic[%f,*n", @t];
        UNTIL nodes = NIL DO
          CWF.FWF2[dump, "%s←%f",
                   makeStringNB[nodes, NIL, FALSE], @nodes↑.nHist.y];
          nodes ← nodes↑.nextNode;
          IF nodes # NIL THEN CWF.FWF[dump, ",*n"] ELSE CWF.FWFCR[dump]
        ENDLOOP;
        UNTIL inds = NIL DO
          CWF.FWF2[dump, "%s←%f",
                   makeStringNB[NIL, inds, FALSE], @inds↑.iHist.y];
          inds ← inds↑.nextInductor;
          IF inds # NIL THEN CWF.FWF[dump, ",*n"] ELSE CWF.FWFCR[dump]
        ENDLOOP;
        CWF.FWF[dump, "];*n"];
        dump.destroy[dump]
      END;
  END.