-- DiagnosticsImplC.mesa  -  edited by:
-- Poskanzer	27-Apr-83 10:31:52
-- Paul 	 9-Jan-84 10:32:49 

DIRECTORY
  Address USING [AddressTrouble, NetworkAddressToString, StringsToNetworkAddress],
  CommOnlineDiagnostics USING [
    CommError, EchoResults, EventReporter, GetEchoResults, StartEchoUser,
    WordsInPacket],
  DiagnosticsOps USING [
    CheckForAbort, PutCR, PutChar, PutLine, PutLongNumber, PutNumber, PutText],
  Format USING [StringProc],
  Heap USING [systemZone],
  NetworkStream USING [AssignNetworkAddress],
  NSConstants USING [echoerSocket],
  Process USING [Detach, SecondsToTicks, SetTimeout, Pause],
  String USING [Replace],
  System USING [NetworkAddress, nullNetworkAddress];

DiagnosticsImplC: MONITOR
  IMPORTS
    Address, CommOnlineDiagnostics, DiagnosticsOps, Heap, NetworkStream, Process,
    String
  EXPORTS DiagnosticsOps =
  BEGIN

  doneWithSession: BOOLEAN ← FALSE;
  echoResults: CommOnlineDiagnostics.EchoResults;
  hostAddr: System.NetworkAddress ← System.nullNetworkAddress;
  showEchoes: BOOLEAN ← TRUE;

  EchoUserTest: PUBLIC PROCEDURE [
    currDomain: LONG STRING, currOrganization: LONG STRING, usernet: LONG STRING,
    user: LONG STRING, targetnet: LONG STRING, target: LONG STRING,
    realFeedBack, waitResponse: BOOLEAN,
    dataPattern: CommOnlineDiagnostics.WordsInPacket,
    minPacketSize, maxPacketSize, delayBetween: CARDINAL] =
    BEGIN
    CopyMyAddress: Format.StringProc = {
      String.Replace[@myAddress, s, Heap.systemZone]};

    destAddr: System.NetworkAddress;
    myAddr: System.NetworkAddress = NetworkStream.AssignNetworkAddress[];
    myAddress: LONG STRING ← NIL;
    errFlag: BOOLEAN ← FALSE;

    showEchoes ← realFeedBack;
    hostAddr ← Address.StringsToNetworkAddress[
      user, NSConstants.echoerSocket, usernet !
      Address.AddressTrouble =>
        BEGIN
        DiagnosticsOps.PutText["Echo Source address translation troubles: "L];
        DiagnosticsOps.PutLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];

    destAddr ← Address.StringsToNetworkAddress[
      target, NSConstants.echoerSocket, targetnet !
      Address.AddressTrouble =>
        BEGIN
        DiagnosticsOps.PutText["Destination address translation troubles: "L];
        DiagnosticsOps.PutLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];

    IF hostAddr.net # myAddr.net OR hostAddr.host # myAddr.host THEN
      showEchoes ← FALSE;  -- can't show remotes

    Address.NetworkAddressToString[
      myAddr, CopyMyAddress !
      Address.AddressTrouble =>
        BEGIN
        DiagnosticsOps.PutText["Local Network Address translation troubles: "L];
        DiagnosticsOps.PutLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];

    IF errFlag THEN RETURN;
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutLine["               Echo Test Parameters:"L];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Current Domain:Organization = "L];
    DiagnosticsOps.PutText[currDomain];
    DiagnosticsOps.PutChar[':];
    DiagnosticsOps.PutText[currOrganization];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Local Network Address = "L];
    DiagnosticsOps.PutText[myAddress];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Echo Source = "L];
    DiagnosticsOps.PutText[user];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Echo Destination = "L];
    DiagnosticsOps.PutText[target];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Data Pattern: "L];
    DiagnosticsOps.PutText[
      SELECT dataPattern FROM
        all0s => "All 0's"L,
        all1s => "All 1's"L,
        incrWords => "Incrementing"L,
        allConstant => "Constant"L,
        dontCare => "Random"L,
        ENDCASE => "Unknown"L];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Minimum Packet Size: "L];
    DiagnosticsOps.PutNumber[minPacketSize, 10];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Maximum Packet Size: "L];
    DiagnosticsOps.PutNumber[maxPacketSize, 10];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Wait for response: "L];
    DiagnosticsOps.PutText[IF waitResponse THEN "YES"L ELSE "NO"L];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutText["Delay parameter: "L];
    DiagnosticsOps.PutNumber[delayBetween, 10];
    DiagnosticsOps.PutText[" milliseconds"L];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutCR[];

    DiagnosticsOps.PutLine["Feedback key:"L];
    DiagnosticsOps.PutLine["Each '!' represents a successful echo operation."L];
    DiagnosticsOps.PutLine[
      "Each '?' represents no packet echoed back during waiting period."L];
    DiagnosticsOps.PutLine["Each '#' represents a packet echoed back late."L];
    DiagnosticsOps.PutLine[
      "Each '~' represents a packet which was echoed with bad data but a good CRC."L];
    DiagnosticsOps.PutLine[
      "Each ':' marks largest size packet has just been transmitted."L];
    DiagnosticsOps.PutLine["Each '*' represents an unexpected packet."L];
    DiagnosticsOps.PutCR[];
    
    echoResults.totalTime ← 0; echoResults.totalAttempts ← 0;
    echoResults.successes ← 0; echoResults.timeouts ← 0; echoResults.late ← 0;
    echoResults.unexpected ← 0; echoResults.bad ← 0; 
    echoResults.avgDelayInMsecs ← 0; 

    CommOnlineDiagnostics.StartEchoUser[
      targetSystemElement: destAddr,
      echoParams: [
      minPacketSizeInBytes: minPacketSize, maxPacketSizeInBytes: maxPacketSize,
      wordContents: dataPattern, waitForResponse: waitResponse,
      minMsecsBetweenPackets: delayBetween], eventReporter: PrintFeedback,
      host: hostAddr !
      CommOnlineDiagnostics.CommError =>
        BEGIN
        DiagnosticsOps.PutText["Communication Error: "L];
        DiagnosticsOps.PutLine[
          SELECT reason FROM
            transmissionMediumProblem => "Transmission Medium Problem"L,
            noAnswerOrBusy => "No Answer Or Busy"L,
            noRouteToSystemElement => "No Route To System Element"L,
            remoteSystemElementNotResponding =>
              "Remote System Element Not Responding"L,
            tooManyConnections => "Too Many Connections"L,
            noSuchDiagnostic => "No Such Diagnostic"L,
            communicationError => "Communication Error"L,
            ENDCASE => "Unknown Error"L];
        CONTINUE;
        END];  --remote needs ServerOn!!!!!

    DiagnosticsOps.PutLine["Echo Test Started"L];

    doneWithSession ← FALSE;
    Process.Detach[FORK Echoer[]];
    DO
      DiagnosticsOps.CheckForAbort[ ! ABORTED => EXIT];
      Process.Pause[Process.SecondsToTicks[1]];
      ENDLOOP;
    doneWithSession ← TRUE;

    [echoResults] ← CommOnlineDiagnostics.GetEchoResults[
      stopIt: TRUE, host: hostAddr !
      CommOnlineDiagnostics.CommError =>
        BEGIN
        DiagnosticsOps.PutText["Communication Error: "L];
        DiagnosticsOps.PutLine[
          SELECT reason FROM
            transmissionMediumProblem => "Transmission Medium Problem"L,
            noAnswerOrBusy => "No Answer Or Busy"L,
            noRouteToSystemElement => "No Route To System Element"L,
            remoteSystemElementNotResponding =>
              "Remote System Element Not Responding"L,
            tooManyConnections => "Too Many Connections"L,
            noSuchDiagnostic => "No Such Diagnostic"L,
            communicationError => "Communication Error"L,
            ENDCASE => "Unknown Error"L];
	  CONTINUE;
        END];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutLine["Final results: "L];
    PrintResults[];
    DiagnosticsOps.PutLine["Done"L];
    END;  -- EchoUserTest --

  Echoer: ENTRY PROC = {
    cv: CONDITION;
    Process.SetTimeout[@cv, Process.SecondsToTicks[50]];
    UNTIL doneWithSession DO
      WAIT cv;
      IF doneWithSession THEN EXIT;
      [echoResults] ← CommOnlineDiagnostics.GetEchoResults[
        stopIt: FALSE, host: hostAddr !
        CommOnlineDiagnostics.CommError =>
          BEGIN
          DiagnosticsOps.PutText["Communication Error: "L];
          DiagnosticsOps.PutLine[
          SELECT reason FROM
            transmissionMediumProblem => "Transmission Medium Problem"L,
            noAnswerOrBusy => "No Answer Or Busy"L,
            noRouteToSystemElement => "No Route To System Element"L,
            remoteSystemElementNotResponding =>
              "Remote System Element Not Responding"L,
            tooManyConnections => "Too Many Connections"L,
            noSuchDiagnostic => "No Such Diagnostic"L,
            communicationError => "Communication Error"L,
            ENDCASE => "Unknown Error"L];
	    EXIT;
          END];
      DiagnosticsOps.PutCR[];
      DiagnosticsOps.PutCR[];
      DiagnosticsOps.PutText["Intermediate results: "L];
      PrintResults[];
      ENDLOOP};

  PrintResults: PROCEDURE [] = {
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutText["Total number of packet attempts: "L];
    DiagnosticsOps.PutLongNumber[echoResults.totalAttempts, 10];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutText["Number of good packets received: "L];
    DiagnosticsOps.PutLongNumber[echoResults.successes, 10];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutText["Number of timed-out packets: "L];
    DiagnosticsOps.PutLongNumber[echoResults.timeouts, 10];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutText["Number of late packets: "L];
    DiagnosticsOps.PutLongNumber[echoResults.late, 10];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutText["Number of bad packets: "L];
    DiagnosticsOps.PutLongNumber[echoResults.bad, 10];
    DiagnosticsOps.PutCR[];
    DiagnosticsOps.PutText[
      "Percent of packets transmitted that were received correctly: "L];
    DiagnosticsOps.PutLongNumber[
      IF echoResults.totalAttempts = 0 THEN 0
      ELSE (echoResults.successes * 100) / echoResults.totalAttempts, 10];
    DiagnosticsOps.PutLine["%"L];
    DiagnosticsOps.PutCR[]};

  PrintFeedback: CommOnlineDiagnostics.EventReporter =
    BEGIN
    -- If NOT showEchoes Then just Return
    IF showEchoes THEN
      DiagnosticsOps.PutChar[
        SELECT event FROM
          success => '!,
          late => '#,
          timeout => '?,
          badDataGoodCRC => '~,
          sizeChange => ':,
          unexpected => '*,
          ENDCASE => '@];
    END;

  END.