//-------------------------------------------------------------------------------------------------//
        /// <summary>
        /// Parse the XML specification string to check its validity. No exceptions are thrown back to the
        /// calling method. If an error occurs, 'accepted' is set to false and the error message is placed
        /// in 'errorMessage' where it can be examined by the calling method. Return 'accepted'.
        /// </summary>
        /// <param name="xmlSpecification"></param>
        public virtual ValidationReport Parse(string xmlSpecification)
        {
            const string STRLOG_MethodName = "Parse";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            //
            // Create a new validation report ready to fill in
            //
            ValidationReport validationReport = new ValidationReport();

            //
            // Process the XML specification string
            //
            try
            {
                //
                // Load XML specification string and get the setup id
                //
                XmlDocument xmlDocument = XmlUtilities.GetXmlDocument(xmlSpecification);
                this.xmlNodeSpecification = XmlUtilities.GetXmlRootNode(xmlDocument, Consts.STRXML_ExperimentSpecification);
                this.setupId = XmlUtilities.GetXmlValue(this.xmlNodeSpecification, Consts.STRXML_SetupId, false);

                //
                // Create an instance of the driver for the specified setup and then
                // get the driver's execution time for this specification
                //
                int executionTime = -1;
                if (this.setupId.Equals(Consts.STRXML_SetupId_DriverGeneric))
                {
                    DriverGeneric driver = new DriverGeneric(this.xmlNodeEquipmentConfig, this);
                    executionTime = driver.GetExecutionTime();
                }

                //
                // Specification is valid
                //
                validationReport.estRuntime = executionTime;
                validationReport.accepted = true;
            }
            catch (Exception ex)
            {
                validationReport.errorMessage = ex.Message;
            }

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName);

            return validationReport;
        }
        //-------------------------------------------------------------------------------------------------//
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xmlSpecification"></param>
        /// <returns>ExecutionStatus</returns>
        public ExecutionStatus StartExecution(String xmlSpecification)
        {
            const string methodName = "StartExecution";
            Logfile.WriteCalled(logLevel, STR_ClassName, methodName);

            ExecutionStatus executionStatus;

            try
            {
                /*
                 * Check if an experiment is already running
                 */
                if (this.driver != null)
                {
                    executionStatus = this.driver.GetExecutionStatus();
                    if (executionStatus.ExecuteStatus != ExecutionStatus.Status.Completed)
                    {
                        throw new ApplicationException(STRERR_AlreadyExecuting);
                    }
                }

                /*
                 * Create an instance of LabExperimentSpecification and get the setup Id
                 */
                LabExperimentSpecification labExperimentSpecification = LabExperimentSpecification.XmlParse(xmlSpecification);

                /*
                 * Get the driver for the setup Id and validate the experiment specification
                 */
                this.driver = this.GetDriver(labExperimentSpecification.SetupId);
                Validation validation = this.driver.Validate(xmlSpecification);
                if (validation.Accepted == false)
                {
                    throw new ApplicationException(validation.ErrorMessage);
                }

                /*
                 * Generate a random number for the execution Id
                 */
                Random random = new Random();
                this.driver.ExecutionId = random.Next();

                /*
                 * Start the driver executing but this may require powering up the equipment first
                 */
                if (this.SuspendPowerdown() == false)
                {
                    throw new ApplicationException(STRERR_FailedToStartExecution);
                }

                /*
                 * Tell the thread that there is an experiment to execute
                 */
                this.signalStartExecution.Notify();

                /*
                 * Get the execution status and update the execution time including the time until the LabEquipment is ready
                 */
                executionStatus = this.driver.GetExecutionStatus();
                executionStatus.TimeRemaining = executionStatus.TimeRemaining + this.GetTimeUntilReady();

            }
            catch (Exception ex)
            {
                Logfile.WriteError(ex.Message);
                executionStatus = new ExecutionStatus();
                executionStatus.ResultStatus = ExecutionStatus.Status.Failed;
                executionStatus.ErrorMessage = ex.Message;
            }

            Logfile.WriteCompleted(logLevel, STR_ClassName, methodName,
                    String.Format(STRLOG_ExecutionStatus_arg5, executionStatus.ExecutionId,
                    Enum.GetName(typeof(ExecutionStatus.Status), executionStatus.ExecuteStatus),
                    Enum.GetName(typeof(ExecutionStatus.Status), executionStatus.ResultStatus),
                    executionStatus.TimeRemaining, executionStatus.ErrorMessage));

            return executionStatus;
        }
        //-------------------------------------------------------------------------------------------------//
        /// <summary>
        /// 
        /// </summary>
        /// <param name="executionId"></param>
        /// <returns></returns>
        public ExecutionStatus GetExecutionStatus(int executionId)
        {
            const string methodName = "GetExecutionStatus";
            Logfile.WriteCalled(logLevel, STR_ClassName, methodName,
                    String.Format(STRLOG_ExecutionId_arg, executionId));

            ExecutionStatus executionStatus = new ExecutionStatus();

            try
            {
                if (this.driver != null)
                {
                    /*
                     * Get the execution status and check the execution Id
                     */
                    executionStatus = this.driver.GetExecutionStatus();
                    if (executionStatus.ExecutionId != executionId && executionStatus.ExecutionId != 0)
                    {
                        throw new ApplicationException(String.Format(STRERR_InvalidExecutionId_arg, executionId));
                    }

                    /*
                     * Check if the experiment has completed
                     */
                    if (executionStatus.ExecuteStatus.Equals(ExecutionStatus.Status.Completed) == true)
                    {
                        /*
                         * Check if the experiment has completed successfully
                         */
                        if (executionStatus.ResultStatus.Equals(ExecutionStatus.Status.Completed) == false)
                        {
                            /*
                             * The driver is no longer needed
                             */
                            this.driver = null;
                        }

                        /*
                         * Experiment has completed but the results are yet to be retrieved
                         */
                        this.ResumePowerdown();
                    }
                    else
                    {
                        /*
                         *  Update the execution time including the time until the LabEquipment is ready
                         */
                        executionStatus.TimeRemaining = executionStatus.TimeRemaining + this.GetTimeUntilReady();
                    }
                }
            }
            catch (Exception ex)
            {
                Logfile.WriteError(ex.Message);
            }

            Logfile.WriteCompleted(logLevel, STR_ClassName, methodName,
                    String.Format(STRLOG_ExecutionStatus_arg5, executionStatus.ExecutionId,
                    Enum.GetName(typeof(ExecutionStatus.Status), executionStatus.ExecuteStatus),
                    Enum.GetName(typeof(ExecutionStatus.Status), executionStatus.ResultStatus),
                    executionStatus.TimeRemaining, executionStatus.ErrorMessage));

            return executionStatus;
        }
        //-------------------------------------------------------------------------------------------------//
        /// <summary>
        /// 
        /// </summary>
        /// <param name="executionId"></param>
        /// <returns>string</returns>
        public string GetExperimentResults(int executionId)
        {
            const string methodName = "GetExperimentResults";
            Logfile.WriteCalled(logLevel, STR_ClassName, methodName,
                    String.Format(STRLOG_ExecutionId_arg, executionId));

            string experimentResults = null;

            try
            {
                if (this.driver != null)
                {
                    /*
                     * Get the execution status and check the execution Id
                     */
                    ExecutionStatus executionStatus = this.driver.GetExecutionStatus();
                    if (executionStatus.ExecutionId != executionId)
                    {
                        throw new ApplicationException(String.Format(STRERR_InvalidExecutionId_arg, executionId));
                    }

                    /*
                     * Get the results from the driver
                     */
                    experimentResults = this.driver.GetExperimentResults();

                    /*
                     * The driver is no longer needed
                     */
                    this.driver = null;
                }
            }
            catch (Exception ex)
            {
                Logfile.WriteError(ex.Message);
            }

            Logfile.WriteCompleted(logLevel, STR_ClassName, methodName,
                    experimentResults);

            return experimentResults;
        }
        //-------------------------------------------------------------------------------------------------//
        /// <summary>
        /// 
        /// </summary>
        /// <param name="setupId"></param>
        /// <returns>DriverGeneric</returns>
        protected virtual DriverGeneric GetDriver(String setupId)
        {
            const string methodName = "GetDriver";
            Logfile.WriteCalled(logLevel, STR_ClassName, methodName,
                    String.Format(STRLOG_SetupId_arg, setupId));

            DriverGeneric driverGeneric = null;

            /*
             * Create an instance of the driver for the specified setup Id
             */
            switch (setupId)
            {
                case LabConsts.STRXML_SetupId_Generic:
                    driverGeneric = new DriverGeneric(this.labEquipmentConfiguration);
                    break;
                default:
                    throw new ApplicationException(String.Format(STRERR_InvalidSetupId_arg, setupId));
            }

            Logfile.WriteCompleted(logLevel, STR_ClassName, methodName,
                    driverGeneric.DriverName);

            return driverGeneric;
        }