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

        public override int GetExecutionTime(ExperimentSpecification experimentSpecification)
        {
            const string STRLOG_MethodName = "GetExecutionTime";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            // Typecast the specification so that it can be used here
            Specification specification = (Specification)experimentSpecification;

            //
            // Log the specification
            //
            string logMessage = string.Empty;
            //Logfile.Write(logMessage);

            //
            // Initialise variables used in the state machine
            //
            double executionTime = 0.0;

            try
            {
                //
                // First, check to see if the LabEquipment is online
                //
                LabEquipmentStatus labEquipmentStatus = this.equipmentServiceProxy.GetLabEquipmentStatus();
                if (labEquipmentStatus.online == false)
                {
                    throw new Exception(labEquipmentStatus.statusMessage);
                }

                //
                // Get the time until the LabEquipment is ready to use
                //
                executionTime = this.equipmentServiceProxy.GetTimeUntilReady();

                //
                // Run the state machine to determine the execution time for the experiment specification
                //
                States_GetExecutionTime state = States_GetExecutionTime.sCompleted;
                if (smTable_GetExecutionTime.Length > 0)
                {
                    state = smTable_GetExecutionTime[0].currentState;
                }
                while (state != States_GetExecutionTime.sCompleted)
                {
                    //
                    // Find table entry
                    //
                    int index = -1;
                    for (int i = 0; i < smTable_GetExecutionTime.Length; i++)
                    {
                        if (smTable_GetExecutionTime[i].currentState == state)
                        {
                            // Entry found
                            index = i;
                            break;
                        }
                    }
                    if (index == -1)
                    {
                        throw new ArgumentOutOfRangeException(state.ToString(), STRERR_StateNotFound);
                    }

                    //
                    // Get table entry and save next state
                    //
                    SMTableEntry_GetExecutionTime entry     = smTable_GetExecutionTime[index];
                    States_GetExecutionTime       nextState = entry.nextState;

                    logMessage = " [ " + STRLOG_MethodName + ": " + entry.currentState.ToString() + " ]";
                    Logfile.Write(logMessage);
                    Trace.WriteLine(logMessage);

                    //
                    // Add command arguments where required
                    //
                    switch (entry.currentState)
                    {
                    case States_GetExecutionTime.sGetStartACDriveTime:
                        entry.commandArguments[0, 1] = specification.SetupId;
                        break;

                    case States_GetExecutionTime.sGetStopACDriveTime:
                        entry.commandArguments[0, 1] = specification.SetupId;
                        break;

                    default:
                        break;
                    }

                    //
                    // Execute command and check response success
                    //
                    XmlDocument xmlRequestDocument = CreateXmlRequestDocument(entry.equipmentCommand, entry.commandArguments);
                    string      xmlResponse        = this.equipmentServiceProxy.ExecuteRequest(xmlRequestDocument.InnerXml);
                    XmlNode     xmlResponseNode    = CreateXmlResponseNode(xmlResponse);
                    if (XmlUtilities.GetBoolValue(xmlResponseNode, LabServerEngine.Consts.STRXML_RspSuccess, false) == false)
                    {
                        //
                        // Command execution failed
                        //
                        string errorMessage = XmlUtilities.GetXmlValue(xmlResponseNode, LabServerEngine.Consts.STRXML_RspErrorMessage, true);
                        throw new ArgumentException(errorMessage);
                    }

                    //
                    // Extract response values where required
                    //
                    double stateExecutionTime = 0.0;
                    switch (entry.currentState)
                    {
                    case States_GetExecutionTime.sGetResetACDriveTime:
                        stateExecutionTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspResetACDriveTime, 0);
                        break;

                    case States_GetExecutionTime.sGetConfigureACDriveTime:
                        stateExecutionTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspConfigureACDriveTime, 0);
                        break;

                    case States_GetExecutionTime.sGetStartACDriveTime:
                        stateExecutionTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspStartACDriveTime, 0);
                        break;

                    case States_GetExecutionTime.sGetTakeMeasurementTime:
                        double measurementTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspTakeMeasurementTime, 0);
                        stateExecutionTime = this.measurementCount * measurementTime;
                        break;

                    case States_GetExecutionTime.sGetStopACDriveTime:
                        stateExecutionTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspStopACDriveTime, 0);
                        break;

                    case States_GetExecutionTime.sGetReconfigureACDriveTime:
                        stateExecutionTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspConfigureACDriveTime, 0);
                        break;

                    default:
                        break;
                    }

                    Trace.WriteLine("stateExecutionTime: " + stateExecutionTime.ToString());

                    //
                    // Update the execution time so far
                    //
                    executionTime += stateExecutionTime;

                    //
                    // Next state
                    //
                    state = nextState;
                }
            }
            catch (Exception ex)
            {
                Logfile.WriteError(ex.Message);
                throw;
            }

            logMessage = STRLOG_ExecutionTime + executionTime.ToString();

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName, logMessage);

            return((int)executionTime);
        }
        //-------------------------------------------------------------------------------------------------//

        public override int GetExecutionTime(ExperimentSpecification experimentSpecification)
        {
            const string STRLOG_MethodName = "GetExecutionTime";

            Logfile.WriteCalled(STRLOG_ClassName, STRLOG_MethodName);

            // Typecast the specification so that it can be used here
            Specification specification = (Specification)experimentSpecification;

            //
            // Log the specification
            //
            string strAbsorberList = null;

            for (int i = 0; i < specification.AbsorberList.Length; i++)
            {
                if (i > 0)
                {
                    strAbsorberList += Consts.CHR_CsvSplitter.ToString();
                }
                strAbsorberList += specification.AbsorberList[i].name;
            }
            string logMessage = STRLOG_Absorber + strAbsorberList;

            logMessage += Logfile.STRLOG_Spacer + STRLOG_Duration + specification.Duration.ToString();
            logMessage += Logfile.STRLOG_Spacer + STRLOG_Repeat + specification.Repeat.ToString();
            Logfile.Write(logMessage);

            //
            // Initialise variables used in the state machine
            //
            double executionTime          = 0.0;
            double lcdWriteLineTime       = 0.0;
            int    tubeHomeDistance       = 0;
            int    absorberIndex          = 0;
            char   absorberHomeLocation   = (char)0;
            double lastAbsorberSelectTime = 0.0;
            double absorberSelectHomeTime = 0.0;

            try
            {
                //
                // First, check to see if the LabEquipment is online
                //
                LabEquipmentStatus labEquipmentStatus = this.equipmentServiceProxy.GetLabEquipmentStatus();
                if (labEquipmentStatus.online == false)
                {
                    throw new Exception(labEquipmentStatus.statusMessage);
                }

                //
                // Get the time until the LabEquipment is ready to use
                //
                executionTime = this.equipmentServiceProxy.GetTimeUntilReady();

                //
                // Run the state machine to determine the execution time for the experiment specification
                //
                States_GetExecutionTime state = States_GetExecutionTime.sCompleted;
                if (smTable_GetExecutionTime.Length > 0)
                {
                    state = smTable_GetExecutionTime[0].currentState;
                }
                while (state != States_GetExecutionTime.sCompleted)
                {
                    //
                    // Find table entry
                    //
                    int index = -1;
                    for (int i = 0; i < smTable_GetExecutionTime.Length; i++)
                    {
                        if (smTable_GetExecutionTime[i].currentState == state)
                        {
                            // Entry found
                            index = i;
                            break;
                        }
                    }
                    if (index == -1)
                    {
                        throw new ArgumentOutOfRangeException(state.ToString(), STRERR_StateNotFound);
                    }

                    //
                    // Get table entry and save next state
                    //
                    SMTableEntry_GetExecutionTime entry     = smTable_GetExecutionTime[index];
                    States_GetExecutionTime       nextState = entry.nextState;

                    Trace.Write(" [ " + entry.currentState.ToString() + ": " + entry.currentState.ToString());

                    //
                    // Add command arguments where required
                    //
                    switch (entry.currentState)
                    {
                    case States_GetExecutionTime.sGetTubeMoveTime:
                        entry.commandArguments[0, 1] = tubeHomeDistance.ToString();
                        entry.commandArguments[1, 1] = specification.DistanceList[0].ToString();
                        break;

                    case States_GetExecutionTime.sGetSourceSelectTime:
                        entry.commandArguments[0, 1] = specification.SourceLocation.ToString();
                        break;

                    case States_GetExecutionTime.sGetAbsorberSelectHomeTime:
                        entry.commandArguments[0, 1] = absorberHomeLocation.ToString();
                        break;

                    case States_GetExecutionTime.sGetAbsorberSelectTime:
                        entry.commandArguments[0, 1] = specification.AbsorberList[absorberIndex].location.ToString();
                        break;

                    case States_GetExecutionTime.sGetCaptureDataTime:
                        entry.commandArguments[0, 1] = specification.Duration.ToString();
                        break;

                    case States_GetExecutionTime.sGetAbsorberReturnTime:
                        entry.commandArguments[0, 1] = specification.AbsorberList[absorberIndex].location.ToString();
                        break;

                    case States_GetExecutionTime.sGetSourceReturnTime:
                        entry.commandArguments[0, 1] = specification.SourceLocation.ToString();
                        break;

                    case States_GetExecutionTime.sGetTubeReturnTime:
                        entry.commandArguments[0, 1] = specification.DistanceList[0].ToString();
                        entry.commandArguments[1, 1] = tubeHomeDistance.ToString();
                        break;

                    default:
                        break;
                    }

                    //
                    // Execute command and check response success
                    //
                    XmlDocument xmlRequestDocument = CreateXmlRequestDocument(entry.equipmentCommand, entry.commandArguments);
                    string      xmlResponse        = this.equipmentServiceProxy.ExecuteRequest(xmlRequestDocument.InnerXml);
                    XmlNode     xmlResponseNode    = CreateXmlResponseNode(xmlResponse);
                    if (XmlUtilities.GetBoolValue(xmlResponseNode, LabServerEngine.Consts.STRXML_RspSuccess, false) == false)
                    {
                        //
                        // Command execution failed
                        //
                        string errorMessage = XmlUtilities.GetXmlValue(xmlResponseNode, LabServerEngine.Consts.STRXML_RspErrorMessage, true);
                        throw new ArgumentException(errorMessage);
                    }

                    //
                    // Extract response values where required
                    //
                    double stateExecutionTime = 0.0;
                    switch (entry.currentState)
                    {
                    case States_GetExecutionTime.sGetLcdWriteLineTime:
                        lcdWriteLineTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspLcdWriteLineTime, 0);

                        // Time to ready LCD when completed
                        stateExecutionTime = lcdWriteLineTime * 2;
                        break;

                    case States_GetExecutionTime.sGetTubeHomeDistance:
                        tubeHomeDistance = XmlUtilities.GetIntValue(xmlResponseNode, Consts.STRXML_RspTubeHomeDistance, 0);
                        break;

                    case States_GetExecutionTime.sGetTubeMoveTime:
                        stateExecutionTime  = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspTubeMoveTime, 0.0);
                        stateExecutionTime += lcdWriteLineTime * 2;
                        break;

                    case States_GetExecutionTime.sGetSourceSelectTime:
                        stateExecutionTime  = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspSourceSelectTime, 0.0);
                        stateExecutionTime += lcdWriteLineTime * 2;
                        break;

                    case States_GetExecutionTime.sGetAbsorberHomeLocation:
                        absorberHomeLocation = XmlUtilities.GetCharValue(xmlResponseNode, Consts.STRXML_RspAbsorberHomeLocation);
                        break;

                    case States_GetExecutionTime.sGetAbsorberSelectHomeTime:
                        absorberSelectHomeTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspAbsorberSelectTime, 0.0);
                        break;

                    case States_GetExecutionTime.sGetAbsorberSelectTime:
                        double absorberSelectTime = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspAbsorberSelectTime, 0.0);
                        if (absorberIndex == 0)
                        {
                            // Time to select the first absorber
                            stateExecutionTime = absorberSelectTime;
                        }
                        else
                        {
                            // Calulate time to move to the next absorber
                            stateExecutionTime = absorberSelectTime - lastAbsorberSelectTime + absorberSelectHomeTime;
                        }
                        stateExecutionTime += lcdWriteLineTime * 2;

                        // Save absorber select time for next iteration
                        lastAbsorberSelectTime = absorberSelectTime;
                        break;

                    case States_GetExecutionTime.sGetCaptureDataTime:
                        stateExecutionTime  = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspCaptureDataTime, 0.0);
                        stateExecutionTime += lcdWriteLineTime * 2;
                        stateExecutionTime *= specification.Repeat;
                        break;

                    case States_GetExecutionTime.sGetAbsorberReturnTime:
                        if (++absorberIndex < specification.AbsorberList.Length)
                        {
                            // Next absorber
                            nextState = States_GetExecutionTime.sGetAbsorberSelectTime;
                        }
                        else
                        {
                            // Only want return time for last absorber
                            stateExecutionTime  = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspAbsorberReturnTime, 0.0);
                            stateExecutionTime += lcdWriteLineTime * 2;
                        }
                        break;

                    case States_GetExecutionTime.sGetSourceReturnTime:
                        stateExecutionTime  = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspSourceReturnTime, 0.0);
                        stateExecutionTime += lcdWriteLineTime * 2;
                        break;

                    case States_GetExecutionTime.sGetTubeReturnTime:
                        stateExecutionTime  = XmlUtilities.GetRealValue(xmlResponseNode, Consts.STRXML_RspTubeMoveTime, 0.0);
                        stateExecutionTime += lcdWriteLineTime * 2;
                        break;

                    default:
                        break;
                    }

                    Trace.WriteLine("  nextState: " + entry.nextState.ToString() + " ]");
                    Trace.WriteLine(" stateExecutionTime: " + stateExecutionTime.ToString());

                    //
                    // Update the execution time so far
                    //
                    executionTime += stateExecutionTime;

                    //
                    // Next state
                    //
                    state = nextState;
                }
            }
            catch (Exception ex)
            {
                Logfile.WriteError(ex.Message);
                throw;
            }

            //
            // Round execution time to the nearest integer
            //
            int execTime = (int)(executionTime + 0.5);

            logMessage = STRLOG_ExecutionTime + execTime.ToString();

            Logfile.WriteCompleted(STRLOG_ClassName, STRLOG_MethodName, logMessage);

            return(execTime);
        }