//-------------------------------------------------------------------------------------------------//

        private bool PrepareExperiment(ExperimentInfo experimentInfo)
        {
            const string STRLOG_MethodName = "PrepareExperiment";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            bool success = false;

            //
            // Check if the experiment has been cancelled
            //
            if (experimentInfo.cancelled == true)
            {
                //
                // Experiment was cancelled while waiting on the queue
                //
                experimentInfo.resultReport = new ResultReport((int)StatusCodes.Cancelled, STRLOG_QueuedExperimentCancelled);

                Logfile.Write(STRLOG_QueuedExperimentCancelled);
            }
            else
            {
                //
                // Update the statistics for starting the experiment
                //
                DateTime now = DateTime.Now;
                success = this.experimentStatistics.Started(experimentInfo.experimentId, experimentInfo.sbName, this.unitId, now);

                //
                // Create an instance of LabExperimentInfo to execute the experiment
                //
                lock (this.statusLock)
                {
                    experimentInfo.unitId  = this.unitId;
                    experimentInfo.status  = StatusCodes.Running;
                    this.labExperimentInfo = new LabExperimentInfo(experimentInfo, now);
                }
            }

            string logMessage = STRLOG_success + success.ToString();

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName, logMessage);

            return(success);
        }
        //-------------------------------------------------------------------------------------------------//
        private bool ConcludeExperiment(ExperimentInfo experimentInfo)
        {
            const string STRLOG_MethodName = "ConcludeExperiment";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            bool success = false;

            try
            {
                //
                // Update experiment status both here and the queue table
                //
                experimentInfo.status = (StatusCodes)experimentInfo.resultReport.statusCode;
                if (this.experimentQueue.UpdateStatus(experimentInfo.experimentId, experimentInfo.sbName, experimentInfo.status) == false)
                {
                    throw new ArgumentException(STRERR_FailedToUpdateQueueStatus);
                }

                //
                // Save the experiment results
                //
                if (this.experimentResults.Save(experimentInfo) == false)
                {
                    throw new ArgumentException(STRERR_FailedToSaveExperimentResults);
                }

                //
                // Check experiment completion status for updating the statistics
                //
                DateTime now = DateTime.Now;
                if (experimentInfo.status == StatusCodes.Cancelled)
                {
                    // Update statistics for cancelled experiment
                    if (this.experimentStatistics.Cancelled(experimentInfo.experimentId, experimentInfo.sbName, now) == false)
                    {
                        throw new ArgumentException(STRERR_FailedToUpdateStatisticsCancelled);
                    }
                }
                else
                {
                    // Update statistics for completed experiment
                    if (this.experimentStatistics.Completed(experimentInfo.experimentId, experimentInfo.sbName, now) == false)
                    {
                        throw new ArgumentException(STRERR_FailedToUpdateStatisticsCompleted);
                    }

                    //
                    // Determine actual execution time of the experiment
                    //
                    TimeSpan timeSpan = now - this.labExperimentInfo.startDateTime;
                    int executionTime = (int)timeSpan.TotalSeconds;
                    Logfile.Write(STRLOG_ActualExecutionTime + executionTime.ToString() + STRLOG_seconds);
                }

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

            //
            // Experiment is finished
            //
            lock (this.statusLock)
            {
                if (this.labExperimentInfo != null)
                {
                    this.labExperimentInfo.cancelExperiment = null;
                    this.labExperimentInfo = null;
                }
            }

            string logMessage = STRLOG_success + success.ToString();

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName, logMessage);

            return success;
        }
        //-------------------------------------------------------------------------------------------------//
        private bool PrepareExperiment(ExperimentInfo experimentInfo)
        {
            const string STRLOG_MethodName = "PrepareExperiment";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            bool success = false;

            //
            // Check if the experiment has been cancelled
            //
            if (experimentInfo.cancelled == true)
            {
                //
                // Experiment was cancelled while waiting on the queue
                //
                experimentInfo.resultReport = new ResultReport((int)StatusCodes.Cancelled, STRLOG_QueuedExperimentCancelled);

                Logfile.Write(STRLOG_QueuedExperimentCancelled);
            }
            else
            {
                //
                // Update the statistics for starting the experiment
                //
                DateTime now = DateTime.Now;
                success = this.experimentStatistics.Started(experimentInfo.experimentId, experimentInfo.sbName, this.unitId, now);

                //
                // Create an instance of LabExperimentInfo to execute the experiment
                //
                lock (this.statusLock)
                {
                    experimentInfo.unitId = this.unitId;
                    experimentInfo.status = StatusCodes.Running;
                    this.labExperimentInfo = new LabExperimentInfo(experimentInfo, now);
                }
            }

            string logMessage = STRLOG_success + success.ToString();

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName, logMessage);

            return success;
        }
        //-------------------------------------------------------------------------------------------------//
        public LabExperimentEngine(int unitId, AppData appData)
        {
            const string STRLOG_MethodName = "LabExperimentEngine";

            string logMessage = STRLOG_unitId + unitId.ToString();

            Logfile.WriteCalled(null, STRLOG_MethodName, logMessage);

            //
            // Initialise local variables
            //
            this.disposed = true;
            this.unitId = unitId;
            this.labExperimentInfo = null;

            //
            // Initialise private properties
            //
            this.slRunning = false;

            try
            {
                //
                // Initialise local variables from application data
                //
                if (appData == null)
                {
                    throw new ArgumentNullException(STRERR_appData);
                }

                this.allowedServiceBrokers = appData.allowedServiceBrokers;
                if (this.allowedServiceBrokers == null)
                {
                    throw new ArgumentNullException(STRERR_allowedServiceBrokers);
                }

                this.experimentQueue = appData.experimentQueue;
                if (this.experimentQueue == null)
                {
                    throw new ArgumentNullException(STRERR_experimentQueue);
                }

                this.experimentResults = appData.experimentResults;
                if (this.experimentResults == null)
                {
                    throw new ArgumentNullException(STRERR_experimentResults);
                }

                this.experimentStatistics = appData.experimentStatistics;
                if (this.experimentStatistics == null)
                {
                    throw new ArgumentNullException(STRERR_experimentStatistics);
                }

                this.labConfiguration = appData.labConfiguration;
                if (this.labConfiguration == null)
                {
                    throw new ArgumentNullException(STRERR_labConfiguration);
                }

                this.signalCompleted = appData.signalCompleted;
                if (this.signalCompleted == null)
                {
                    throw new ArgumentNullException(STRERR_signalCompleted);
                }

                //
                // Try to get a proxy to the equipment service, it may not exist
                //
                this.equipmentServiceProxy = null;
                this.equipmentServiceAPI = null;
                try
                {
                    this.equipmentServiceAPI = new EquipmentServiceAPI(this.unitId);
                    this.equipmentServiceProxy = this.equipmentServiceAPI.EquipmentServiceProxy;
                    if (this.equipmentServiceProxy != null)
                    {
                        this.equipmentServiceProxy.GetTimeUntilReady();
                    }
                }
                catch
                {
                    // No equipment service available
                }

                //
                // Save email addresses for experiment completion notification
                //
                this.emailAddressLabServer = appData.emailAddressLabServer;
                this.emailAddressesExperimentCompleted = appData.emailAddressesExperimentCompleted;
                this.emailAddressesExperimentFailed = appData.emailAddressesExperimentFailed;

                //
                // Create thread objects
                //
                this.statusLock = new Object();
                if (this.statusLock == null)
                {
                    throw new ArgumentNullException(STRERR_statusLock);
                }
                this.threadLabExperimentEngine = new Thread(new ThreadStart(LabExperimentEngineThread));
                if (this.threadLabExperimentEngine == null)
                {
                    throw new ArgumentNullException(STRERR_threadLabExperimentEngine);
                }

                //
                // Don't start the thread yet, the method Start() must be called to start the thread
                // after the derived class has completed its initialisation.
                //
                Logfile.Write(STRLOG_LabExperimentEngineIsReady + STRLOG_unitId + this.unitId.ToString());
            }
            catch (Exception ex)
            {
                // Log the message and throw the exception back to the caller
                Logfile.WriteError(ex.Message);
                throw;
            }

            Logfile.WriteCompleted(null, STRLOG_MethodName);
        }
        //-------------------------------------------------------------------------------------------------//

        public LabExperimentEngine(int unitId, AppData appData)
        {
            const string STRLOG_MethodName = "LabExperimentEngine";

            string logMessage = STRLOG_unitId + unitId.ToString();

            Logfile.WriteCalled(null, STRLOG_MethodName, logMessage);

            //
            // Initialise local variables
            //
            this.disposed          = true;
            this.unitId            = unitId;
            this.labExperimentInfo = null;

            //
            // Initialise private properties
            //
            this.slRunning = false;

            try
            {
                //
                // Initialise local variables from application data
                //
                if (appData == null)
                {
                    throw new ArgumentNullException(STRERR_appData);
                }

                this.allowedServiceBrokers = appData.allowedServiceBrokers;
                if (this.allowedServiceBrokers == null)
                {
                    throw new ArgumentNullException(STRERR_allowedServiceBrokers);
                }

                this.experimentQueue = appData.experimentQueue;
                if (this.experimentQueue == null)
                {
                    throw new ArgumentNullException(STRERR_experimentQueue);
                }

                this.experimentResults = appData.experimentResults;
                if (this.experimentResults == null)
                {
                    throw new ArgumentNullException(STRERR_experimentResults);
                }

                this.experimentStatistics = appData.experimentStatistics;
                if (this.experimentStatistics == null)
                {
                    throw new ArgumentNullException(STRERR_experimentStatistics);
                }

                this.labConfiguration = appData.labConfiguration;
                if (this.labConfiguration == null)
                {
                    throw new ArgumentNullException(STRERR_labConfiguration);
                }

                this.signalCompleted = appData.signalCompleted;
                if (this.signalCompleted == null)
                {
                    throw new ArgumentNullException(STRERR_signalCompleted);
                }

                //
                // Try to get a proxy to the equipment service, it may not exist
                //
                this.equipmentServiceProxy = null;
                this.equipmentServiceAPI   = null;
                try
                {
                    this.equipmentServiceAPI   = new EquipmentServiceAPI(this.unitId);
                    this.equipmentServiceProxy = this.equipmentServiceAPI.EquipmentServiceProxy;
                    if (this.equipmentServiceProxy != null)
                    {
                        this.equipmentServiceProxy.GetTimeUntilReady();
                    }
                }
                catch
                {
                    // No equipment service available
                }

                //
                // Save email addresses for experiment completion notification
                //
                this.emailAddressLabServer             = appData.emailAddressLabServer;
                this.emailAddressesExperimentCompleted = appData.emailAddressesExperimentCompleted;
                this.emailAddressesExperimentFailed    = appData.emailAddressesExperimentFailed;

                //
                // Create thread objects
                //
                this.statusLock = new Object();
                if (this.statusLock == null)
                {
                    throw new ArgumentNullException(STRERR_statusLock);
                }
                this.threadLabExperimentEngine = new Thread(new ThreadStart(LabExperimentEngineThread));
                if (this.threadLabExperimentEngine == null)
                {
                    throw new ArgumentNullException(STRERR_threadLabExperimentEngine);
                }

                //
                // Don't start the thread yet, the method Start() must be called to start the thread
                // after the derived class has completed its initialisation.
                //
                Logfile.Write(STRLOG_LabExperimentEngineIsReady + STRLOG_unitId + this.unitId.ToString());
            }
            catch (Exception ex)
            {
                // Log the message and throw the exception back to the caller
                Logfile.WriteError(ex.Message);
                throw;
            }

            Logfile.WriteCompleted(null, STRLOG_MethodName);
        }
        //-------------------------------------------------------------------------------------------------//

        private bool ConcludeExperiment(ExperimentInfo experimentInfo)
        {
            const string STRLOG_MethodName = "ConcludeExperiment";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            bool success = false;

            try
            {
                //
                // Update experiment status both here and the queue table
                //
                experimentInfo.status = (StatusCodes)experimentInfo.resultReport.statusCode;
                if (this.experimentQueue.UpdateStatus(experimentInfo.experimentId, experimentInfo.sbName, experimentInfo.status) == false)
                {
                    throw new ArgumentException(STRERR_FailedToUpdateQueueStatus);
                }

                //
                // Save the experiment results
                //
                if (this.experimentResults.Save(experimentInfo) == false)
                {
                    throw new ArgumentException(STRERR_FailedToSaveExperimentResults);
                }

                //
                // Check experiment completion status for updating the statistics
                //
                DateTime now = DateTime.Now;
                if (experimentInfo.status == StatusCodes.Cancelled)
                {
                    // Update statistics for cancelled experiment
                    if (this.experimentStatistics.Cancelled(experimentInfo.experimentId, experimentInfo.sbName, now) == false)
                    {
                        throw new ArgumentException(STRERR_FailedToUpdateStatisticsCancelled);
                    }
                }
                else
                {
                    // Update statistics for completed experiment
                    if (this.experimentStatistics.Completed(experimentInfo.experimentId, experimentInfo.sbName, now) == false)
                    {
                        throw new ArgumentException(STRERR_FailedToUpdateStatisticsCompleted);
                    }

                    //
                    // Determine actual execution time of the experiment
                    //
                    TimeSpan timeSpan      = now - this.labExperimentInfo.startDateTime;
                    int      executionTime = (int)timeSpan.TotalSeconds;
                    Logfile.Write(STRLOG_ActualExecutionTime + executionTime.ToString() + STRLOG_seconds);
                }

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

            //
            // Experiment is finished
            //
            lock (this.statusLock)
            {
                if (this.labExperimentInfo != null)
                {
                    this.labExperimentInfo.cancelExperiment = null;
                    this.labExperimentInfo = null;
                }
            }

            string logMessage = STRLOG_success + success.ToString();

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName, logMessage);

            return(success);
        }