/** @return */
  public boolean Create() {
    final String methodName = "Create";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    boolean success = false;

    try {
      /*
       * Create local class instances just to check that all is in order
       */
      LabExperimentResult labExperimentResult =
          new LabExperimentResult(this.labManagement.getLabConfiguration());

      /*
       * Create instances of the experiment engines
       */
      this.labExperimentEngines = new LabExperimentEngine[this.labManagement.getFarmSize()];
      for (int unitId = 0; unitId < this.labManagement.getFarmSize(); unitId++) {
        this.labExperimentEngines[unitId] = new LabExperimentEngine(unitId, labManagement);
        if (this.labExperimentEngines[unitId] == null) {
          throw new NullPointerException(LabExperimentEngine.class.getSimpleName());
        }
      }

      success = true;
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    Logfile.WriteCompleted(
        logLevel, STR_ClassName, methodName, String.format(STRLOG_Success_arg, success));

    return success;
  }
  /**
   * @param configPropertiesFilename
   * @throws Exception
   */
  public LabExperimentManager(LabManagement labManagement) throws Exception {
    final String methodName = "LabExperimentManager";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    try {
      /*
       * Check that parameters are valid
       */
      if (labManagement == null) {
        throw new NullPointerException(LabManagement.class.getSimpleName());
      }

      /*
       * Save to local variables
       */
      this.labManagement = labManagement;

      /*
       * Initialise locals
       */
      this.stopRunning = false;
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
      throw ex;
    }

    Logfile.WriteCompleted(logLevel, STR_ClassName, methodName);
  }
  public void Close() {
    final String methodName = "Close";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    try {
      if (this.labExperimentEngines == null) {
        throw new NullPointerException(STRERR_LabExperimentEngines);
      }

      /*
       * Check each experiment engine to see if it is running an experiment
       */
      for (int unitId = 0; unitId < this.labManagement.getFarmSize(); unitId++) {
        /*
         * Get the experiment engine
         */
        LabExperimentEngine labExperimentEngine = this.labExperimentEngines[unitId];
        if (labExperimentEngine == null) {
          throw new NullPointerException(
              String.format(STRERR_LabExperimentEngineUnitId_arg, unitId));
        }

        /*
         * Shutdown experiment engine
         */
        labExperimentEngine.Close();
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    /*
     * Shutdown experiment manager
     */
    if (this.running == true) {
      this.stopRunning = true;

      /*
       * Lab experiment manager thread may be waiting for an experiment submission signal
       */
      this.labManagement.getSignalSubmitted().Notify();

      /*
       * Wait for LabExperimentManager thread to terminate
       */
      try {
        this.thread.join();
      } catch (InterruptedException ex) {
        Logfile.WriteError(ex.toString());
      }
    }

    Logfile.WriteCompleted(logLevel, STR_ClassName, methodName);
  }
  /**
   * @param xmlSpecification
   * @param userGroup
   * @return
   */
  public ValidationReport Validate(String xmlSpecification, String userGroup) {
    final String methodName = "Validate";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    ValidationReport validationReport = null;

    try {
      if (this.labExperimentEngines == null) {
        throw new NullPointerException(STRERR_LabExperimentEngines);
      }

      /*
       * Pass to the each experiment engine in turn to validate until validation is successful.
       * It may be possible that the LabEquipment for a particular LabExperimentEngine may be offline.
       */
      for (int i = 0; i < this.labExperimentEngines.length; i++) {
        LabExperimentEngine labExperimentEngine = this.labExperimentEngines[i];
        if (labExperimentEngine == null) {
          throw new NullPointerException(String.format(STRERR_LabExperimentEngineUnitId_arg, 0));
        }

        /*
         * Check if the LabExperimentEngine is online before trying to use it to validate
         */
        LabStatus labStatus = labExperimentEngine.GetLabStatus();
        if (labStatus.isOnline() == false) {
          continue;
        }

        /*
         * Validate the specification
         */
        validationReport = labExperimentEngine.Validate(xmlSpecification);
        if (validationReport.isAccepted() == true) {
          break;
        }
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
      validationReport = new ValidationReport(ex.toString());
    }

    Logfile.WriteCompleted(
        logLevel,
        STR_ClassName,
        methodName,
        String.format(
            STRLOG_Accepted_arg,
            (validationReport != null) ? validationReport.isAccepted() : false));

    return validationReport;
  }
  /**
   * @param experimentId
   * @param sbName
   * @return
   */
  public synchronized boolean Cancel(int experimentId, String sbName) {
    final String methodName = "Cancel";
    Logfile.WriteCalled(
        logLevel,
        STR_ClassName,
        methodName,
        String.format(STRLOG_ExperimentIdSbName_arg2, experimentId, sbName));

    boolean success = false;

    try {
      /*
       * First, try cancelling the experiment on the queue
       */
      if ((success = this.labManagement.getExperimentQueueDB().Cancel(experimentId, sbName))
          == false) {
        if (this.labExperimentEngines == null) {
          throw new NullPointerException(STRERR_LabExperimentEngines);
        }

        /*
         * Experiment may be currently running, find the engine it might be running on and cancel it there
         */
        for (int unitId = 0; unitId < this.labManagement.getFarmSize(); unitId++) {
          /*
           * Get the lab experiment engine
           */
          LabExperimentEngine labExperimentEngine = this.labExperimentEngines[unitId];
          if (labExperimentEngine == null) {
            throw new NullPointerException(
                String.format(STRERR_LabExperimentEngineUnitId_arg, unitId));
          }

          if ((success = labExperimentEngine.Cancel(experimentId, sbName)) == true) {
            break;
          }
        }
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    Logfile.WriteCompleted(
        logLevel, STR_ClassName, methodName, String.format(STRLOG_Success_arg, success));

    return success;
  }
  /** @return */
  private int GetMinRemainingRuntime() {
    final String methodName = "GetMinRemainingRuntime";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    int minRemainingRuntime = Integer.MAX_VALUE;

    try {
      if (this.labExperimentEngines == null) {
        throw new NullPointerException(STRERR_LabExperimentEngines);
      }

      /*
       * Get the remaining runtime for each experiment engine
       */
      for (int unitId = 0; unitId < this.labManagement.getFarmSize(); unitId++) {
        /*
         * Get the experiment engine
         */
        LabExperimentEngine labExperimentEngine = this.labExperimentEngines[unitId];
        if (labExperimentEngine == null) {
          throw new NullPointerException(
              String.format(STRERR_LabExperimentEngineUnitId_arg, unitId));
        }

        /*
         * Check to see if the experiment engine is online
         */
        LabStatus labStatus = labExperimentEngine.GetLabStatus();
        if (labStatus.isOnline() == true) {
          /*
           * Get the remaining runtime for this engine and check if this is a smaller value
           */
          int remainingRuntime = labExperimentEngine.GetRemainingRuntime();
          if (remainingRuntime < minRemainingRuntime) {
            minRemainingRuntime = remainingRuntime;
          }
        }
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    Logfile.WriteCompleted(logLevel, STR_ClassName, methodName);

    return minRemainingRuntime;
  }
  /**
   * @param experimentId
   * @param sbName
   * @return
   */
  public ResultReport RetrieveResult(int experimentId, String sbName) {
    final String methodName = "RetrieveResult";
    Logfile.WriteCalled(
        logLevel,
        STR_ClassName,
        methodName,
        String.format(STRLOG_ExperimentIdSbName_arg2, experimentId, sbName));

    ResultReport resultReport;

    try {
      /*
       * Try getting the result of the completed experiment
       */
      resultReport =
          this.labManagement.getExperimentResultsDB().RetrieveResultReport(experimentId, sbName);
      if (resultReport.getStatusCode() == StatusCodes.Unknown) {
        /*
         * No results found for the experiment, check the queue to see if it ever existed
         */
        ExperimentQueueInfo experimentQueueInfo =
            this.labManagement.getExperimentQueueDB().RetrieveByExperimentId(experimentId, sbName);
        if (experimentQueueInfo != null) {
          resultReport.setStatusCode(experimentQueueInfo.getStatusCode());
        }
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
      resultReport = new ResultReport(StatusCodes.Unknown, ex.toString());
    }

    Logfile.WriteCompleted(
        logLevel,
        STR_ClassName,
        methodName,
        String.format(STRLOG_StatusCode_arg, resultReport.getStatusCode()));

    return resultReport;
  }
  /**
   * @param userGroup
   * @return
   */
  public String GetXmlLabConfiguration(String userGroup) {
    final String methodName = "GetXmlLabConfiguration";
    Logfile.WriteCalled(
        logLevel, STR_ClassName, methodName, String.format(STRLOG_UserGroup_arg, userGroup));

    String xmlLabConfiguration = null;

    try {
      /*
       * Load the lab configuration XML document from the file and convert to a string
       */
      Document document =
          XmlUtilities.GetDocumentFromFile(
              null, this.labManagement.getLabConfiguration().getFilename());
      xmlLabConfiguration = XmlUtilities.ToXmlString(document);
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    Logfile.WriteCompleted(logLevel, STR_ClassName, methodName);

    return xmlLabConfiguration;
  }
  /** @return */
  public boolean Start() {
    final String methodName = "Start";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    boolean success = false;

    try {
      /*
       * Create a new thread and start it
       */
      this.thread = new Thread(this);
      this.thread.start();

      /*
       * Give it a chance to start running and then check that it has started
       */
      for (int i = 0; i < 5; i++) {
        if ((success = this.running) == true) {
          break;
        }

        Delay.MilliSeconds(500);
        System.out.println('?');
      }

      if (success == false) {
        throw new RuntimeException(STRERR_ThreadFailedToStart);
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    Logfile.WriteCompleted(
        logLevel, STR_ClassName, methodName, String.format(STRLOG_Success_arg, success));

    return success;
  }
  @Override
  public void run() {
    final String methodName = "run";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    /*
     * Initialise state machine
     */
    States lastState = States.Done;
    States thisState = States.Init;
    int nextUnit = this.labManagement.getFarmSize() - 1;
    this.running = true;

    /*
     * Allow other threads to check the state of this thread
     */
    Delay.MilliSeconds(500);

    /*
     * State machine loop
     */
    try {
      boolean success;

      while (thisState != States.Done) {
        /*
         * Display message on each state change
         */
        if (thisState != lastState) {
          String logMessage =
              String.format(STRLOG_StateChange_arg2, lastState.toString(), thisState.toString());
          if (debugTrace == true) {
            System.out.println(logMessage);
          }
          //                    Logfile.Write(logLevel, logMessage);

          lastState = thisState;
        }

        switch (thisState) {
          case Init:
            /*
             * Update lab status
             */
            this.online = true;
            this.labStatusMessage = StatusCodes.Ready.toString();

            /*
             * Revert any 'Running' experiments back to 'Waiting' so that they can be run again
             */
            ArrayList<ExperimentQueueInfo> experimentQueueInfoList =
                this.labManagement.getExperimentQueueDB().RetrieveByStatusCode(StatusCodes.Running);
            if (experimentQueueInfoList != null) {
              for (ExperimentQueueInfo experimentQueueInfo : experimentQueueInfoList) {
                success =
                    this.labManagement
                        .getExperimentQueueDB()
                        .UpdateStatus(experimentQueueInfo.getId(), StatusCodes.Waiting);

                /*
                 * Delete the database statistics entry for this experiment
                 */
                ExperimentStatisticsInfo experimentStatisticInfo =
                    this.labManagement
                        .getExperimentStatisticsDB()
                        .RetrieveByExperimentId(
                            experimentQueueInfo.getExperimentId(), experimentQueueInfo.getSbName());
                if (experimentStatisticInfo != null) {
                  this.labManagement
                      .getExperimentStatisticsDB()
                      .Delete(experimentStatisticInfo.getId());
                }

                /*
                 * Delete the database results entry for this experiment
                 */
                ExperimentResultInfo experimentResultInfo =
                    this.labManagement
                        .getExperimentResultsDB()
                        .RetrieveByExperimentId(
                            experimentQueueInfo.getExperimentId(), experimentQueueInfo.getSbName());
                if (experimentResultInfo != null) {
                  this.labManagement.getExperimentResultsDB().Delete(experimentResultInfo.getId());
                }

                String logMessage =
                    String.format(
                        STRLOG_RevertToWaiting_arg,
                        experimentQueueInfo.getExperimentId(),
                        experimentQueueInfo.getSbName(),
                        success);
                Logfile.Write(logMessage);
              }
            }

            /*
             * Check if any experiments have not notified their ServiceBroker
             */
            thisState = States.CheckNotified;
            break;

          case Idle:
            /*
             * Wait for an experiment to be submitted or timeout after a certain time. In either case, check
             * the experiment queue. Maybe an experiment submission signal got missed and it didn't get seen
             * here. It has happened before.
             */
            if (this.labManagement.getSignalSubmitted().Wait(INT_DelayCheckQueueSeconds * 1000)
                == true) {
              /*
               * Check if shutting down
               */
              if (this.stopRunning == true) {
                thisState = States.StopRunning;
                break;
              }

              /*
               * An experiment has been submitted, go check the queue
               */
              this.labManagement.getSignalSubmitted().Reset();
              thisState = States.CheckQueue;
              break;
            }

            /*
             * Timed out, go check some other things
             */
            thisState = States.Maintenance;
            break;

          case CheckQueue:
            /*
             * Check the queue to see if there are any experiments waiting
             */
            if (this.labManagement.getExperimentQueueDB().GetCountWaiting() > 0) {
              thisState = States.StartEngine;
              break;
            }

            thisState = States.Idle;
            break;

          case StartEngine:
            /*
             * Find the available experiment engine to run a waiting experiment
             */
            boolean foundAvailable = false;
            for (int i = 0; i < this.labManagement.getFarmSize(); i++) {
              /*
               * Determine the next experiment engine for running the experiment
               */
              if (++nextUnit == this.labManagement.getFarmSize()) {
                nextUnit = 0;
              }
              if (debugTrace == true) {
                System.out.println(String.format("[nextUnit: %s]", nextUnit));
              }

              /*
               * Get the experiment engine and check to see if it is online
               */
              LabExperimentEngine labExperimentEngine = this.labExperimentEngines[nextUnit];
              LabStatus labStatus = labExperimentEngine.GetLabStatus();
              if (labStatus.isOnline() == false) {
                /*
                 * This one is not online
                 */
                if (debugTrace == true) {
                  System.out.println(String.format("[nextUnit: %s is Offline]", nextUnit));
                }
                continue;
              }

              /*
               * Determine if this experiment engine is currently running
               */
              if (labExperimentEngine.isRunning() == false) {
                /*
                 * Not running, try starting it
                 */
                if (labExperimentEngine.Start() == true) {
                  /*
                   * The available engine has been started, wait a bit before checking the queue
                   */
                  foundAvailable = true;
                  Delay.MilliSeconds(1000);
                  break;
                }
              }
            }

            /*
             * Check if there are any engines available
             */
            if (foundAvailable == false) {
              /*
               * No engines available to start running
               */
              thisState = States.Idle;
              break;
            }

            thisState = States.CheckNotified;
            break;

          case CheckNotified:
            /*
             * Check if any experiments have not notified their ServiceBroker
             */
            success = true;
            ArrayList<ExperimentResultInfo> allNotNotified =
                this.labManagement.getExperimentResultsDB().RetrieveAllNotNotified();
            if (allNotNotified != null) {
              Iterator iterator = allNotNotified.iterator();
              while (iterator.hasNext() && success == true) {
                ExperimentResultInfo experimentResultInfo = (ExperimentResultInfo) iterator.next();
                success =
                    this.labExperimentEngines[0].NotifyServiceBroker(
                        experimentResultInfo.getExperimentId(), experimentResultInfo.getSbName());
              }
            }

            thisState = States.CheckQueue;
            break;

          case Maintenance:
            thisState = States.Idle;
            break;

          case StopRunning:
            thisState = States.Done;
            break;
        }
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
    }

    /*
     * Thread is no longer running
     */
    this.running = false;

    Logfile.WriteCompleted(logLevel, STR_ClassName, methodName);
  }
  /**
   * @param experimentId
   * @param sbName
   * @param xmlSpecification
   * @param userGroup
   * @param priorityHint
   * @return
   */
  public synchronized SubmissionReport Submit(
      int experimentId,
      String sbName,
      String xmlSpecification,
      String userGroup,
      int priorityHint) {
    final String methodName = "Submit";
    Logfile.WriteCalled(
        logLevel,
        STR_ClassName,
        methodName,
        String.format(STRLOG_ExperimentIdSbName_arg2, experimentId, sbName));

    SubmissionReport submissionReport = new SubmissionReport(experimentId);

    /*
     * Validate the experiment specification before submitting
     */
    ValidationReport validationReport = this.Validate(xmlSpecification, userGroup);
    if (validationReport.isAccepted() == true) {
      /*
       * Specification is valid, create an instance of the experiment
       */
      LabExperimentInfo labExperimentInfo = new LabExperimentInfo(experimentId, sbName);
      labExperimentInfo.setXmlSpecification(xmlSpecification);
      labExperimentInfo.setUserGroup(userGroup);
      labExperimentInfo.setPriorityHint(priorityHint);
      labExperimentInfo.setEstimatedExecTime((int) validationReport.getEstRuntime());

      /*
       * Add the experiment to the queue
       */
      try {
        QueuedExperimentInfo queuedExperimentInfo =
            this.labManagement.getExperimentQueueDB().Enqueue(labExperimentInfo);
        if (queuedExperimentInfo == null) {
          throw new RuntimeException(STRERR_FailedToEnqueueExperiment);
        }

        /*
         * Update submission report current queue length and wait time
         */
        WaitEstimate waitEstimate = new WaitEstimate();
        waitEstimate.setEffectiveQueueLength(queuedExperimentInfo.getQueueLength());
        waitEstimate.setEstWait(queuedExperimentInfo.getWaitTime() + this.GetMinRemainingRuntime());
        submissionReport.setWaitEstimate(waitEstimate);

        /*
         * Update the statistics with revised wait estimate
         */
        queuedExperimentInfo.setWaitTime((int) waitEstimate.getEstWait());
        this.labManagement.getExperimentStatisticsDB().Submitted(queuedExperimentInfo);

        /*
         * Tell lab experiment manager thread that an experiment has been submitted
         */
        this.labManagement.getSignalSubmitted().Notify();
      } catch (Exception ex) {
        validationReport = new ValidationReport(ex.getMessage());
      }
    }

    submissionReport.setValidationReport(validationReport);

    Logfile.WriteCompleted(
        logLevel,
        STR_ClassName,
        methodName,
        String.format(STRLOG_Accepted_arg, submissionReport.getValidationReport().isAccepted()));

    return submissionReport;
  }
  /** @return LabStatus */
  public LabStatus GetLabStatus(boolean verbose) {
    final String methodName = "GetLabStatus";
    Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

    LabStatus labStatus = new LabStatus(false, "");

    try {
      if (this.labExperimentEngines == null) {
        throw new NullPointerException(STRERR_LabExperimentEngines);
      }

      /*
       * Check lab status of each experiment engine
       */
      for (int unitId = 0; unitId < this.labManagement.getFarmSize(); unitId++) {
        LabExperimentEngine labExperimentEngine = this.labExperimentEngines[unitId];
        if (labExperimentEngine == null) {
          throw new NullPointerException(
              String.format(STRERR_LabExperimentEngineUnitId_arg, unitId));
        }

        /*
         * Keep a tally, add the LabStatusMessage only if the engine is online
         */
        LabStatus engineLabStatus = labExperimentEngine.GetLabStatus();
        labStatus.setOnline(labStatus.isOnline() || engineLabStatus.isOnline());
        String message;
        if (engineLabStatus.isOnline() == true) {

          /*
           * Check if the engine is currently running an experiment
           */
          int experimentId = labExperimentEngine.getExperimentId();
          if (experimentId > 0) {
            /*
             * It is, so include the experiment Id in the lab status message
             */
            message =
                String.format(
                    STRLOG_UnitIdExperimentIdLabStatusMessage_arg2,
                    unitId,
                    experimentId,
                    engineLabStatus.getLabStatusMessage());
          } else {
            message =
                String.format(
                    STRLOG_UnitIdLabStatusMessage_arg2,
                    unitId,
                    engineLabStatus.getLabStatusMessage());
          }
        } else {
          message =
              (verbose == true)
                  ? String.format(
                      STRLOG_UnitIdLabStatusMessage_arg2,
                      unitId,
                      engineLabStatus.getLabStatusMessage())
                  : "";
        }
        labStatus.setLabStatusMessage(labStatus.getLabStatusMessage() + message);
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
      labStatus.setLabStatusMessage(ex.toString());
    }

    Logfile.WriteCompleted(logLevel, STR_ClassName, methodName);

    return labStatus;
  }
  /**
   * @param experimentId
   * @param sbName
   * @return
   */
  public synchronized LabExperimentStatus GetLabExperimentStatus(int experimentId, String sbName) {
    final String methodName = "GetLabExperimentStatus";
    Logfile.WriteCalled(
        Level.INFO,
        STR_ClassName,
        methodName,
        String.format(STRLOG_ExperimentIdSbName_arg2, experimentId, sbName));

    LabExperimentStatus labExperimentStatus = new LabExperimentStatus();

    try {
      /*
       * Check that parameters are valid
       */
      if (experimentId <= 0) {
        throw new RuntimeException(String.format(STRERR_InvalidExperimentId_arg, experimentId));
      }
      if (sbName == null) {
        throw new NullPointerException(STRERR_SbName);
      }

      /*
       * Get the status of the experiment from the queue
       */
      ExperimentQueueInfo experimentQueueInfo =
          this.labManagement.getExperimentQueueDB().RetrieveByExperimentId(experimentId, sbName);
      if (experimentQueueInfo == null) {
        throw new RuntimeException(
            String.format(STRERR_UnknownExperimentSbNameExperimentId_arg2, sbName, experimentId));
      }

      System.out.println(String.format("StatusCode: %s", experimentQueueInfo.getStatusCode()));

      /*
       * Experiment exists, check status
       */
      switch (experimentQueueInfo.getStatusCode()) {
        case Waiting:
          /*
           * Experiment is waiting on the queue, get the queue position and wait time
           */
          QueuedExperimentInfo queuedExperimentInfo =
              this.labManagement
                  .getExperimentQueueDB()
                  .GetQueuedExperimentInfo(experimentId, sbName);
          WaitEstimate waitEstimate = new WaitEstimate();
          waitEstimate.setEffectiveQueueLength(queuedExperimentInfo.getPosition());
          waitEstimate.setEstWait(
              queuedExperimentInfo.getWaitTime() + this.GetMinRemainingRuntime());

          System.out.println(
              String.format(
                  "WaitEstimate: QueueLength=%d  WaitTime=%f01",
                  waitEstimate.getEffectiveQueueLength(), waitEstimate.getEstWait()));

          /*
           * Set the experiment status and time it takes to run the experiment
           */
          ExperimentStatus experimentStatus =
              new ExperimentStatus(experimentQueueInfo.getStatusCode());
          experimentStatus.setEstRuntime(experimentQueueInfo.getEstimatedExecTime());
          experimentStatus.setEstRemainingRuntime(experimentQueueInfo.getEstimatedExecTime());
          experimentStatus.setWaitEstimate(waitEstimate);

          labExperimentStatus = new LabExperimentStatus(experimentStatus);

          Logfile.Write(
              logLevel,
              String.format(
                  STRLOG_ExperimentStatus_arg4,
                  waitEstimate.getEffectiveQueueLength(),
                  waitEstimate.getEstWait(),
                  experimentStatus.getEstRuntime(),
                  experimentStatus.getEstRemainingRuntime()));

          System.out.println(
              String.format(
                  STRLOG_ExperimentStatus_arg4,
                  waitEstimate.getEffectiveQueueLength(),
                  waitEstimate.getEstWait(),
                  experimentStatus.getEstRuntime(),
                  experimentStatus.getEstRemainingRuntime()));
          break;

        case Running:
          /*
           * Get the experiment status from the lab experiment engine
           */
          LabExperimentEngine labExperimentEngine =
              this.labExperimentEngines[experimentQueueInfo.getUnitId()];
          labExperimentStatus.setExperimentStatus(
              labExperimentEngine.GetExperimentStatus(experimentId, sbName));
          break;

        case Cancelled:
          /*
           * The experiment was cancelled while waiting on the queue
           */
          labExperimentStatus.setExperimentStatus(new ExperimentStatus(StatusCodes.Cancelled));
          break;

        default:
          /*
           * Experiment has completed, cancelled or failed so get the status from the experiment results
           */
          ResultReport resultReport =
              this.labManagement
                  .getExperimentResultsDB()
                  .RetrieveResultReport(experimentId, sbName);
          labExperimentStatus.setExperimentStatus(
              new ExperimentStatus(resultReport.getStatusCode()));
          break;
      }
    } catch (Exception ex) {
      Logfile.WriteError(ex.toString());
      labExperimentStatus.setExperimentStatus(new ExperimentStatus(StatusCodes.Unknown));
    }

    ExperimentStatus experimentStatus = labExperimentStatus.getExperimentStatus();

    Logfile.WriteCompleted(
        Level.INFO,
        STR_ClassName,
        methodName,
        String.format(
            STRLOG_ExperimentStatus_arg3,
            experimentStatus.getStatusCode(),
            experimentStatus.getEstRuntime(),
            experimentStatus.getEstRemainingRuntime()));

    return labExperimentStatus;
  }