Function Engineering_Services(@Service, @Params)
/***********************************************************************************************************************

    Name        :   Engineering_Services

    Description :   Handler program for all Disposition services.

    Notes       :   Application errors should be logged using the Error Services module. There are a few methodological
                    assumptions built into way errors are managed which are important to understand in order to properly
                    work with Error Services:

                    - The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to
                      the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger
                      this will appear backwards since the originating procedure always appears at the bottom of the
                      list and the current routine appears at the top of the list. We are using this orientation because
                      it is common to refer to the process of calling other procedures as 'drilling down'.

                    - The reason for defining the orientation of the call stack is because Error_Services allows for
                      multiple error conditions to be appended to an original error. In most cases this will happen when
                      a procedure at the bottom of the stack generates an error condition and then returns to its
                      calling procedure. This higher level procedure can optionally add more information relevant to
                      itself. This continues as the call stack 'bubbles' its way back to the top to where the
                      originating procedure is waiting.

                    - Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus)
                      preserve their error state until explicitly cleared. This can hinder the normal execution of code
                      since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists.
                      Our philosophy is that error conditions should automatically be cleared before a new procedure
                      is executed to avoid this problem. However, the nature of Basic+ does not make this easy to
                      automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our
                      philosophy then it should include a call into the 'Clear' service request at the top of the
                      program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.)

                    - Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any
                      error conditions that were set before.

    Parameters  :
        Service         [in] -- Name of the service being requested
        Param1-10   [in/out] -- Additional request parameter holders
        Response       [out] -- Response to be sent back to the Controller (MCP) or requesting procedure

    Metadata    :

    History     :   (Date, Initials, Notes)
        08/24/20    djs     Original programmer.
														

***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler

$insert SERVICE_SETUP
$insert REACT_RUN_EQUATES
$insert WO_WFR_EQUATES
$insert RUN_STAGE_WFR_EQUATES
$insert APP_INSERTS
$insert RLIST_EQUATES
$insert EA_REQUESTS_EQUATES
$insert FILE_SERVICES_EQUATES

Declare subroutine  Error_Services, Database_Services, GaN_Services, Disposition_Services, Engineering_Services, RList
Declare subroutine  Excel_Services, Set_Status, Engineering_Services, Btree.Extract
Declare subroutine  Logging_Services, File_Services, GetTempPath
Declare function    Database_Services, Error_Services, DateTime, GaN_Services, Engineering_Services, Excel_Services
Declare function    Engineering_Services, Environment_Services, Logging_Services, File_Services, SQL_Services
Declare Function    Math_Services

LogPath          = Environment_Services('GetApplicationRootPath') : '\LogFiles\EngineeringAnalysis'
LogDate          = Oconv(Date(), 'D4/')
LogTime          = Oconv(Time(), 'MTS')
LogFileName      = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' PostToExcelLog.csv'
Headers          = 'Logging DTM' : @FM : 'RDS Key ID' : @FM : 'Notes'
objLog           = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
QueueLogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' QueueLog.csv'
QueueObjLog      = Logging_Services('NewLog', LogPath, QueueLogFileName, CRLF$, Comma$, Headers, '', False$, False$)
SQLLogFileName   = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' SQL Response Log.csv'
Headers          = 'Logging DTM' : @FM : 'RDS Key ID' : @FM : 'SQL Response'
objSQLLog        = Logging_Services('NewLog', LogPath, SQLLogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LoggingDTM       = LogDate : ' ' : LogTime
LogData          = ''

GoToService else
	Error_Services('Set', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
end

Return Response else ''

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
* Options STAGES        = 'GROWTH','UV','WARP','CAN','XRD','AFM','BV'
* Options BV_PARAMETERS = 'Breakdown Voltage','Breakdown Voltage - Edge','Breakdown Voltage - Middle'

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Service GetSpreadSheetInfo(RDSNo)

    If RDSNo NE '' then
        
        ReactRunRec  = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        WONo         = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor      = ReactRunRec<REACT_RUN_REACTOR$>
        Voltage      = Xlate('WO_LOG', WONo, 'GAN_COST_TYPE', 'X')
        EpiStructure = Xlate('WO_LOG', WONo, 'EPI_STRUCTURE', 'X')
        Begin Case
            Case (Reactor EQ '69')                        
                SpreadsheetName = '8-inch New HV Epi Development.xlsx'
                WorksheetName   = '8inch HELENS'
                Password        = 'IRMN2714'
                Directory       = '\\messv02ecc1.ec.local\EC_GaN_Development\R69 EPI DEVELOPMENT\DAILY RUN RECORD\'
                Offset          = 18
                EndCol          = 'CC'
                
            Case ( (Reactor EQ '71') and ( (Voltage _EQC 'Mid Voltage') or (EpiStructure EQ 'MV') ) )
                SpreadsheetName = 'MV_R71_6-inch Epi Development.xlsx'
                WorksheetName   = 'R71-MV_With CAN02'
                Password        = 'IRMN2714'
                Directory       = '\\messv02ecc1.ec.local\EC_GaN_Development\R71 EPI DEVELOPMENT\Daily Run Record\'
                Offset          = 16
                EndCol          = 'CF'
                
            Case ( (Reactor EQ '71') and ( (Voltage _EQC 'High Voltage') or (EpiStructure EQ 'HV') ) )
                SpreadsheetName = 'HV_R71_6-inch Epi Development.xlsx'
                WorksheetName   = 'HV_with_CAN02'
                Password        = 'IRMN2714'
                Directory       = '\\messv02ecc1.ec.local\EC_GaN_Development\R71 EPI DEVELOPMENT\Daily Run Record\'
                Offset          = 18
                EndCol          = 'CG'
                
            Case Otherwise$
                ErrorMsg = 'Error in service ':Service:'. Failed to determine Excel Spreadsheet information.'
                Error_Services('Add', ErrorMsg)
        End Case
    end
    If Error_Services('NoError') then
        Response<1> = SpreadsheetName
        Response<2> = WorksheetName
        Response<3> = Password
        Response<4> = Directory
        Response<5> = Offset
        Response<6> = EndCol
    end
    
end service


Service GetSpreadSheets()

    SpreadsheetNames = ''
    WorksheetNames   = ''
    Passwords        = ''
    Directories      = ''
    Offsets          = ''
    EndCols          = ''
                    
    SpreadsheetNames<0, -1> = '8-inch New HV Epi Development.xlsx'
    WorksheetNames<0, -1>   = '8inch HELENS'
    Passwords<0, -1>        = 'IRMN2714'
    Offsets<0, -1>          = 18
    Directories<0, -1>      = '\\messv02ecc1.ec.local\EC_GaN_Development\R69 EPI DEVELOPMENT\DAILY RUN RECORD\'
    EndCols<0, -1>          = 'CC'

    SpreadsheetNames<0, -1> = 'MV_R71_6-inch Epi Development.xlsx'
    WorksheetNames<0, -1>   = 'R71-MV_With CAN02'
    Passwords<0, -1>        = 'IRMN2714'
    Offsets<0, -1>          = 16
    Directories<0, -1>      = '\\messv02ecc1.ec.local\EC_GaN_Development\R71 EPI DEVELOPMENT\Daily Run Record\'
    EndCols<0, -1>          = 'CF'

    SpreadsheetNames<0, -1> = 'HV_R71_6-inch Epi Development.xlsx'
    WorksheetNames<0, -1>   = 'HV_with_CAN02'
    Passwords<0, -1>        = 'IRMN2714'
    Offsets<0, -1>          = 18
    Directories<0, -1>      = '\\messv02ecc1.ec.local\EC_GaN_Development\R71 EPI DEVELOPMENT\Daily Run Record\'
    EndCols<0, -1>          = 'CG'

    If Error_Services('NoError') then
        Response<1> = SpreadsheetNames
        Response<2> = WorksheetNames
        Response<3> = Passwords
        Response<4> = Directories
        Response<5> = Offsets
        Response<6> = EndCols
    end
    
end service


Service PostGrowthData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then       
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo, True$, 0, False$)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        InWfrIDs            = ReactRunRec<REACT_RUN_IN_WFR_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>
        LoadDTM             = ReactRunRec<REACT_RUN_LOAD_SIG_DTM$>
        LoadDT              = OConv(LoadDTM[1, 'F.'], 'D/')
        NewRun              = False$        
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
        EndCol              = SpreadsheetInfo<6>
        
        // i.e. LOAD -> write the recipe, scribes, and satellite IDs to the appropriate spreadsheet
        If SpreadsheetRowIndex EQ '' then
            // RunID does not yet exist in the spreadsheet, so assign the next available row in the
            // worksheet to the REACT_RUN record
            SpreadsheetRowIndex                  = Database_Services('ReadDataRow', 'APP_INFO', WorksheetName)
            ReactRunRec<REACT_RUN_EA_ROW_INDEX$> = SpreadsheetRowIndex           
            NewRun                               = True$                                  
        end       
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then            
                RunInfo    = GaN_Services('GetRunInfo', GaNRunID)
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = RunInfo
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                If ( (RunInfo NE '') and (RunInfo NE 0) ) then
                    Pockets      = RunInfo<3>
                    Scribes      = RunInfo<4>
                    SatIDs       = RunInfo<5> 
                    DocumentPath = SpreadsheetDir:SpreadsheetName
                    FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                    If FileHandle NE FILE_ERROR$ then
                        // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                        Datetime      = OConv(Datetime(), 'DT')
                        Swap ' ' with '-' in Datetime
                        Swap ':' with ';' in Datetime
                        TempFilename  = GaNRunID:'-':Datetime:'.xlsx'                    
                        TempDirectory = Str(\00\, 1024)
                        GetTempPath(Len(TempDirectory), TempDirectory)
                        Convert \00\ to '' in TempDirectory                       
                        TempPath      = TempDirectory:TempFilename
                        File_Services('CopyFile', DocumentPath, TempPath)                  
                        DocObj        = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                        If Error_Services('NoError') then
                            // Write the Run Recipe, Scribes, and Satellite IDs to the spreadsheet
                            // Verify spreadsheet row index is correct
                            RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                            RowRunID      = RunIDCellText[1, 12]
                            If ( (RowRunID EQ GaNRunID) or (NewRun EQ True$) ) then
                                // Verified row index is correct or run is not yet in spreadsheet, so continue.
                                If NewRun EQ True$ then
                                    // Copy formatted cell range (i.e. the cell "block")
                                    BeginRow       = SpreadsheetRowIndex
                                    CellRangeBegin = 'A':BeginRow 
                                    EndRow         = SpreadsheetRowIndex + (Offset - 1)
                                    CellRangeEnd   = EndCol:EndRow
                                    CopyToCell     = 'A':(SpreadsheetRowIndex + Offset)
                                    Excel_Services('CopyCellRange', DocObj, WorksheetName, CellRangeBegin, CellRangeEnd, CopyToCell)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex, GaNRunID)
                                end                                
                                // Run Recipe
                                Excel_Services('SetCellValue', DocObj, WorksheetName, 'D', SpreadsheetRowIndex, RunRecipe)                                
                                If WorksheetName EQ 'R71-MV_With CAN02' then
                                    DateCol   = 'Z'
                                    DateCol2  = 'AA'                                
                                    SatCol    = 'AB'
                                    ScribeCol = 'AC'
                                end else
                                    DateCol   = 'AA'
                                    DateCol2  = 'AB'
                                    SatCol    = 'AC'
                                    ScribeCol = 'AD'                            
                                end
                                // Unload Date
                                DateRow = (SpreadsheetRowIndex + 9)
                                Excel_Services('SetCellValue', DocObj, WorksheetName, DateCol, DateRow, 'Date')
                                Excel_Services('SetCellValue', DocObj, WorksheetName, DateCol2, DateRow, LoadDT)
                                // Scribes and Satellite IDs                            
                                For each Scribe in Scribes using @VM setting wPos
                                    SatID         = SatIDs<0, wPos>
                                    WaferRowIndex = SpreadsheetRowIndex + (wPos - 1)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, ScribeCol, WaferRowIndex, Scribe)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, SatCol, WaferRowIndex, SatID)
                                Next Scribe
                                Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                If Error_Services('NoError') then
                                    TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                    TempFileHandleCopy = TempFileHandle
                                    TempFileSize       = 0
                                    If FileHandle NE FILE_ERROR$ then
                                        // We were able to get the lock
                                        TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                        File_Services('UnlockFile', TempFileHandle)
                                    end
                                    Begin Case
                                        Case TempFileHandleCopy EQ FILE_ERROR$
                                            // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                            ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                        Case TempFileSize EQ 0
                                            // Save failed - file corrupted
                                            ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                        Case Otherwise$
                                            // Save successful, so copy temp file back to document (PROD) path.
                                            File_Services('UnlockFile', FileHandle)
                                            CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                            If CopySucceeded EQ False$ then
                                                ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                            end else
                                                If NewRun EQ True$ then
                                                    // Advance APP_INFO next row index for the worksheet
                                                    NextSpreadsheetRowIndex = SpreadsheetRowIndex + (Offset + 1)
                                                    Database_Services('WriteDataRow', 'APP_INFO', WorksheetName, NextSpreadsheetRowIndex, True$, False$, True$)
                                                    // Save REACT_RUN record with row index.
                                                    Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
                                                end    
                                            end                                            
                                    End Case    
                                end else
                                    // SaveDocument call failed. Get internal error for logging purposes.
                                    ErrorMessage = Error_Services('GetMessage') 
                                end                                                                
                            end else
                                // Row index did not match. Log error.
                                ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                Excel_Services('CloseDocument', DocObj)       
                            end                                                                                           
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'                              
                        end
                        OSDelete TempPath                                     
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                       
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. Run info for Run ID ':GaNRunID:' is not available in IQS at this time.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostAFMData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then       
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>        
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then            
                AFMData    = Engineering_Services('GetAFMData', RDSNo)
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = AFMData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                If ( (AFMData NE '') and (AFMData NE 0) ) then
                    DocumentPath = SpreadsheetDir:SpreadsheetName
                    FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                    If FileHandle NE FILE_ERROR$ then
                        // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                        Datetime     = OConv(Datetime(), 'DT')
                        Swap ' ' with '-' in Datetime
                        Swap ':' with ';' in Datetime
                        TempFilename = GaNRunID:'-':Datetime:'.xlsx'                       
                        TempDirectory = Str(\00\, 1024)
                        GetTempPath(Len(TempDirectory), TempDirectory)
                        Convert \00\ to '' in TempDirectory                       
                        TempPath     = TempDirectory:TempFilename
                        File_Services('CopyFile', DocumentPath, TempPath)                  
                        DocObj       = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                        If Error_Services('NoError') then                            
                            // Verify spreadsheet row index is correct
                            RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                            RowRunID      = RunIDCellText[1, 12]
                            If RowRunID EQ GaNRunID then
                                // Verified row index is correct so continue.
                                // Write the AFM Pit Count and AFM Roughness to the spreadsheet                               
                                If WorksheetName EQ 'R71-MV_With CAN02' then
                                    PitCountCol  = 'Z'
                                    RoughnessCol = 'AA'
                                end else
                                    PitCountCol  = 'AA'
                                    RoughnessCol = 'AB'                            
                                end                 
                                For each Row in AFMData using @FM setting fPos
                                    Parameter = Row<0, 1>
                                    Pocket    = Row<0, 2>
                                    Value     = Row<0, 3>
                                    Begin Case
                                        Case Parameter EQ 'AFM Pit Count'
                                            Col = PitCountCol
                                        Case Parameter EQ 'AFM Roughness'
                                            Col = RoughnessCol
                                        Case Otherwise$
                                            ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                    End Case
                                Until ErrorMessage NE ''
                                    RowIndex = SpreadsheetRowIndex + (Pocket - 1)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                Next Row
                                If ErrorMessage EQ '' then
                                    Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                    If Error_Services('NoError') then
                                        TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                        TempFileHandleCopy = TempFileHandle
                                        TempFileSize       = 0
                                        If FileHandle NE FILE_ERROR$ then
                                            // We were able to get the lock
                                            TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                            File_Services('UnlockFile', TempFileHandle)
                                        end
                                        Begin Case
                                            Case TempFileHandleCopy EQ FILE_ERROR$
                                                // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                            Case TempFileSize EQ 0
                                                // Save failed - file corrupted
                                                ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                            Case Otherwise$
                                                // Save successful, so copy temp file back to document (PROD) path.
                                                File_Services('UnlockFile', FileHandle)
                                                CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                If CopySucceeded EQ False$ then
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                end
                                        End Case    
                                    end else
                                        // SaveDocument call failed. Get internal error for logging purposes.
                                        ErrorMessage = Error_Services('GetMessage') 
                                    end
                                end                                                            
                            end else
                                // Row index did not match. Log error.
                                ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                Excel_Services('CloseDocument', DocObj)       
                            end                                                                                    
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':TempPath:' file could not be opened by Excel_Services.'   
                        end
                        OSDelete TempPath                                                                                                
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. AFM data for Run ID ':GaNRunID:' is not available in IQS at this time.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'    
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument')
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostCHARData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then       
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>
        InWfrIDs            = ReactRunRec<REACT_RUN_IN_WFR_ID$>  
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>        
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then                           
                DestroyedList = ''
                For each WfrID in InWfrIDs using @VM setting wPos
                    WaferDestroyed = GaN_Services('GetDestroyedStatus', WfrID)
                    If WaferDestroyed EQ True$ then DestroyedList<0, -1> = wPos
                Next WfrID
                LogData<1>   = LoggingDTM
                LogData<2>   = RDSNo
                LogData<3>   = DestroyedList
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                DocumentPath = SpreadsheetDir:SpreadsheetName
                FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                If FileHandle NE FILE_ERROR$ then
                    // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                    Datetime      = OConv(Datetime(), 'DT')
                    Swap ' ' with '-' in Datetime
                    Swap ':' with ';' in Datetime
                    TempFilename  = GaNRunID:'-':Datetime:'.xlsx'                       
                    TempDirectory = Str(\00\, 1024)
                    GetTempPath(Len(TempDirectory), TempDirectory)
                    Convert \00\ to '' in TempDirectory                       
                    TempPath      = TempDirectory:TempFilename
                    File_Services('CopyFile', DocumentPath, TempPath)                  
                    DocObj        = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                    If Error_Services('NoError') then                            
                        // Verify spreadsheet row index is correct
                        RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                        RowRunID      = RunIDCellText[1, 12]
                        If RowRunID EQ GaNRunID then
                            // Verified row index is correct so continue.                             
                            If WorksheetName EQ 'R71-MV_With CAN02' then
                                ScribeCol = 'AC'
                                RowIndex = SpreadsheetRowIndex + Offset - 1
                            end else    
                                ScribeCol = 'AD'
                                RowIndex = SpreadsheetRowIndex + Offset - 3
                            end
                            CharString = ''
                            If DestroyedList NE '' then     
                                For each WfrPos in DestroyedList using @VM
                                    CharString := 'W0':WfrPos:','
                                Next WfrPos
                            end else
                                CharString = '#N/A'
                            end
                            // Erase last comma
                            CharString[-1, 1] = ''
                            Excel_Services('SetCellValue', DocObj, WorksheetName, ScribeCol, RowIndex, CharString)
                            // Attempt to save changes to spreadsheet
                            Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                            If Error_Services('NoError') then
                                TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                TempFileHandleCopy = TempFileHandle
                                TempFileSize       = 0
                                If FileHandle NE FILE_ERROR$ then
                                    // We were able to get the lock
                                    TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                    File_Services('UnlockFile', TempFileHandle)
                                end
                                Begin Case
                                    Case TempFileHandleCopy EQ FILE_ERROR$
                                        // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                        ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                    Case TempFileSize EQ 0
                                        // Save failed - file corrupted
                                        ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                    Case Otherwise$
                                        // Save successful, so copy temp file back to document (PROD) path.
                                        File_Services('UnlockFile', FileHandle)
                                        CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                        If CopySucceeded EQ False$ then
                                            ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                        end
                                End Case    
                            end else
                                // SaveDocument call failed. Get internal error for logging purposes.
                                ErrorMessage = Error_Services('GetMessage') 
                            end                                                                
                        end else
                            // Row index did not match. Log error.
                            ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                            ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                            ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                            Excel_Services('CloseDocument', DocObj)       
                        end                                                                                               
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'   
                    end
                    OSDelete TempPath                                  
                end else
                    ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'    
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument')
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostRPMData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then       
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then
                RPMData    = Engineering_Services('GetRPMData', RDSNo)
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = RPMData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                If ( Error_Services('NoError') and (RPMData NE '') and (RPMData NE 0) ) then
                    PLData = Engineering_Services('GetPLData', RDSNo)
                    If ( Error_Services('NoError') and (PLData NE '') and (PLData NE 0) ) then  
                        DocumentPath = SpreadsheetDir:SpreadsheetName
                        FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                        If FileHandle NE FILE_ERROR$ then
                            // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                            Datetime      = OConv(Datetime(), 'DT')
                            Swap ' ' with '-' in Datetime
                            Swap ':' with ';' in Datetime
                            TempFilename  = GaNRunID:'-':Datetime:'.xlsx'                       
                            TempDirectory = Str(\00\, 1024)
                            GetTempPath(Len(TempDirectory), TempDirectory)
                            Convert \00\ to '' in TempDirectory                       
                            TempPath      = TempDirectory:TempFilename
                            File_Services('CopyFile', DocumentPath, TempPath)                  
                            DocObj        = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                            If Error_Services('NoError') then                            
                                // Verify spreadsheet row index is correct
                                RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                                RowRunID      = RunIDCellText[1, 12]
                                If RowRunID EQ GaNRunID then
                                    // Verified row index is correct so continue.
                                    Begin Case
                                        Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                            PLEdgeCol       = 'AY'
                                            PLYellowCol     = 'AZ'
                                            PLRatioCol      = 'AZ'
                                            RPMThicknessCol = 'BA'
                                            RPMStdDevCol    = 'BB'                                      
                                        Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                            PLEdgeCol       = 'AZ'
                                            PLYellowCol     = 'BA'
                                            PLRatioCol      = 'BA'
                                            RPMThicknessCol = 'BB'
                                            RPMStdDevCol    = 'BC'
                                        Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                            PLEdgeCol       = 'BC'
                                            PLYellowCol     = 'BD'
                                            PLRatioCol      = 'BD'
                                            RPMThicknessCol = 'BE'
                                            RPMStdDevCol    = 'BF'                                   
                                    End Case
                                    // Write constant values
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, PLEdgeCol, (SpreadsheetRowIndex + 1), 'Ratio')                           
                                    // Write RPM values                           
                                    RowIndex = SpreadsheetRowIndex
                                    For each Row in RPMData using @FM setting fPos
                                        Parameter = Row<0, 1>
                                        Value     = Row<0, 2>
                                        Begin Case
                                            Case Parameter EQ 'Epi Thickness Mean'
                                                Col = RPMThicknessCol                                           
                                            Case Parameter EQ 'Epi Thickness Std Dev %'
                                                Col = RPMStdDevCol
                                        End Case                                    
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                    Next Row                                  
                                    // Write PL values                                                                
                                    For each Row in PLData using @FM setting fPos
                                        Parameter = Row<0, 1>
                                        Value     = Row<0, 2>
                                        Begin Case
                                            Case Parameter EQ 'PL Ratio'
                                                Col = PLRatioCol
                                                RowIndex = SpreadsheetRowIndex + 1                                      
                                            Case Parameter EQ 'BandEdge_V'
                                                RowIndex = SpreadsheetRowIndex
                                                Col = PLEdgeCol
                                            Case Parameter EQ 'YellowBand_V'
                                                RowIndex = SpreadsheetRowIndex
                                                Col = PLYellowCol
                                            Case Otherwise$
                                                ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                        End Case
                                    Until ErrorMessage NE ''                         
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                    Next Row
                                    If ErrorMessage EQ '' then              
                                        Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                        If Error_Services('NoError') then
                                            TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                            TempFileHandleCopy = TempFileHandle
                                            TempFileSize       = 0
                                            If FileHandle NE FILE_ERROR$ then
                                                // We were able to get the lock
                                                TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                                File_Services('UnlockFile', TempFileHandle)
                                            end
                                            Begin Case
                                                Case TempFileHandleCopy EQ FILE_ERROR$
                                                    // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                                Case TempFileSize EQ 0
                                                    // Save failed - file corrupted
                                                    ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                                Case Otherwise$
                                                    // Save successful, so copy temp file back to document (PROD) path.
                                                    File_Services('UnlockFile', FileHandle)
                                                    CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                    If CopySucceeded EQ False$ then
                                                        ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                    end
                                            End Case    
                                        end else
                                            // SaveDocument call failed. Get internal error for logging purposes.
                                            ErrorMessage = Error_Services('GetMessage') 
                                        end
                                    end                                                        
                                end else
                                    // Row index did not match. Log error.
                                    ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                    ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                    ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                    Excel_Services('CloseDocument', DocObj)       
                                end                                                                                      
                            end else
                                ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'   
                            end
                            OSDelete TempPath                                     
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                        end
                    end else
                        ErrorMessage = 'Error in ':Service:' service. PL data missing in IQS.'    
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. RPM data missing in IQS.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'        
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'      
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostPRData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then       
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then
                PRData     = Engineering_Services('GetPRData', RDSNo)
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = PRData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                If ( Error_Services('NoError') and (PRData NE '') and (PRData NE 0) ) then            
                    DocumentPath = SpreadsheetDir:SpreadsheetName
                    FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                    If FileHandle NE FILE_ERROR$ then
                        // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                        Datetime     = OConv(Datetime(), 'DT')
                        Swap ' ' with '-' in Datetime
                        Swap ':' with ';' in Datetime
                        TempFilename = GaNRunID:'-':Datetime:'.xlsx'                       
                        TempDirectory = Str(\00\, 1024)
                        GetTempPath(Len(TempDirectory), TempDirectory)
                        Convert \00\ to '' in TempDirectory                       
                        TempPath     = TempDirectory:TempFilename
                        File_Services('CopyFile', DocumentPath, TempPath)                  
                        DocObj       = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                        If Error_Services('NoError') then                            
                            // Verify spreadsheet row index is correct
                            RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                            RowRunID      = RunIDCellText[1, 12]
                            If RowRunID EQ GaNRunID then
                                // Verified row index is correct so continue.
                                Begin Case
                                    Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                        PRPeakCol        = 'BC'
                                        PRBarrierCompCol = 'BD'
                                        PRStdDevCol      = 'BD'
                                        NumValues        = 9
                                    Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                        PRPeakCol        = 'BD'
                                        PRBarrierCompCol = 'BE'
                                        NumValues        = 5
                                    Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                        PRPosCol         = 'BG'
                                        PRBarrierCompCol = 'BH'
                                        NumValues        = 7                                   
                                End Case
                                // Write constant values
                                Excel_Services('SetCellValue', DocObj, WorksheetName, PLEdgeCol, (SpreadsheetRowIndex + 1), 'Ratio')                           
                                // Write PR values
                                PeakCounter        = 0
                                PRBarrCompCounter  = 0
                                RPMBarrCompCounter = 0
                                PRBarrCompValues   = ''
                                YCoordCounter      = 0
                                BarrierMax         = 0
                                BarrierMin         = 0
                                BarrierRange       = 0
                                For each Row in PRData using @FM setting fPos
                                    Parameter = Row<0, 1>
                                    SBNo      = Row<0, 2>
                                    Value     = Row<0, 3>
                                    Begin Case
                                        Case Parameter EQ 'PR Peak'
                                            Col          = PRPeakCol
                                            If SBNo GT 0 then
                                                RowOffset = PeakCounter
                                            end else
                                                RowOffset = 9
                                            end
                                            PeakCounter += 1                                           
                                        Case Parameter EQ 'PR Barrier Composition'
                                            Col                      = PRBarrierCompCol
                                            If SBNo GT 0 then
                                                RowOffset = PRBarrCompCounter
                                            end else
                                                RowOffset = 9
                                            end
                                            If ( (SBNo NE 0) and (Value NE '') ) then PRBarrCompValues<0, -1> = Value
                                            PRBarrCompCounter += 1                                           
                                        Case Parameter EQ 'Barrier_Composition_RPM_XY'
                                            Col       = PRBarrierCompCol
                                            RowOffset = RPMBarrCompCounter
                                            If BarrierMax EQ 0 then BarrierMax = Value
                                            If BarrierMin EQ 0 then BarrierMin = Value
                                            If Value GT BarrierMax then BarrierMax = Value
                                            If Value LT BarrierMin then BarrierMin = Value
                                            If ( (SpreadsheetName EQ '8-inch New HV Epi Development.xlsx') and (SBNo EQ 0) ) then
                                                // Write the Barrier Comp average
                                                BarrierAvg = Value
                                                RowIndex   = NumValues 
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, BarrierAvg)
                                            end
                                            RPMBarrCompCounter += 1
                                        Case Parameter EQ 'Y-Coord'
                                            Col            = PRPosCol
                                            RowOffset      = YCoordCounter
                                            YCoordCounter += 1
                                        Case Otherwise$
                                            ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'                        
                                    End Case
                                Until ErrorMessage NE ''                                                     
                                    RowIndex  = SpreadsheetRowIndex + RowOffset
                                    If ( (Parameter NE 'Y-Coord') or (SBNo GT 0) ) then
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                    end                                   
                                Next Row
                                If ErrorMessage EQ '' then
                                    If SpreadsheetName EQ '8-inch New HV Epi Development.xlsx' then
                                        RowIndex     = SpreadsheetRowIndex + NumValues + 1
                                        BarrierRange = BarrierMax - BarrierMin
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, PRBarrierCompCol, RowIndex, BarrierRange)                                                  
                                    end
                                    If SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx' then
                                        RowIndex = SpreadsheetRowIndex + 10
                                        PRCV     = Math_Services('GetCoefficientOfVariation', PRBarrCompValues, 'SAMPLE')
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, PRStdDevCol, RowIndex, PRCV)       
                                    end
                                    Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                    If Error_Services('NoError') then
                                        TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                        TempFileHandleCopy = TempFileHandle
                                        TempFileSize       = 0
                                        If FileHandle NE FILE_ERROR$ then
                                            // We were able to get the lock
                                            TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                            File_Services('UnlockFile', TempFileHandle)
                                        end
                                        Begin Case
                                            Case TempFileHandleCopy EQ FILE_ERROR$
                                                // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                            Case TempFileSize EQ 0
                                                // Save failed - file corrupted
                                                ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                            Case Otherwise$
                                                // Save successful, so copy temp file back to document (PROD) path.
                                                File_Services('UnlockFile', FileHandle)
                                                CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                If CopySucceeded EQ False$ then
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                end
                                        End Case    
                                    end else
                                        // SaveDocument call failed. Get internal error for logging purposes.
                                        ErrorMessage = Error_Services('GetMessage') 
                                    end                                                                  
                                end
                            end else
                                // Row index did not match. Log error.
                                ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                Excel_Services('CloseDocument', DocObj)       
                            end                                                                                     
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.' 
                        end
                        OSDelete TempPath                                     
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                       
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. PR data missing in IQS.'    
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'    
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostXRDData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>             
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then
                // Log SQL response
                LogData<1>       = LoggingDTM
                LogData<2>       = RDSNo
                XRDAlumData      = Engineering_Services('GetXRDAlumData', RDSNo)                
                LogData<3>       = XRDAlumData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                XRDThicknessData = Engineering_Services('GetXRDThicknessData', RDSNo)
                LogData<3>       = XRDThicknessData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                If SpreadsheetName NE '8-inch New HV Epi Development.xlsx' then
                    // 6-inch
                    XRDCoordData = Engineering_Services('GetXRDXCoordData', RDSNo)
                    LogData<3>   = XRDCoordData
                    Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                    XRDWtAvgData = Engineering_Services('GetXRDWtAvgData', RDSNo)
                    LogData<3>   = XRDWtAvgData
                    Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                end else
                    // 8-inch
                    XRDCoordData = Engineering_Services('GetXRDYCoordData', RDSNo)
                    LogData<3>   = XRDCoordData
                    Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                end                
                If ( Error_Services('NoError') and (XRDAlumData NE '') and (XRDThicknessData NE '') and (XRDCoordData NE '') and (XRDAlumData NE 0) and (XRDThicknessData NE 0) and (XRDCoordData NE 0) ) then
                    If SpreadsheetName NE '8-inch New HV Epi Development.xlsx' then
                        XRDWtAvgData = Engineering_Services('GetXRDWtAvgData', RDSNo)
                        LogData<3>   = XRDWtAvgData
                        Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                        If ( (XRDWtAvgData EQ 0) or (XRDWtAvgData EQ '') ) then
                            ErrorMessage = 'Error in service':Service:'. XRD data missing in IQS.'
                        end
                    end
                    If ErrorMessage EQ '' then
                        DocumentPath = SpreadsheetDir:SpreadsheetName
                        FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                        If FileHandle NE FILE_ERROR$ then
                            // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                            Datetime      = OConv(Datetime(), 'DT')
                            Swap ' ' with '-' in Datetime
                            Swap ':' with ';' in Datetime
                            TempFilename  = GaNRunID:'-':Datetime:'.xlsx'                       
                            TempDirectory = Str(\00\, 1024)
                            GetTempPath(Len(TempDirectory), TempDirectory)
                            Convert \00\ to '' in TempDirectory                       
                            TempPath      = TempDirectory:TempFilename
                            File_Services('CopyFile', DocumentPath, TempPath)                  
                            DocObj        = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                            If Error_Services('NoError') then                            
                                // Verify spreadsheet row index is correct
                                RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                                RowRunID      = RunIDCellText[1, 12]
                                If RowRunID EQ GaNRunID then
                                    Begin Case
                                        Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                            BBCol         = 'BE'
                                            BarrierCol    = 'BF'
                                            SLSCol        = 'BG'
                                            TL1Col        = 'BH'
                                            TL0Col        = 'BI'
                                            AVECol        = 'BJ'
                                            SLPeriodCol   = 'BK'
                                            ThickCoordCol = 'AP'
                                            ThicknessCol  = 'AQ'
                                            FWHM006Col    = 'AW'
                                            FWHM105Col    = 'AX'
                                            NumValues     = 3
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, AVECol, (SpreadsheetRowIndex + 3), 'AVE')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, TL0Col, (SpreadsheetRowIndex + 6), 'Coordinate')                                       
                                        Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                            BBCol         = 'BF'
                                            BarrierCol    = 'BG'
                                            SLSCol        = 'BH'
                                            TL1Col        = 'BI'
                                            TL0Col        = 'BJ'
                                            AVECol        = 'BK'
                                            SLPeriodCol   = 'BL'
                                            ThickCoordCol = 'AQ'
                                            ThicknessCol  = 'AR'
                                            FWHM006Col    = 'AX'
                                            FWHM105Col    = 'AY'
                                            NumValues     = 3
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, 'AP' , SpreadsheetRowIndex, 'XRR')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, AVECol , SpreadsheetRowIndex, 'SL thickness')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, AVECol, (SpreadsheetRowIndex + 3), 'AVE')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, TL0Col, (SpreadsheetRowIndex + 6), 'Coordinate')
                                        Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                            BBCol         = 'BI'
                                            BarrierCol    = 'BJ'
                                            SLSCol        = 'BK'
                                            TL2Col        = 'BL'
                                            TL1Col        = 'BM'
                                            TL0Col        = 'BN'
                                            SLPeriodCol   = 'BO'
                                            ThickCoordCol = 'AT'
                                            ThicknessCol  = 'AU'
                                            FWHM006Col    = 'BA'
                                            FWHM105Col    = 'BB'
                                            NumValues     = 4
                                            RangeOffset   = 5
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, 'AS' , SpreadsheetRowIndex, 'XRR')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, BBCol,        (SpreadsheetRowIndex + 17), 'B.B.')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, BarrierCol,   (SpreadsheetRowIndex + 17), 'Barrier')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, SLSCol,       (SpreadsheetRowIndex + 17), 'SLS')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, TL2Col,       (SpreadsheetRowIndex + 17), 'TL2')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, TL1Col,       (SpreadsheetRowIndex + 17), 'TL1')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, TL0Col,       (SpreadsheetRowIndex + 17), 'TL0')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, SLPeriodCol,  (SpreadsheetRowIndex + 17), 'SL Period')                                        
                                    End Case                                                                    
                                    // Write Al% values
                                    BBMin         = ''
                                    BBMax         = ''
                                    BBRange       = ''
                                    BarrierMin    = ''
                                    BarrierMax    = ''
                                    BarrierRange  = ''
                                    SLSMin        = ''
                                    SLSMax        = ''
                                    SLSRange      = ''
                                    TL1Min        = ''
                                    TL1Max        = ''
                                    TL1Range      = ''
                                    SLPeriodMin   = ''
                                    SLPeriodMax   = ''
                                    SLPeriodRange = ''
                                    For each Row in XRDAlumData using @FM setting fPos
                                        Parameter = Row<0, 1>
                                        SBNo      = Row<0, 2>
                                        Pocket    = Row<0, 3>
                                        Value     = Row<0, 4>
                                        Begin Case
                                            Case Parameter EQ 'Al% B.B.'
                                                Col = BBCol
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset = NumValues
                                                end else
                                                    RowOffset = SBNo - 1
                                                    If BBMin EQ '' then BBMin = Value
                                                    If BBMax EQ '' then BBMax = Value
                                                    If Value LT BBMin then BBMin = Value
                                                    If Value GT BBMax then BBMax = Value
                                                end                                          
                                            Case Parameter EQ 'Al% Barrier'
                                                Col = BarrierCol
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset = NumValues
                                                end else
                                                    RowOffset = SBNo - 1
                                                    If BarrierMin EQ '' then BarrierMin = Value
                                                    If BarrierMax EQ '' then BarrierMax = Value
                                                    If Value LT BarrierMin then BarrierMin = Value
                                                    If Value GT BarrierMax then BarrierMax = Value
                                                end
                                            Case Parameter EQ 'Al% SLS'
                                                Col = SLSCol
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset = NumValues
                                                end else
                                                    RowOffset = SBNo - 1
                                                    If SLSMin EQ '' then SLSMin = Value
                                                    If SLSMax EQ '' then SLSMax = Value
                                                    If Value LT SLSMin then SLSMin = Value
                                                    If Value GT SLSMax then SLSMax = Value
                                                end
                                            Case Parameter EQ 'Al% TL1'
                                                Col = TL1Col
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset = NumValues
                                                end else
                                                    RowOffset = SBNo - 1
                                                    If TL1Min EQ '' then TL1Min = Value
                                                    If TL1Max EQ '' then TL1Max = Value
                                                    If Value LT TL1Min then TL1Min = Value
                                                    If Value GT TL1Max then TL1Max = Value
                                                end
                                            Case Parameter EQ 'SL Period'
                                                Col = SLPeriodCol
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset = NumValues
                                                end else
                                                    RowOffset = SBNo - 1
                                                    If SLPeriodMin EQ '' then SLPeriodMin = Value
                                                    If SLPeriodMax EQ '' then SLPeriodMax = Value
                                                    If Value LT SLPeriodMin then SLPeriodMin = Value
                                                    If Value GT SLPeriodMax then SLPeriodMax = Value
                                                end
                                            Case Otherwise$
                                                ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                        End Case
                                    Until ErrorMessage NE ''                                                                              
                                        RowIndex  = SpreadsheetRowIndex + RowOffset
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                    Next Row
                                    If ErrorMessage EQ '' then
                                        // Write Coordinate Data
                                        If SpreadsheetName NE '8-inch New HV Epi Development.xlsx' then
                                            // 6-inch spreadsheets                                      
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, ThickCoordCol, (SpreadsheetRowIndex + NumValues), 'Avg.')
                                            For each Row in XRDCoordData using @FM setting fPos
                                                SBNo  = Row<0, 2>
                                                Value = Abs(Row<0, 4>)
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, TL0Col, (SpreadsheetRowIndex + 6 + SBNo), Value)
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, ThickCoordCol, ( SpreadsheetRowIndex + (SBNo - 1) ), Value:'mm')                                               
                                            Next Row 
                                            // Write Wt. Avg. Data (applies to 6-inch spreadsheets only)
                                            RowIndex = SpreadsheetRowIndex + NumValues + 3
                                            For each Row in XRDWtAvgData using @FM setting fPos
                                                Parameter = Row<0, 1>
                                                SBNo      = Row<0, 2>
                                                Pocket    = Row<0, 3>
                                                Value     = Row<0, 4>
                                                Begin Case
                                                    Case Parameter EQ 'Al% B.B. WTAVG'
                                                        Col = BBCol                                           
                                                    Case Parameter EQ 'Al% Barrier WTAVG'
                                                        Col = BarrierCol
                                                    Case Parameter EQ 'Al% SLS WTAVG'
                                                        Col = SLSCol
                                                    Case Parameter EQ 'Al% TL1 WTAVG'
                                                        Col = TL1Col
                                                    Case Parameter EQ 'SL Period WTAVG'
                                                        Col = SLPeriodCol
                                                    Case Otherwise$
                                                    ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                                End Case
                                            Until ErrorMessage NE ''
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                            Next Row                                                                                                                                           
                                        end else
                                            // 8-inch spreadsheet
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, ThickCoordCol, (SpreadsheetRowIndex + NumValues), 'Avg.')
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, ThickCoordCol, (SpreadsheetRowIndex + NumValues + 1), 'Range')   
                                            For each Row in XRDCoordData using @FM setting fPos
                                                SBNo  = Row<0, 2>
                                                Value = Abs(Row<0, 4>)
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, ThickCoordCol, ( SpreadsheetRowIndex + (SBNo - 1) ), Value:'mm')     
                                            Next Row
                                            // Calculate Range Values
                                            BBRange       = BBMax - BBMin
                                            BarrierRange  = BarrierMax - BarrierMin
                                            SLSRange      = SLSMax - SLSMin
                                            TL1Range      = TL1Max - TL1Min
                                            SLPeriodRange = SLPeriodMax - SLPeriodMin
                                            // Write Range Values
                                            RowIndex      = SpreadsheetRowIndex + RangeOffset
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, BBCol, RowIndex, BBRange)
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, BarrierCol, RowIndex, BarrierRange)
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, SLSCol, RowIndex, SLSRange)
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, TL1Col, RowIndex, TL1Range)
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, SLPeriodCol, RowIndex, SLPeriodRange)                          
                                        end                                       
                                        // Write thickness and FWHM data
                                        ThicknessMin   = 0
                                        ThicknessMax   = 0
                                        ThicknessRange = 0
                                        FWHM006Min     = 0
                                        FWHM006Max     = 0
                                        FWHM006Range   = 0
                                        FWHM105Min     = 0
                                        FWHM105Max     = 0
                                        FWHM105Range   = 0
                                        For each Row in XRDThicknessData using @FM setting fPos
                                            Parameter = Row<0, 1>
                                            SBNo      = Row<0, 2>
                                            Pocket    = Row<0, 3>
                                            Value     = Row<0, 4>
                                            Begin Case
                                                Case Parameter EQ 'XRR Thickness (nm)'
                                                    Col = ThicknessCol
                                                    If ThicknessMin EQ 0 then
                                                        // Initialize min and max variables
                                                        ThicknessMin = Value
                                                        ThicknessMax = Value
                                                    end
                                                    If Value LT ThicknessMin then ThicknessMin = Value
                                                    If Value GT ThicknessMax then ThicknessMax = Value
                                                Case Parameter EQ 'FWHM 006'
                                                    Col = FWHM006Col
                                                    If FWHM006Min EQ 0 then
                                                        // Initialize min and max variables
                                                        FWHM006Min = Value
                                                        FWHM006Max = Value
                                                    end
                                                    If Value LT FWHM006Min then FWHM006Min = Value
                                                    If Value GT FWHM006Max then FWHM006Max = Value
                                                Case Parameter EQ 'FWHM 105'
                                                    Col = FWHM105Col
                                                    If FWHM105Min EQ 0 then
                                                        FWHM105Min = Value
                                                        FWHM105Max = Value
                                                    end
                                                    If Value LT FWHM105Min then FWHM105Min = Value
                                                    If Value GT FWHM105Max then FWHM105Max = Value
                                                Case Otherwise$                                                    
                                                    ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                            End Case
                                        Until ErrorMessage NE '' 
                                            // The following code to used to place the average in the correct row
                                            If SBNo EQ 0 then
                                                // Average value is SBNo 0
                                                RowOffset = NumValues
                                            end else
                                                RowOffset = SBNo - 1
                                            end                                        
                                            RowIndex  = SpreadsheetRowIndex + RowOffset
                                            If ( (SBNo NE 0) or (SpreadsheetName EQ '8-inch New HV Epi Development.xlsx') or (Parameter EQ 'XRR Thickness (nm)') ) then
                                                // Do not write the average values (i.e. SBNo = 0) for 6-inch spreadsheets
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)                                                                                                                             
                                            end
                                            If SpreadsheetName EQ '8-inch New HV Epi Development.xlsx' then
                                                RowIndex       = SpreadsheetRowIndex + NumValues + 1    
                                                ThicknessRange = ThicknessMax - ThicknessMin
                                                FWHM006Range   = FWHM006Max - FWHM006Min
                                                FWHM105Range   = FWHM105Max - FWHM105Min                                     
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, ThicknessCol, RowIndex, ThicknessRange)
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, FWHM006Col, RowIndex, FWHM006Range)
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, FWHM105Col, RowIndex, FWHM105Range) 
                                            end
                                        Next Row
                                    end
                                    If ErrorMessage EQ '' then
                                        Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                        If Error_Services('NoError') then
                                            TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                            TempFileHandleCopy = TempFileHandle
                                            TempFileSize       = 0
                                            If FileHandle NE FILE_ERROR$ then
                                                // We were able to get the lock
                                                TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                                File_Services('UnlockFile', TempFileHandle)
                                            end
                                            Begin Case
                                                Case TempFileHandleCopy EQ FILE_ERROR$
                                                    // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                                Case TempFileSize EQ 0
                                                    // Save failed - file corrupted
                                                    ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                                Case Otherwise$
                                                    // Save successful, so copy temp file back to document (PROD) path.
                                                    File_Services('UnlockFile', FileHandle)
                                                    CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                    If CopySucceeded EQ False$ then
                                                        ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                    end
                                            End Case    
                                        end else
                                            // SaveDocument call failed. Get internal error for logging purposes.
                                            ErrorMessage = Error_Services('GetMessage') 
                                        end
                                    end                                                                
                                end else
                                    // Row index did not match. Log error.
                                    ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                    ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                    ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                    Excel_Services('CloseDocument', DocObj)       
                                end                                                                                   
                            end else                        
                                ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.' 
                            end
                            OSDelete TempPath                                     
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                       
                        end    
                    end else
                        ErrorMessage = 'Error in ':Service:' service. XRD data is missing in IQS.'
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. XRD data is missing in IQS.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'      
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'     
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostHALLData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetName NE '8-inch New HV Epi Development.xlsx' then
            // Hall data is currently only recorded for 6-inch wafers       
            If SpreadsheetRowIndex NE '' then
                If GaNRunID NE '' then
                    HallData   = Engineering_Services('GetHallData', RDSNo)
                    // Log SQL response
                    LogData<1> = LoggingDTM
                    LogData<2> = RDSNo
                    LogData<3> = HallData
                    Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                    If ( (HallData NE '') and (HallData NE 0) ) then          
                        DocumentPath = SpreadsheetDir:SpreadsheetName
                        FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                        If FileHandle NE FILE_ERROR$ then
                            // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                            Datetime      = OConv(Datetime(), 'DT')
                            Swap ' ' with '-' in Datetime
                            Swap ':' with ';' in Datetime
                            TempFilename  = GaNRunID:'-':Datetime:'.xlsx'                       
                            TempDirectory = Str(\00\, 1024)
                            GetTempPath(Len(TempDirectory), TempDirectory)
                            Convert \00\ to '' in TempDirectory                       
                            TempPath      = TempDirectory:TempFilename
                            File_Services('CopyFile', DocumentPath, TempPath)                  
                            DocObj        = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                            If Error_Services('NoError') then                            
                                Set_Status(0)
                                // Verify spreadsheet row index is correct
                                RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                                RowRunID      = RunIDCellText[1, 12]
                                If RowRunID EQ GaNRunID then
                                    Begin Case
                                        Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                            RSCol       = 'AR'
                                            MobilityCol = 'AS'
                                            NSCol       = 'AT'                                   
                                        Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                            RSCol       = 'AS'
                                            MobilityCol = 'AT'
                                            NSCol       = 'AU'                                      
                                    End Case
                                    NumRSVals          = 0
                                    NumMobilityVals    = 0
                                    NumNSVals          = 0
                                    RSVals             = ''
                                    MobilityVals       = ''
                                    NSVals             = ''
                                    RSDeviations       = ''
                                    MobilityDeviations = ''
                                    NSDeviations       = ''
                                    RSAvg              = ''
                                    MobilityAvg        = ''
                                    NSAvg              = ''
                                    RSCV               = ''
                                    MobilityCV         = ''
                                    NSCV               = ''                     
                                    // Write Hall values                                                                        
                                    For each Row in HallData using @FM setting fPos
                                        Parameter = Row<0, 1>
                                        SBNo      = Row<0, 2>
                                        Value     = Row<0, 3>
                                        Begin Case
                                            Case Parameter EQ 'Hall Rs'
                                                Col = RSCol
                                                If SBNo EQ 0 then
                                                    RowOffset = 7
                                                    RSAvg     = Value
                                                end else
                                                    RowOffset      = SBNo - 1
                                                    NumRSVals     += 1
                                                    RSVals<0, -1>  = Value
                                                end                              
                                            Case Parameter EQ 'Mobility'
                                                Col = MobilityCol
                                                If SBNo EQ 0 then
                                                    RowOffset   = 7
                                                    MobilityAvg = Value
                                                end else
                                                    RowOffset            = SBNo - 1
                                                    NumMobilityVals     += 1
                                                    MobilityVals<0, -1>  = Value
                                                end       
                                            Case Parameter EQ 'Sheet Concentration'
                                                Col = NSCol
                                                If SBNo EQ 0 then
                                                    RowOffset = 7
                                                    NSAvg     = Value
                                                end else
                                                    RowOffset      = SBNo - 1
                                                    NumNSVals     += 1
                                                    NSVals<0, -1>  = Value
                                                end
                                            Case Otherwise$
                                                ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'       
                                        End Case
                                    Until ErrorMessage NE ''                                                                                                                       
                                        RowIndex  = SpreadsheetRowIndex + RowOffset
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)                                                                                             
                                    Next Row
                                    If ErrorMessage EQ '' then
                                        // Calculate Std Dev for RS
                                        For each Value in RSVals using @VM setting vPos
                                            Deviation             = Value - RSAvg
                                            RSDeviations<0, vPos> = Deviation ** 2
                                        Next Value
                                        RSDevsSum = Sum(RSDeviations)
                                        // Std Dev for a sample, not population
                                        If (NumRSVals - 1) GT 0 then
                                            Variance = RSDevsSum / (NumRSVals - 1)
                                            RSStdDev  = Variance ** 0.5
                                            // Coefficient of Variation
                                            If RSAvg GT 0 then RSCV = RSStdDev / RSAvg
                                        end                                    
                                        // Calculate Std Dev for Mobility
                                        For each Value in MobilityVals using @VM setting vPos
                                            Deviation             = Value - MobilityAvg
                                            MobilityDeviations<0, vPos> = Deviation ** 2
                                        Next Value
                                        MobilityDevsSum = Sum(MobilityDeviations)
                                        If (NumMobilityVals - 1) GT 0 then
                                            Variance = MobilityDevsSum / (NumMobilityVals - 1)
                                            MobilityStdDev  = Variance ** 0.5
                                            // Coefficient of Variation
                                            If MobilityAvg GT 0 then MobilityCV = MobilityStdDev / MobilityAvg
                                        end                                   
                                        // Calculate Std Dev for NS
                                        For each Value in NSVals using @VM setting vPos
                                            Deviation             = Value - NSAvg
                                            NSDeviations<0, vPos> = Deviation ** 2
                                        Next Value
                                        NSDevsSum  = Sum(NSDeviations)
                                        // Std Dev for a sample, not population
                                        If (NumNSVals - 1) GT 0 then
                                            Variance = NSDevsSum / (NumNSVals - 1)
                                            NSStdDev  = Variance ** 0.5
                                            // Coefficient of Variation
                                            If NSAvg GT 0 then NSCV = NSStdDev / NSAvg
                                        end                                    
                                        // Write Std Dev Values
                                        StdDevRow = 8
                                        RowIndex  = SpreadsheetRowIndex + StdDevRow
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, RSCol, RowIndex, RSCV)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, MobilityCol, RowIndex, MobilityCV)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, NSCol, RowIndex, NSCV)
                                        // Save document                                                                                                                                                        
                                        Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                        If Error_Services('NoError') then
                                            TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                            TempFileHandleCopy = TempFileHandle
                                            TempFileSize       = 0
                                            If FileHandle NE FILE_ERROR$ then
                                                // We were able to get the lock
                                                TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                                File_Services('UnlockFile', TempFileHandle)
                                            end
                                            Begin Case
                                                Case TempFileHandleCopy EQ FILE_ERROR$
                                                    // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                                Case TempFileSize EQ 0
                                                    // Save failed - file corrupted
                                                    ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                                Case Otherwise$
                                                    // Save successful, so copy temp file back to document (PROD) path.
                                                    File_Services('UnlockFile', FileHandle)
                                                    CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                    If CopySucceeded EQ False$ then
                                                        ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                    end
                                            End Case    
                                        end else
                                            // SaveDocument call failed. Get internal error for logging purposes.
                                            ErrorMessage = Error_Services('GetMessage') 
                                        end
                                    end                                                           
                                end else
                                    // Row index did not match. Log error.
                                    ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                    ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                    ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                    Excel_Services('CloseDocument', DocObj)       
                                end                                                                                           
                            end else
                                ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'  
                            end
                            OSDelete TempPath                                     
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                        end
                    end else
                        ErrorMessage = 'Error in ':Service:' service. Hall data missing in IQS.'
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
            end
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)

end service


Service PostBVData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then
                BVData     = Engineering_Services('GetBVData', RDSNo, 'Breakdown Voltage')
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = BVData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)             
                If ( Error_Services('NoError') and (BVData NE '') and (BVData NE 0) ) then
                    BVEdgeData = Engineering_Services('GetBVData', RDSNo, 'Breakdown Voltage - Edge')
                    // Log SQL response
                    LogData<1> = LoggingDTM
                    LogData<2> = RDSNo
                    LogData<3> = BVEdgeData
                    Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                    If ( Error_Services('NoError') and (BVEdgeData NE '') and (BVEdgeData NE 0) ) then
                        If SpreadsheetName EQ '8-inch New HV Epi Development.xlsx' then
                            // 8-inch spreadsheet - Middle = Left, Edge = Right                                   
                            BVMiddleData = Engineering_Services('GetBVData', RDSNo, 'Breakdown Voltage - Middle')
                            // Log SQL response
                            LogData<1>   = LoggingDTM
                            LogData<2>   = RDSNo
                            LogData<3>   = BVMiddleData
                            Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                            If ( Error_Services('HasError') or (BVMiddleData EQ '') or (BVMiddleData EQ 0) ) then
                                ErrorMessage = 'Error in service ':Service:'. Breakdown Voltage - Middle data is missing in IQS.'
                            end
                        end
                        If ErrorMessage EQ '' then
                            DocumentPath = SpreadsheetDir:SpreadsheetName
                            FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                            If FileHandle NE FILE_ERROR$ then
                                // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                                Datetime      = OConv(Datetime(), 'DT')
                                Swap ' ' with '-' in Datetime
                                Swap ':' with ';' in Datetime
                                TempFilename  = GaNRunID:'-':Datetime:'.xlsx'                       
                                TempDirectory = Str(\00\, 1024)
                                GetTempPath(Len(TempDirectory), TempDirectory)
                                Convert \00\ to '' in TempDirectory                       
                                TempPath      = TempDirectory:TempFilename
                                File_Services('CopyFile', DocumentPath, TempPath)                  
                                DocObj        = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                                If Error_Services('NoError') then                            
                                    // Verify spreadsheet row index is correct
                                    RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                                    RowRunID      = RunIDCellText[1, 12]
                                    If RowRunID EQ GaNRunID then
                                        Begin Case
                                            Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                                LeftCol   = 'BL'
                                                CenterCol = 'BM'
                                                RightCol  = 'BN'
                                                AvgCol    = 'BO'
                                                NumValues = 5                                      
                                            Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                                LeftCol   = 'BM'
                                                CenterCol = 'BN'
                                                RightCol  = 'BO'
                                                AvgCol    = 'BP'
                                                NumValues = 5
                                            Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                                LeftCol   = 'BP'
                                                CenterCol = 'BQ'
                                                RightCol  = 'BR'
                                                AvgCol    = 'BS'
                                                NumValues = 5                                     
                                        End Case                             
                                        // Write Breakdown Voltage values
                                        BVAvgTotal  = 0
                                        BVAvgPoints = 0
                                        BVAvgMax    = 0
                                        BVAvgMin    = 0            
                                        Col         = CenterCol
                                        For each Row in BVData using @FM setting fPos
                                            SBNo      = Row<0, 2>
                                            Value     = Row<0, 3>
                                            // The following code to used to place the average in the correct row
                                            If SBNo EQ 0 then
                                                // Average value is SBNo 0
                                                RowOffset    = NumValues + 1
                                                BVAvgTotal  += Value
                                                BVAvgPoints += 1
                                                // Initialize BV Avg Max and Min to the Center Avg
                                                BVAvgMax     = Value
                                                BVAvgMin     = Value
                                            end else
                                                RowOffset = SBNo - 1
                                            end                                        
                                            RowIndex  = SpreadsheetRowIndex + RowOffset
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                        Next Row      
                                        // Write Left (or Middle) and Right (or Edge) BV Data
                                        If SpreadsheetName NE '8-inch New HV Epi Development.xlsx' then
                                            // 6-inch spreadsheets - SBNo = 1 -> Left, SBNo = 2 -> Right
                                            RowIndex = SpreadsheetRowIndex
                                            For each Row in BVEdgeData using @FM setting fPos
                                                SBNo      = Row<0, 2>
                                                Value     = Row<0, 3>                                       
                                                Begin Case
                                                    Case SBNo EQ 1
                                                        // Left                                                    
                                                        Excel_Services('SetCellValue', DocObj, WorksheetName, LeftCol, RowIndex, Value)
                                                        BVAvgTotal  += Value
                                                        BVAvgPoints += 1
                                                        If Value GT BVAvgMax then BVAvgMax = Value
                                                        If Value LT BVAvgMin then BVAvgMin = Value
                                                    Case SBNo EQ 2
                                                        // Right
                                                        Excel_Services('SetCellValue', DocObj, WorksheetName, RightCol, RowIndex, Value)
                                                        BVAvgTotal  += Value
                                                        BVAvgPoints += 1
                                                        If Value GT BVAvgMax then BVAvgMax = Value
                                                        If Value LT BVAvgMin then BVAvgMin = Value
                                                    Case Otherwise$
                                                        // 6 inch spreadsheets currently don't record the average (i.e. SBNo = 0)
                                                        Null
                                                End Case                                                                                                                              
                                            Next Row
                                            // Calculate the average of the Left, Center average, and Right values
                                            If BVAvgPoints GT 0 then
                                                BVAvg    = BVAvgTotal / BVAvgPoints
                                                RowIndex = SpreadsheetRowIndex + NumValues + 1
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, AvgCol, RowIndex, BVAvg)                          
                                            end
                                            // Calculate the range of the averages
                                            AvgRange = BVAvgMax - BVAvgMin
                                            RowIndex = SpreadsheetRowIndex + NumValues + 1
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, AvgCol, RowIndex, AvgRange)      
                                        end else
                                            // 8-inch spreadsheet - Middle = Left, Edge = Right
                                            MiddleAvg = 0                                  
                                            For each Row in BVMiddleData using @FM setting fPos
                                                SBNo  = Row<0, 2>
                                                Value = Row<0, 3>
                                                // The following code to used to place the average in the correct row
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset    = NumValues + 1
                                                    BVAvgTotal  += Value
                                                    BVAvgPoints += 1
                                                    If Value GT BVAvgMax then BVAvgMax = Value
                                                    If Value LT BVAvgMin then BVAvgMin = Value
                                                end else
                                                    RowOffset = SBNo - 1
                                                end                                        
                                                RowIndex  = SpreadsheetRowIndex + RowOffset
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, LeftCol, RowIndex, Value)                       
                                            Next Row   
                                            For each Row in BVEdgeData using @FM setting fPos
                                                SBNo  = Row<0, 2>
                                                Value = Row<0, 3>
                                                // The following code to used to place the average in the correct row
                                                If SBNo EQ 0 then
                                                    // Average value is SBNo 0
                                                    RowOffset    = NumValues + 1
                                                    BVAvgTotal  += Value
                                                    BVAvgPoints += 1
                                                    If Value GT BVAvgMax then BVAvgMax = Value
                                                    If Value LT BVAvgMin then BVAvgMin = Value
                                                end else
                                                    RowOffset = SBNo - 1
                                                end                                        
                                                RowIndex  = SpreadsheetRowIndex + RowOffset
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, RightCol, RowIndex, Value)                                                                                                    
                                            Next Row
                                            // Calculate the average of the Middle, Center, and Edge average values
                                            If BVAvgPoints GT 0 then
                                                BVAvg    = BVAvgTotal / BVAvgPoints
                                                RowIndex = SpreadsheetRowIndex + NumValues + 3
                                                Excel_Services('SetCellValue', DocObj, WorksheetName, CenterCol, RowIndex, BVAvg)                             
                                            end
                                            // Calculate the range of the averages
                                            AvgRange = BVAvgMax - BVAvgMin
                                            RowIndex = SpreadsheetRowIndex + NumValues + 1
                                            Excel_Services('SetCellValue', DocObj, WorksheetName, AvgCol, RowIndex, AvgRange)
                                        end                                                                   
                                        Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                        If Error_Services('NoError') then
                                            TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                            TempFileHandleCopy = TempFileHandle
                                            TempFileSize       = 0
                                            If FileHandle NE FILE_ERROR$ then
                                                // We were able to get the lock
                                                TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                                File_Services('UnlockFile', TempFileHandle)
                                            end
                                            Begin Case
                                                Case TempFileHandleCopy EQ FILE_ERROR$
                                                    // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                                Case TempFileSize EQ 0
                                                    // Save failed - file corrupted
                                                    ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                                Case Otherwise$
                                                    // Save successful, so copy temp file back to document (PROD) path.
                                                    File_Services('UnlockFile', FileHandle)
                                                    CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                    If CopySucceeded EQ False$ then
                                                        ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                    end
                                            End Case    
                                        end else
                                            // SaveDocument call failed. Get internal error for logging purposes.
                                            ErrorMessage = Error_Services('GetMessage') 
                                        end                                                                
                                    end else
                                        // Row index did not match. Log error.
                                        ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                        ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                        ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                        Excel_Services('CloseDocument', DocObj)       
                                    end                                                              
                                end else
                                    ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'   
                                end
                                OSDelete TempPath                                   
                            end else
                                ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                            end
                        end
                    end else
                        ErrorMessage = 'Error in ':Service:' service. Breakdown Voltage data missing in IQS.'    
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. Breakdown Voltage data missing in IQS.'   
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'   
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'   
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument')
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostUVData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then
                UVData     = Engineering_Services('GetUVData', RDSNo)
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = UVData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)
                If ( Error_Services('NoError') and (UVData NE '') and (UVData NE 0) ) then            
                    DocumentPath = SpreadsheetDir:SpreadsheetName
                    FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                    If FileHandle NE FILE_ERROR$ then
                        // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                        Datetime     = OConv(Datetime(), 'DT')
                        Swap ' ' with '-' in Datetime
                        Swap ':' with ';' in Datetime
                        TempFilename = GaNRunID:'-':Datetime:'.xlsx'                       
                        TempDirectory = Str(\00\, 1024)
                        GetTempPath(Len(TempDirectory), TempDirectory)
                        Convert \00\ to '' in TempDirectory                       
                        TempPath     = TempDirectory:TempFilename
                        File_Services('CopyFile', DocumentPath, TempPath)                  
                        DocObj       = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                        If Error_Services('NoError') then                            
                            // Verify spreadsheet row index is correct
                            RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                            RowRunID      = RunIDCellText[1, 12]
                            If RowRunID EQ GaNRunID then
                                Begin Case
                                    Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                        BrokenCol       = 'BW'
                                        NonRotationCol  = 'BX'
                                        ScratchCol      = 'BY'
                                        Cracking3to7Col = 'BZ'
                                        CrackingGT7Col  = 'CA'
                                        TrueHazeCol     = 'CB'
                                        CrazingCol      = 'CC'
                                        SlipCountCol    = 'CD'
                                        SPEGT1mmCol     = 'CE'
                                        SPECountCol     = 'CF'
                                        NumValues       = 8                                       
                                    Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                        BrokenCol       = 'BX'
                                        NonRotationCol  = 'BY'
                                        ScratchCol      = 'BZ'
                                        Cracking3to7Col = 'CA'
                                        CrackingGT7Col  = 'CB'
                                        TrueHazeCol     = 'CC'
                                        CrazingCol      = 'CD'
                                        SlipCountCol    = 'CE'
                                        SPEGT1mmCol     = 'CF'
                                        SPECountCol     = 'CG'
                                        NumValues       = 8
                                    Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                        BrokenCol       = 'BT'
                                        NonRotationCol  = 'BU'
                                        ScratchCol      = 'BV'
                                        Cracking3to7Col = 'BW'
                                        CrackingGT7Col  = 'BX'
                                        TrueHazeCol     = 'BY'
                                        CrazingCol      = 'BZ'
                                        SlipCountCol    = 'CA'
                                        SPEGT1mmCol     = 'CB'
                                        SPECountCol     = 'CC'
                                        NumValues       = 5                                      
                                End Case                             
                                // Write UV values
                                SPEAvg = 0
                                For each Row in UVData using @FM setting fPos
                                    Parameter = Row<0, 1>
                                    Pocket    = Row<0, 2>
                                    Value     = Row<0, 3>
                                    Begin Case
                                        Case Parameter EQ 'UV Broken'
                                            Col = BrokenCol                                           
                                        Case Parameter EQ 'UV Non-rotation'
                                            Col = NonRotationCol
                                        Case Parameter EQ 'UV Scratch (count)'
                                            Col = ScratchCol
                                        Case Parameter EQ 'UV Cracking (3-7mm)'
                                            Col = Cracking3to7Col
                                        Case Parameter EQ 'UV Cracking (>7mm)'
                                            Col = CrackingGT7Col
                                        Case Parameter EQ 'UV True Haze'
                                            Col = TrueHazeCol
                                        Case Parameter EQ 'UV Crazing'
                                            Col = CrazingCol
                                        Case Parameter EQ 'UV Slip (count)' 
                                            Col = SlipCountCol
                                        Case Parameter EQ 'UV-SPE (>1mm) count'
                                            Col = SPEGT1mmCol
                                        Case Parameter EQ 'UV-SPE (count)'
                                            Col     = SPECountCol
                                            SPEAvg += Value
                                        Case Otherwise$
                                            ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                    End Case
                                Until ErrorMessage NE ''                    
                                    RowIndex  = SpreadsheetRowIndex + Pocket - 1
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                Next Row
                                If ErrorMessage EQ '' then
                                    If SpreadsheetName EQ '8-inch New HV Epi Development.xlsx' then
                                        // Write SPE Avg
                                        If NumValues GT 0 then SPEAvg = SPEAvg / NumValues
                                        RowIndex = SpreadsheetRowIndex + NumValues
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, SPECountCol, RowIndex, SPEAvg)
                                    end
                                    Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                    If Error_Services('NoError') then
                                        TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                        TempFileHandleCopy = TempFileHandle
                                        TempFileSize       = 0
                                        If FileHandle NE FILE_ERROR$ then
                                            // We were able to get the lock
                                            TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                            File_Services('UnlockFile', TempFileHandle)
                                        end
                                        Begin Case
                                            Case TempFileHandleCopy EQ FILE_ERROR$
                                                // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                            Case TempFileSize EQ 0
                                                // Save failed - file corrupted
                                                ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                            Case Otherwise$
                                                // Save successful, so copy temp file back to document (PROD) path.
                                                File_Services('UnlockFile', FileHandle)
                                                CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                If CopySucceeded EQ False$ then
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                end
                                        End Case    
                                    end else
                                        // SaveDocument call failed. Get internal error for logging purposes.
                                        ErrorMessage = Error_Services('GetMessage') 
                                    end
                                end                                                         
                            end else
                                // Row index did not match. Log error.
                                ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                Excel_Services('CloseDocument', DocObj)       
                            end                                                                                  
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'   
                        end
                        OSDelete TempPath                                                            
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. UV data missing in IQS.'    
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'    
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'      
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostCANData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then            
                CandelaData = Engineering_Services('GetCandelaData', RDSNo)
                // Log SQL response
                LogData<1>  = LoggingDTM
                LogData<2>  = RDSNo
                LogData<3>  = CandelaData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)               
                If ( (CandelaData NE '') and (CandelaData NE 0) ) then
                    DocumentPath = SpreadsheetDir:SpreadsheetName
                    FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                    If FileHandle NE FILE_ERROR$ then
                        // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                        Datetime     = OConv(Datetime(), 'DT')
                        Swap ' ' with '-' in Datetime
                        Swap ':' with ';' in Datetime
                        TempFilename = GaNRunID:'-':Datetime:'.xlsx'                       
                        TempDirectory = Str(\00\, 1024)
                        GetTempPath(Len(TempDirectory), TempDirectory)
                        Convert \00\ to '' in TempDirectory                       
                        TempPath     = TempDirectory:TempFilename
                        File_Services('CopyFile', DocumentPath, TempPath)                  
                        DocObj       = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                        If Error_Services('NoError') then                            
                            // Verify spreadsheet row index is correct
                            RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                            RowRunID      = RunIDCellText[1, 12]
                            If RowRunID EQ GaNRunID then
                                Begin Case
                                    Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                        PitsCol           = 'AD'
                                        LPDSmallCol       = 'AE'
                                        LPDMedCol         = 'AF'
                                        LPDLargeCol       = 'AG'
                                        SPECol            = 'AH'
                                        SpiralsCol        = 'AI'
                                        CraterCol         = 'AJ'
                                        CrackingAcceptCol = 'AK'
                                        CrackingCol       = 'AL'
                                        TotalDefectsCol   = '' ; // Currently not being recorded in spreadsheet                                       
                                    Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                        PitsCol           = 'AE'
                                        LPDSmallCol       = 'AF'
                                        LPDMedCol         = 'AG'
                                        LPDLargeCol       = 'AH'
                                        SPECol            = 'AI'
                                        SpiralsCol        = 'AJ'
                                        CraterCol         = 'AK'
                                        CrackingAcceptCol = 'AL'
                                        CrackingCol       = 'AM'
                                        TotalDefectsCol   = '' ; // Currently not being recorded in spreadsheet
                                    Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                        PitsCol           = 'AL'
                                        LPDSmallCol       = 'AJ'
                                        LPDMedCol         = 'AI'
                                        LPDLargeCol       = 'AH'
                                        SPECol            = 'AG'
                                        SpiralsCol        = 'AM'
                                        CraterCol         = 'AK'
                                        CrackingAcceptCol = 'AO'
                                        CrackingCol       = 'AN'
                                        TotalDefectsCol   = 'AP'
                                        AvgRowOffset      = 5                                      
                                End Case                             
                                // Write Candela values
                                CrackingAvg        = 0
                                NumCrackingVals    = 0
                                CrackingAccAvg     = 0
                                NumCrackingAccVals = 0
                                CraterAvg          = 0
                                NumCraterVals      = 0
                                LPDLargeAvg        = 0
                                NumLPDLargeVals    = 0
                                LPDMedAvg          = 0
                                NumLPDMedVals      = 0
                                LPDSmallAvg        = 0
                                NumLPDSmallVals    = 0
                                PitsAvg            = 0
                                NumPitsVals        = 0
                                SPEAvg             = 0
                                NumSPEVals         = 0
                                SpiralsAvg         = 0
                                NumSpiralsVals     = 0
                                CanTotDefAvg       = 0
                                NumCanTotDefVals   = 0
                                For each Row in CandelaData using @FM setting fPos
                                    Parameter = Row<0, 1>
                                    Pocket    = Row<0, 2>
                                    Value     = Row<0, 3>
                                    Begin Case
                                        Case Parameter EQ 'Candela Cracking'
                                            Col              = CrackingCol
                                            CrackingAvg     += Value
                                            NumCrackingVals += 1                                        
                                        Case Parameter EQ 'Candela Cracking Acceptable'
                                            Col                 = CrackingAcceptCol
                                            CrackingAccAvg     += Value
                                            NumCrackingAccVals += 1
                                        Case Parameter EQ 'Candela Crater'
                                            Col            = CraterCol
                                            CraterAvg     += Value
                                            NumCraterVals += 1
                                        Case Parameter EQ 'Candela LPD Large'
                                            Col              = LPDLargeCol
                                            LPDLargeAvg     += Value
                                            NumLPDLargeVals += 1
                                        Case Parameter EQ 'Candela LPD Medium'
                                            Col            = LPDMedCol
                                            LPDMedAvg     += Value
                                            NumLPDMedVals += 1
                                        Case Parameter EQ 'Candela LPD Small'
                                            Col              = LPDSmallCol
                                            LPDSmallAvg     += Value
                                            NumLPDSmallVals += 1
                                        Case Parameter EQ 'Candela Pits'
                                            Col          = PitsCol
                                            PitsAvg     += Value
                                            NumPitsVals += 1
                                        Case Parameter EQ 'Candela SPE'
                                            Col         = SPECol
                                            SPEAvg     += Value
                                            NumSPEVals += 1
                                        Case Parameter EQ 'Candela Spirals'
                                            Col             = SpiralsCol
                                            SpiralsAvg     += Value
                                            NumSpiralsVals += 1
                                        Case Parameter EQ 'Candela Total Defects'
                                            Col               = TotalDefectsCol
                                            CanTotDefAvg     += Value
                                            NumCanTotDefVals += 1
                                        Case Otherwise$
                                            ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                    End Case
                                Until ErrorMessage NE ''                  
                                    RowIndex  = SpreadsheetRowIndex + Pocket - 1
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                Next Row
                                If ErrorMessage EQ '' then
                                    If SpreadsheetName EQ '8-inch New HV Epi Development.xlsx' then
                                        // Calculate averages
                                        If NumCrackingVals    GT 0 then CrackingAvg    = CrackingAvg    / NumCrackingVals
                                        If NumCrackingAccVals GT 0 then CrackingAccAvg = CrackingAccAvg / NumCrackingAccVals
                                        If NumCraterVals      GT 0 then CraterAvg      = CraterAvg      / NumCraterVals
                                        If NumLPDLargeVals    GT 0 then LPDLargeAvg    = LPDLargeAvg    / NumLPDLargeVals
                                        If NumLPDMedVals      GT 0 then LPDMedAvg      = LPDMedAvg      / NumLPDMedVals
                                        If NumLPDSmallVals    GT 0 then LPDSmallAvg    = LPDSmallAvg    / NumLPDSmallVals
                                        If NumPitsVals        GT 0 then PitsAvg        = PitsAvg        / NumPitsVals
                                        If NumSPEVals         GT 0 then SPEAvg         = SPEAvg         / NumSPEVals
                                        If NumSpiralsVals     GT 0 then SpiralsAvg     = SpiralsAvg     / NumSpiralsVals
                                        If NumCanTotDefVals   GT 0 then CanTotDefAvg   = CanTotDefAvg   / NumCanTotDefVals
                                        // Write averages
                                        RowIndex       = SpreadsheetRowIndex + AvgRowOffset
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, CrackingCol, RowIndex, CrackingAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, CrackingAcceptCol, RowIndex, CrackingAccAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, CraterCol, RowIndex, CraterAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, LPDLargeCol, RowIndex, LPDLargeAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, LPDMedCol, RowIndex, LPDMedAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, LPDSmallCol, RowIndex, LPDSmallAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, PitsCol, RowIndex, PitsAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, SPECol, RowIndex, SPEAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, SpiralsCol, RowIndex, SpiralsAvg)
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, TotalDefectsCol, RowIndex, CanTotDefAvg)
                                    end                                                             
                                    Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                    If Error_Services('NoError') then
                                        TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                        TempFileHandleCopy = TempFileHandle
                                        TempFileSize       = 0
                                        If FileHandle NE FILE_ERROR$ then
                                            // We were able to get the lock
                                            TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                            File_Services('UnlockFile', TempFileHandle)
                                        end
                                        Begin Case
                                            Case TempFileHandleCopy EQ FILE_ERROR$
                                                // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                            Case TempFileSize EQ 0
                                                // Save failed - file corrupted
                                                ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                            Case Otherwise$
                                                // Save successful, so copy temp file back to document (PROD) path.
                                                File_Services('UnlockFile', FileHandle)
                                                CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                If CopySucceeded EQ False$ then
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                end
                                        End Case    
                                    end else
                                        // SaveDocument call failed. Get internal error for logging purposes.
                                        ErrorMessage = Error_Services('GetMessage') 
                                    end
                                end                                                                
                            end else
                                // Row index did not match. Log error.
                                ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                Excel_Services('CloseDocument', DocObj)       
                            end                                                                                    
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':TempPath:' file could not be opened by Excel_Services.'   
                        end
                        OSDelete TempPath                                   
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. Candela data for Run ID ':GaNRunID:' is not available in IQS at this time.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.' 
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.'   
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service PostWARPData(RDSNo)

    DocObj       = ''
    ErrorMessage = ''
    FileHandle   = ''
    If RDSNo NE '' then
        ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        GaNRunID            = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
        WONo                = ReactRunRec<REACT_RUN_WO_NO$>
        Reactor             = ReactRunRec<REACT_RUN_REACTOR$>
        SpreadsheetRowIndex = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
        RunRecipe           = ReactRunRec<REACT_RUN_GAN_RECIPE$>      
        SpreadsheetInfo     = Engineering_Services('GetSpreadSheetInfo', RDSNo)
        SpreadsheetName     = SpreadsheetInfo<1>
        WorksheetName       = SpreadsheetInfo<2>
        SpreadsheetPW       = SpreadsheetInfo<3>
        SpreadsheetDir      = SpreadsheetInfo<4>
        Offset              = SpreadsheetInfo<5>
              
        If SpreadsheetRowIndex NE '' then
            If GaNRunID NE '' then
                WarpData   = Engineering_Services('GetWARPData', RDSNo)
                // Log SQL response
                LogData<1> = LoggingDTM
                LogData<2> = RDSNo
                LogData<3> = WarpData
                Logging_Services('AppendLog', objSQLLog, LogData, @RM, @FM)                
                If ( Error_Services('NoError') and (WarpData NE '') and (WarpData NE 0) ) then
                    DocumentPath = SpreadsheetDir:SpreadsheetName
                    FileHandle   = File_Services('LockFile', DocumentPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                    If FileHandle NE FILE_ERROR$ then
                        // Make a temporary backup in case the Excel file fails to save and becomes corrupted
                        Datetime     = OConv(Datetime(), 'DT')
                        Swap ' ' with '-' in Datetime
                        Swap ':' with ';' in Datetime
                        TempFilename = GaNRunID:'-':Datetime:'.xlsx'                       
                        TempDirectory = Str(\00\, 1024)
                        GetTempPath(Len(TempDirectory), TempDirectory)
                        Convert \00\ to '' in TempDirectory                       
                        TempPath     = TempDirectory:TempFilename
                        File_Services('CopyFile', DocumentPath, TempPath)                  
                        DocObj       = Excel_Services('OpenDocument', TempPath, SpreadsheetPW)
                        If Error_Services('NoError') then                            
                            // Verify spreadsheet row index is correct
                            RunIDCellText = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', SpreadsheetRowIndex)
                            RowRunID      = RunIDCellText[1, 12]
                            If RowRunID EQ GaNRunID then
                                Begin Case
                                    Case SpreadsheetName EQ 'MV_R71_6-inch Epi Development.xlsx'
                                        WarpCol   = 'AM'
                                        BowCol    = 'AN'
                                        AvgRow    = SpreadsheetRowIndex + 10
                                        RangeRow  = SpreadsheetRowIndex + 11                                      
                                    Case SpreadsheetName EQ 'HV_R71_6-inch Epi Development.xlsx'
                                        WarpCol   = 'AN'
                                        BowCol    = 'AO'
                                        AvgRow    = SpreadsheetRowIndex + 10
                                        RangeRow  = SpreadsheetRowIndex + 11
                                    Case SpreadsheetName EQ '8-inch New HV Epi Development.xlsx'
                                        WarpCol   = 'AQ'
                                        BowCol    = 'AR'
                                        AvgRow    = SpreadsheetRowIndex + 6
                                        RangeRow  = SpreadsheetRowIndex + 7
                                        Excel_Services('SetCellValue', DocObj, WorksheetName, BowCol, (RangeRow + 2), 'BowCenter')                                     
                                End Case                        
                                // Write Warp values
                                BowMin      = ''
                                BowMax      = ''
                                WarpMin     = ''
                                WarpMax     = ''
                                BowTotal    = 0
                                WarpTotal   = 0
                                NumWarpVals = 0
                                NumBowVals  = 0
                                For each Row in WarpData using @FM setting fPos
                                    Parameter = Row<0, 1>
                                    Pocket    = Row<0, 2>
                                    Value     = Row<0, 3>
                                    Begin Case                                        
                                        Case ( (Parameter EQ 'Bow') or (Parameter EQ 'BowCenter') )
                                            Col       = BowCol
                                            BowTotal += Value
                                            If BowMin EQ '' then BowMin = Value
                                            If BowMax EQ '' then BowMax = Value
                                            If Value LT BowMin then
                                                BowMin = Value
                                            end
                                            If Value GT BowMax then
                                                BowMax = Value
                                            end
                                            NumBowVals += 1
                                        Case Parameter EQ 'Warp'
                                            Col        = WarpCol
                                            WarpTotal += Value
                                            If WarpMin EQ '' then WarpMin = Value
                                            If WarpMax EQ '' then WarpMax = Value
                                            If Value LT WarpMin then
                                                WarpMin = Value
                                            end
                                            If Value GT WarpMax then
                                                WarpMax = Value
                                            end
                                            NumWarpVals += 1
                                        Case Otherwise$
                                            ErrorMessage = 'Error in service ':Service:'. Unrecognized parameter ':Parameter:'.'
                                    End Case
                                Until ErrorMessage NE ''                            
                                    RowIndex  = SpreadsheetRowIndex + Pocket - 1
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, Col, RowIndex, Value)
                                Next Row
                                If ErrorMessage EQ '' then
                                    BowRange  = BowMax  - BowMin
                                    WarpRange = WarpMax - WarpMin
                                    If NumBowVals  GT 0 then BowAvg  = BowTotal  / NumBowVals
                                    If NumWarpVals GT 0 then WarpAvg = WarpTotal / NumWarpVals
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, WarpCol, AvgRow, WarpAvg)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, WarpCol, RangeRow, WarpRange)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, BowCol, AvgRow, BowAvg)
                                    Excel_Services('SetCellValue', DocObj, WorksheetName, BowCol, RangeRow, BowRange)
                                    Excel_Services('SaveDocument', DocObj, TempPath, SpreadsheetPW)
                                    If Error_Services('NoError') then
                                        TempFileHandle     = File_Services('LockFile', TempPath, GENERIC_WRITE$, FILE_SHARE_READ$, OPEN_EXISTING$)
                                        TempFileHandleCopy = TempFileHandle
                                        TempFileSize       = 0
                                        If FileHandle NE FILE_ERROR$ then
                                            // We were able to get the lock
                                            TempFileSize = File_Services('GetFileSize', TempFileHandle)
                                            File_Services('UnlockFile', TempFileHandle)
                                        end
                                        Begin Case
                                            Case TempFileHandleCopy EQ FILE_ERROR$
                                                // Failed to lock file again to verify file size (i.e. that the save operation worked)
                                                ErrorMessage = 'Error in ':Service:' service. Failed to lock document ':TempPath:' to verify the save operation was successful.'      
                                            Case TempFileSize EQ 0
                                                // Save failed - file corrupted
                                                ErrorMessage = 'Error in ':Service:' service. File size equal to zero. Document ':TempPath:' failed to save.'   
                                            Case Otherwise$
                                                // Save successful, so copy temp file back to document (PROD) path.
                                                File_Services('UnlockFile', FileHandle)
                                                CopySucceeded = File_Services('CopyFile', TempPath, DocumentPath)
                                                If CopySucceeded EQ False$ then
                                                    ErrorMessage = 'Error in ':Service:' service. Failed to copy ':TempPath:' to ':DocumentPath:'.'
                                                end
                                        End Case    
                                    end else
                                        // SaveDocument call failed. Get internal error for logging purposes.
                                        ErrorMessage = Error_Services('GetMessage') 
                                    end
                                end                                                           
                            end else
                                // Row index did not match. Log error.
                                ErrorMessage  = 'Error in ':Service:' service. Run ID in row ':SpreadsheetRowIndex
                                ErrorMessage := ' column A in 'SpreadsheetName:' worksheet ':WorksheetName
                                ErrorMessage := ' did not match GaNRunID ':GaNRunID:' in REACT_RUN record ':RDSNo:'.'
                                Excel_Services('CloseDocument', DocObj)       
                            end                                                                                          
                        end else
                            ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file could not be opened by Excel_Services.'
                        end
                        OSDelete TempPath                                     
                    end else
                        ErrorMessage = 'Error in ':Service:' service. ':DocumentPath:' file is in use an cannot be updated at this time.'                        
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. WARP data is missing in IQS.' 
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Run ID for RDS ':RDSNo:' is null.'      
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No Excel row index found for RDS ':RDSNo:' - Run ID ':GaNRunID:'.' 
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. RDSNo is null.'
    end
    
    If DocObj NE '' then Excel_Services('CloseDocument', DocObj)
    If ( (FileHandle NE '') and (FileHandle NE FILE_ERROR$) ) then File_Services('UnlockFile', FileHandle)
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service



//----------------------------------------------------------------------------------------------------------------------
// GetAFMData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetAFMData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')                                                                           
            Query    = "SELECT td.F_NAME, dd.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'AFM Pit Count' or td.F_NAME = 'AFM Roughness') AND "
            Query   := "      dd.F_DSGP  = 1342510661 AND "                            
            Query   := "dd.F_NAME IS NOT NULL AND "
            Query   := "sd.F_SGRP = "
	        Query   := "(SELECT MAX(sd.F_SGRP) "
	        Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT "
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query   := "WHERE pd.F_NAME = '":PartNo:"'  AND "
            Query   := "      pr.F_NAME = '":Reactor:"' AND "
            Query   := "      pl.F_NAME = '":RunNo:"'   AND "
            Query   := "      se.F_FLAG = 0 AND "
            Query   := "      (td.F_NAME = 'AFM Pit Count' or td.F_NAME = 'AFM Roughness') AND "
            Query   := "      dd.F_DSGP  = 1342510661 AND dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME"

            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetUVData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetUVData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_TSNO, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'UV Broken' or td.F_NAME = 'UV Cracking (>7mm)' or "
            Query   := "       td.F_NAME = 'UV Cracking (3-7mm)' or td.F_NAME = 'UV Crazing' or "
            Query   := "       td.F_NAME = 'UV Non-rotation' or td.F_NAME = 'UV Scratch (count)' or "
            Query   := "       td.F_NAME = 'UV Slip (count)' or td.F_NAME = 'UV-SPE (>1mm) count' or "
            Query   := "       td.F_NAME = 'UV-SPE (count)' or td.F_NAME = 'UV True Haze') AND "
            Query   := "       dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND "                            
            Query   := "       sd.F_SGRP = "
	        Query   := "       (SELECT MAX(sd.F_SGRP) "
	        Query   := "       FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
	        Query   := "       JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "
	        Query   := "       JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST  = td.F_TEST "
	        Query   := "       JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART  = pd.F_PART "
	        Query   := "       JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS  = pr.F_PRCS "
	        Query   := "       JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP  = sd.F_SGRP "
	        Query   := "       LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
	        Query   := "       WHERE pd.F_NAME = '":PartNo:"'	AND       pr.F_NAME = '":Reactor:"'	AND "
	        Query   := "            pl.F_NAME = '":RunNo:"'	AND se.F_FLAG = 0 AND "
	        Query   := "            (td.F_NAME = 'UV Broken' or td.F_NAME = 'UV Cracking (>7mm)' or td.F_NAME = 'UV Cracking (3-7mm)' or "
		    Query   := "            td.F_NAME  = 'UV Crazing' or td.F_NAME = 'UV Non-rotation' or td.F_NAME = 'UV Scratch (count)' or "
		    Query   := "            td.F_NAME  = 'UV Slip (count)' or td.F_NAME = 'UV-SPE (>1mm) count' or td.F_NAME = 'UV-SPE (count)' or "
		    Query   := "            td.F_NAME  = 'UV True Haze') AND " 
	        Query   := "            dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME, se.F_TSNO"

            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetRPMData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetRPMData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'Epi Thickness Mean' or td.F_NAME = 'Epi Thickness Std Dev %') AND "
            Query   := "      dd.F_DSGP  = 1337859646 AND "                            
            Query   := "      dd.F_NAME IS NOT NULL AND "
            Query   := "	  sd.F_SGRP = "
            Query   := "      (SELECT MAX(sd.F_SGRP) "
            Query   := "      FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "      JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT "
            Query   := "      JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query   := "      JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query   := "      JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query   := "      JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query   := "      LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query   := "      WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "            pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "            pl.F_NAME = '":RunNo:"'	    AND "                                                       
            Query   := "            se.F_FLAG = 0				AND "
            Query   := "            (td.F_NAME = 'Epi Thickness Mean' or td.F_NAME = 'Epi Thickness Std Dev %') AND "
            Query   := "            dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME"     

            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetPLData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetPLData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'PL Ratio' or td.F_NAME = 'BandEdge_V' or td.F_NAME = 'YellowBand_V') AND "
            Query   := "      dd.F_DSGP  = 1337859646 AND "                            
            Query   := "      dd.F_NAME IS NOT NULL AND "
            Query   := "	  sd.F_SGRP = "
            Query   := "      (SELECT MAX(sd.F_SGRP)  "
            Query   := "      FROM [IRMNSPC].[dbo].[SGRP_EXT] se  " 
            Query   := "      JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT  " 
            Query   := "      JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST  "
            Query   := "      JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART  "
            Query   := "      JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS  "
            Query   := "      JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP  "
            Query   := "      LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC  "
            Query   := "      WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "            pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "            pl.F_NAME = '":RunNo:"'	    AND "                                                       
            Query   := "            se.F_FLAG = 0				AND "
            Query   := "            (td.F_NAME = 'PL Ratio' or td.F_NAME = 'BandEdge_V' or "
            Query   := "             td.F_NAME  = 'YellowBand_V') AND "
            Query   := "            dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME"     

            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetPRData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetPRData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo      = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor    = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo     = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')
            EpiPartNo  = Xlate('REACT_RUN', RDSNo, 'EPI_PART_NO', 'X')
            WfrSize    = Xlate('EPI_PART', EpiPartNo, 'SUB_WAFER_SIZE', 'X')
            
            If WfrSize EQ '200 mm 8 in' then
                Query  = "SELECT td.F_NAME, se.F_SBNO,  se.F_VAL "
                Query := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se " 
                Query := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT " 
                Query := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST " 
                Query := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART " 
                Query := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS " 
                Query := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP " 
                Query := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC " 
                Query := "WHERE pd.F_NAME  = '":PartNo:"' AND       pr.F_NAME = '":Reactor:"' " 
                Query := "    AND pl.F_NAME = '":RunNo:"' AND       se.F_FLAG = 0 " 
                Query := "    AND (td.F_NAME = 'Barrier_Composition_RPM_XY' or td.F_NAME = 'Y-Coord') " 
                Query := "    AND dd.F_DSGP = 1337859646 " 
                Query := "    AND dd.F_NAME IS NOT NULL " 	
                Query := "    AND se.F_SGRP = " 
                Query := "    (SELECT TOP 1 se.F_SGRP " 
                Query := "        FROM [IRMNSPC].[dbo].[SGRP_EXT] se " 
                Query := "        JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT " 
                Query := "        JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST " 
                Query := "        JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART " 
                Query := "        JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS " 
                Query := "        JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP " 
                Query := "        LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC " 
                Query := "        WHERE pd.F_NAME = '":PartNo:"' AND       pr.F_NAME = '":Reactor:"' AND       pl.F_NAME = '":RunNo:"' " 
                Query := "        AND       se.F_FLAG = 0    AND       (td.F_NAME = 'Barrier_Composition_RPM_XY' or td.F_NAME = 'Y-Coord') " 
                Query := "        AND        dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) " 
                Query := "ORDER BY td.F_NAME, se.F_SBNO DESC" 
            end else                                                                    
                Query  = "SELECT td.F_NAME, se.F_SBNO, se.F_VAL "                                           
                Query := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
                Query := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
                Query := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
                Query := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
                Query := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
                Query := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
                Query := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
                Query := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
                Query := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
                Query := "      pl.F_NAME = '":RunNo:"'	    AND "                                                       
                Query := "      se.F_FLAG = 0				AND "
                Query := "      (td.F_NAME = 'PR Peak' or td.F_NAME = 'PR Barrier Composition') AND "
                Query := "      dd.F_DSGP  = 1337859592 AND "                            
                Query := "      dd.F_NAME IS NOT NULL AND "
                Query := "      sd.F_SGRP  = " 
                Query := "          (SELECT MAX(sd.F_SGRP) "
                Query := "          FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
                Query := "          JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT "
                Query := "          JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
                Query := "          JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
                Query := "          JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
                Query := "          JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
                Query := "          LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
                Query := "          WHERE pd.F_NAME  = '":PartNo:"'  AND "
                Query := "                pr.F_NAME  = '":Reactor:"' AND "
                Query := "                pl.F_NAME  = '":RunNo:"'   AND "
                Query := "                se.F_FLAG = 0              AND "
                Query := "                (td.F_NAME = 'PR Peak' or td.F_NAME = 'PR Barrier Composition') AND "  
                Query := "                dd.F_DSGP  = 1337859592 and dd.F_NAME IS NOT NULL) "
                Query := "ORDER BY td.F_NAME, se.F_SBNO DESC"                
            end

            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// GetWARPData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetWARPData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo      = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor    = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo     = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')
            EpiPartNo  = Xlate('REACT_RUN', RDSNo, 'EPI_PART_NO', 'X')
            WfrSize    = Xlate('EPI_PART', EpiPartNo, 'SUB_WAFER_SIZE', 'X')    
            Query      = ''                                                                    
            Query      = "SELECT td.F_NAME, se.F_TSNO, se.F_VAL "                                            
            Query     := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query     := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query     := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query     := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query     := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query     := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query     := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query     := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query     := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query     := "      pl.F_NAME = '":RunNo:"'	    AND "                                                       
            Query     := "      se.F_FLAG = 0				AND "
            Query     := "      (td.F_NAME = 'Warp' "
            If WfrSize EQ '200 mm 8 in' then
                Query := "or td.F_NAME = 'BowCenter') AND "                       
            end else
                Query := "or td.F_NAME = 'Bow') AND "   
            end
            Query := "      dd.F_DSGP  = 1337859646 AND "                            
            Query := "      dd.F_NAME IS NOT NULL AND "
            Query := "      sd.F_SGRP = "
            Query := "      (SELECT MAX(sd.F_SGRP) "
            Query := "      FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query := "      JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT  "
            Query := "      JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query := "      JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query := "      JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query := "      JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query := "      LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query :=        "WHERE  pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query := "              pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query := "              pl.F_NAME = '":RunNo:"'	    AND "                                                       
            Query := "              se.F_FLAG = 0				AND "
            Query := "              (td.F_NAME = 'Warp' "
            If WfrSize EQ '200 mm 8 in' then
                Query := "or td.F_NAME = 'BowCenter') AND "                       
            end else
                Query := "or td.F_NAME = 'Bow') AND "   
            end
            Query := "              dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query := "ORDER BY td.F_NAME"     

            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetCandelaData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetCandelaData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo      = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor    = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            EpiPartNo  = Xlate('REACT_RUN', RDSNo, 'EPI_PART_NO', 'X')
            WfrSize    = Xlate('EPI_PART', EpiPartNo, 'SUB_WAFER_SIZE', 'X')
            PartNo     = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query      = ''                                                                    
            Query      = "SELECT td.F_NAME, se.F_TSNO, se.F_VAL "                                            
            Query     := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query     := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query     := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query     := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query     := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query     := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query     := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query     := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query     := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query     := "      pl.F_NAME = '":RunNo:"'	    AND "                                                       
            Query     := "      se.F_FLAG = 0				AND "
            Query     := "      (td.F_NAME = 'Candela Cracking' or td.F_NAME = 'Candela Cracking Acceptable' or "
            Query     := "       td.F_NAME  = 'Candela Crater' or td.F_NAME = 'Candela LPD Large' or "
            Query     := "       td.F_NAME  = 'Candela LPD Medium' or td.F_NAME = 'Candela LPD Small' or "
            Query     := "       td.F_NAME  = 'Candela Pits' or td.F_NAME = 'Candela SPE' or "
            Query     := "       td.F_NAME  = 'Candela Spirals' "
            If WfrSize EQ '200 mm 8 in' then
                Query := "or td.F_NAME = 'Candela Total Defects') AND "                       
            end else
                Query := ") AND "   
            end
            Query := "       dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND "
            Query := "       sd.F_SGRP = "
            Query := "       (SELECT MAX(sd.F_SGRP) "
            Query := "       FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query := "       JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT "
            Query := "       JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query := "       JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query := "       JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query := "       JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query := "       LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query := "       WHERE pd.F_NAME = '":PartNo:"'	    AND "
            Query := "             pr.F_NAME = '":Reactor:"'    AND "
            Query := "             pl.F_NAME = '":RunNo:"'	    AND "
            Query := "             se.F_FLAG  = 0 "
            Query := "       AND (td.F_NAME = 'Candela Cracking' or td.F_NAME = 'Candela Cracking Acceptable' or td.F_NAME = 'Candela Crater' or "
            Query := "            td.F_NAME = 'Candela LPD Large' or td.F_NAME = 'Candela LPD Medium' or td.F_NAME = 'Candela LPD Small' or td.F_NAME = 'Candela Pits' or "
            Query := "            td.F_NAME = 'Candela SPE' or td.F_NAME = 'Candela Spirals' "
            If WfrSize EQ '200 mm 8 in' then
                Query := "or td.F_NAME = 'Candela Total Defects') AND "                       
            end else
                Query := ") AND "   
            end  
            Query := "      dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query := "ORDER BY td.F_NAME"

            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetXRDAlumData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetXRDAlumData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, dd.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'Al% B.B.' or td.F_NAME = 'Al% Barrier' or td.F_NAME = 'Al% SLS' or "
            Query   := "       td.F_NAME = 'Al% TL1' or td.F_NAME = 'SL Period') AND "                       
            Query   := "       dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND "
            Query   := "       sd.F_SGRP = "
            Query   := "       (SELECT MAX(sd.F_SGRP) "
            Query   := "       FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "       JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT  "
            Query   := "       JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query   := "       JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query   := "       JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query   := "       JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query   := "       LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query   := "       WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "             pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "             pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "             se.F_FLAG = 0				AND "
            Query   := "             (td.F_NAME = 'Al% B.B.' or td.F_NAME = 'Al% Barrier' or "
            Query   := "              td.F_NAME  = 'Al% SLS' or td.F_NAME = 'Al% TL1' or td.F_NAME = 'SL Period') AND "
            Query   := "              dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME, se.F_SBNO"

            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetXRDThicknessData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetXRDThicknessData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, dd.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'XRR Thickness (nm)' or td.F_NAME = 'FWHM 006' or "
            Query   := "       td.F_NAME = 'FWHM 105') AND "                       
            Query   := "       dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND "
            Query   := "       sd.F_SGRP = "
            Query   := "       (SELECT MAX(sd.F_SGRP) "
            Query   := "       FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "       JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT  "
            Query   := "       JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query   := "       JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query   := "       JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query   := "       JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query   := "       LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query   := "       WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "             pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "             pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "             se.F_FLAG = 0				AND "
            Query   := "             (td.F_NAME = 'XRR Thickness (nm)' or td.F_NAME = 'FWHM 006' or "
            Query   := "              td.F_NAME = 'FWHM 105') AND "
            Query   := "              dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME, se.F_SBNO"

            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetXRDWtAvgData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetXRDWtAvgData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, dd.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'Al% B.B. WTAVG' or "
            Query   := "       td.F_NAME = 'Al% Barrier WTAVG' or td.F_NAME = 'Al% SLS WTAVG' or "
            Query   := "       td.F_NAME = 'Al% TL1 WTAVG' or td.F_NAME = 'SL Period WTAVG') AND "                       
            Query   := "       dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND "
            Query   := "       sd.F_SGRP = "
            Query   := "       (SELECT MAX(sd.F_SGRP) "
            Query   := "       FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "       JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT  "
            Query   := "       JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query   := "       JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query   := "       JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query   := "       JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query   := "       LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query   := "       WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "             pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "             pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "             se.F_FLAG = 0				AND "
            Query   := "             (td.F_NAME = 'Al% B.B. WTAVG' or "
            Query   := "              td.F_NAME = 'Al% Barrier WTAVG' or td.F_NAME = 'Al% SLS WTAVG' or "
            Query   := "              td.F_NAME = 'Al% TL1 WTAVG' or td.F_NAME = 'SL Period WTAVG') AND "
            Query   := "              dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME, se.F_SBNO"

            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetXRDCoordData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetXRDXCoordData(RDSNo)

    Response = ''
    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, dd.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      td.F_NAME = 'X-Coord' AND "
            Query   := "      dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND se.F_SBNO > 0 AND "
            Query   := "      sd.F_TRTM = "
            Query   := "         (SELECT MAX(sd.F_TRTM) "
            Query   := "         FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "         JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "
            Query   := "         JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "
            Query   := "         JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "
            Query   := "         JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "
            Query   := "         JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "
            Query   := "         LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "
            Query   := "         WHERE pd.F_NAME = '":PartNo:"'	    AND "
            Query   := "               pr.F_NAME = '":Reactor:"'	AND "
            Query   := "               pl.F_NAME = '":RunNo:"'	    AND " 
            Query   := "               se.F_FLAG = 0 AND td.F_NAME = 'X-Coord' AND "
            Query   := "               dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL)"
            
            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetXRDCoordData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetXRDYCoordData(RDSNo)

    Response = ''
    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, dd.F_NAME, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      td.F_NAME = 'Y-Coord' AND "
            Query   := "      dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND se.F_SBNO > 0 AND "
            Query   := "      sd.F_TRTM = "
            Query   := "         (SELECT MAX(sd.F_TRTM) "
            Query   := "         FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "         JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "
            Query   := "         JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "
            Query   := "         JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "
            Query   := "         JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "
            Query   := "         JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "
            Query   := "         LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "
            Query   := "         WHERE pd.F_NAME = '":PartNo:"'	    AND "
            Query   := "               pr.F_NAME = '":Reactor:"'	AND "
            Query   := "               pl.F_NAME = '":RunNo:"'	    AND " 
            Query   := "               se.F_FLAG = 0 AND td.F_NAME = 'Y-Coord' AND "
            Query   := "               dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL)"
            
            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetBVData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetBVData(RDSNo, Parameter=BV_PARAMETERS)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      td.F_NAME = '":Parameter:"' AND "
            Query   := "      dd.F_DSGP  = 1337859646 AND "                            
            Query   := "dd.F_NAME IS NOT NULL "
            Query   := "AND sd.F_SGRP = "
            Query   :=            "(SELECT MIN(sd.F_SGRP) "
            Query   :=            "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   :=            "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "
            Query   :=            "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "
            Query   :=            "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "
            Query   :=            "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "
            Query   :=            "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "
            Query   :=            "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "
            Query   :=            "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   :=            "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   :=            "      pl.F_NAME = '":RunNo:"'	AND "
            Query   :=            "      se.F_FLAG  = 0 AND td.F_NAME = '":Parameter:"' AND "
            Query   :=            "      dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL)"
                        
            If Query NE '' then Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)

            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
            
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetHallData
//
//
//----------------------------------------------------------------------------------------------------------------------
Service GetHallData(RDSNo)

    If RDSNo NE '' then
           
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        If Error_Services('NoError') then 										 
            RunNo    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            Reactor  = 'R':ReactRunRec<REACT_RUN_REACTOR$>
            PartNo   = Xlate('REACT_RUN', RDSNo, 'WFR_TRACK_PART', 'X')       
            Query    = ''                                                                    
            Query    = "SELECT td.F_NAME, se.F_SBNO, se.F_VAL "                                            
            Query   := "FROM [IRMNSPC].[dbo].[SGRP_EXT] se "                                                          
            Query   := "JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT  = pl.F_LOT "                                  
            Query   := "JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST = td.F_TEST "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART = pd.F_PART "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS = pr.F_PRCS "                                 
            Query   := "JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP = sd.F_SGRP "                                 
            Query   := "LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC = dd.F_DESC "                            
            Query   := "WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "      pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "      pl.F_NAME = '":RunNo:"'	AND "                                                       
            Query   := "      se.F_FLAG = 0				AND "
            Query   := "      (td.F_NAME = 'Hall Rs' or td.F_NAME = 'Mobility' or "
            Query   := "       td.F_NAME  = 'Sheet Concentration') AND "                       
            Query   := "       dd.F_DSGP  = 1337859646 AND dd.F_NAME IS NOT NULL AND "
            Query   := "	   sd.F_SGRP = "
            Query   := "	  (SELECT MAX(sd.F_SGRP) "
            Query   := "	  FROM [IRMNSPC].[dbo].[SGRP_EXT] se "
            Query   := "	  JOIN [IRMNSPC].[dbo].[PART_LOT] pl on se.F_LOT        = pl.F_LOT "
            Query   := "	  JOIN [IRMNSPC].[dbo].[TEST_DAT] td on se.F_TEST       = td.F_TEST "
            Query   := "	  JOIN [IRMNSPC].[dbo].[PART_DAT] pd on se.F_PART       = pd.F_PART "
            Query   := "	  JOIN [IRMNSPC].[dbo].[PRCS_DAT] pr on se.F_PRCS       = pr.F_PRCS "
            Query   := "	  JOIN [IRMNSPC].[dbo].[SGRP_DSC] sd on se.F_SGRP       = sd.F_SGRP "
            Query   := "	  LEFT JOIN [IRMNSPC].[dbo].[DESC_DAT] dd on sd.F_DESC  = dd.F_DESC "
            Query   := "      WHERE pd.F_NAME = '":PartNo:"'	AND "                                                   
            Query   := "            pr.F_NAME = '":Reactor:"'	AND "                                                       
            Query   := "            pl.F_NAME = '":RunNo:"'	    AND "                                                       
            Query   := "            se.F_FLAG = 0				AND "
            Query   := "	        (td.F_NAME = 'Hall Rs' or td.F_NAME = 'Mobility' or "
            Query   := "             td.F_NAME  = 'Sheet Concentration') AND "
            Query   := "	         dd.F_DSGP  = 1337859646 and dd.F_NAME IS NOT NULL) "
            Query   := "ORDER BY td.F_NAME, se.F_SBNO"

            Response = SQL_Services('PostSQLRequest', 'IQSDMS1', Query)
            Convert @FM to @VM in Response
            Convert @RM to @FM in Response
        end
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// UpdateExcelIndexes
//
//
//----------------------------------------------------------------------------------------------------------------------
Service UpdateExcelIndexes() 

    StartRow         = 5 ; // All spreadsheets use the first four rows for header information 
    SpreadsheetInfo  = Engineering_Services('GetSpreadSheets')
    SpreadsheetNames = SpreadsheetInfo<1>
    WorksheetNames   = SpreadsheetInfo<2>
    SpreadsheetPWs   = SpreadsheetInfo<3>
    SpreadsheetDirs  = SpreadsheetInfo<4>
    Offsets          = SpreadsheetInfo<5>
    hDict            = Database_Services('GetTableHandle', 'DICT.REACT_RUN')
    If Error_Services('NoError') then
        
        For each SpreadSheetName in SpreadsheetNames using @VM setting sPos
            
            WorksheetName  = WorksheetNames<0, sPos>
            SpreadsheetPW  = SpreadsheetPWs<0, sPos>
            SpreadSheetDir = SpreadsheetDirs<0, sPos>
            Offset         = Offsets<0, sPos>
            
            DocumentPath    = SpreadsheetDir:SpreadsheetName
            DocObj          = Excel_Services('OpenDocument', DocumentPath, SpreadsheetPW)
            If Error_Services('NoError') then
                
                // Process Spreadsheet
                MaxValidRow = ''                
                NumExcelRows = Excel_Services('GetNumRows', DocObj, WorksheetName)
                StartRow     = (NumExcelRows - (30 * Offset) )
                If StartRow LT 5 then StartRow = 5
                If Error_Services('NoError') then
                    Row         = StartRow
                    RunIDRow    = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', Row)
                    ExcelRunID  = RunIDRow[1, 12]       
                    RunNo       = ExcelRunID[1, 7]                        
                    PrevRunNo   = RunNo
                    Row        += 1
                    Loop
                        RunIDRow   = Excel_Services('GetCellValue', DocObj, WorksheetName, 'A', Row)
                        ExcelRunID = RunIDRow[1, 12]
                        RunNo      = ExcelRunID[1, 7]
                        ValidRunID = ( (ExcelRunID Matches "'69'5N'hvfet'") or (ExcelRunID Matches "'69'5N'mvfet'") or (ExcelRunID Matches "'71'5N'hvfet'") or (ExcelRunID Matches "'71'5N'mvfet'") )
                        If ( (ValidRunID EQ True$) and (RunNo NE PrevRunNo) ) then
                            MaxValidRow = Row
                            GoSub ClearCursors
                            Query       = 'GAN_RUN_ID' : @VM : ExcelRunID : @FM
                            RDSNo       = ''
                            Option      = ''
                            Flag        = ''
                            Btree.Extract(Query, 'REACT_RUN', hDict, RDSNo, Option, Flag)
                            If RDSNo NE '' then
                                ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
                                If Error_Services('NoError') then
                                    RRIndex     = ReactRunRec<REACT_RUN_EA_ROW_INDEX$>
                                    If RRIndex NE Row then
                                        ReactRunRec<REACT_RUN_EA_ROW_INDEX$>      = Row
                                        Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
                                    end
                                end
                            end
                        end
                        Row       += 1
                        PrevRunNo  = RunNo
                    Until Row GT NumExcelRows
                    Repeat
                end
                // Update APP_INFO table containing max row counters
                NextValidRow = MaxValidRow + Offset
                Database_Services('WriteDataRow', 'APP_INFO', WorksheetName, NextValidRow, True$, False$, True$)                    
                Excel_Services('CloseDocument', DocObj)                   
            end else
                ErrMsg = Error_Services('GetMessage')                    
            end
              
        Next SpreadSheetName
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// PostEARequest
//
// Input:
//      RDSNo  - [Required]
//      Stage  - [Required]
//      WfrID  - [Optional]
//      ToolID - [Optional]
//
//----------------------------------------------------------------------------------------------------------------------
Service PostEARequest(RDSNo, Stage=STAGES, WfrID, ToolID)

    If ( (RDSNo NE '') and (Stage NE '') ) then
        ErrorMessage = ''
        LogData      = ''
        LogData<1>   = LoggingDTM
        LogData<2>   = RDSNo
        LogData<3>   = Stage
        // Look for a pre-existing request in the queue
        hDict        = Database_Services('GetTableHandle', 'DICT.EA_REQUESTS')
        If Error_Services('NoError') then
            RequestKeyID = ''   
            Query        = 'RDS_NO' : @VM : RDSNo : @FM : 'STAGE' : @VM : Stage : @FM
            Option       = ''
            Flag         = ''
            Btree.Extract(Query, 'EA_REQUESTS', hDict, RequestKeyID, Option, Flag)
            If RequestKeyID EQ '' then
                // This is a new request
                RequestDate                      = Date()
                RequestTime                      = Time()
                RequestKeyID                     = RDSNo:'*':Stage:'*':RequestDate :'*':RequestTime
                RequestRow                       = ''
                RequestRow<EA_REQUESTS.WFR_ID$>  = WfrID
                RequestRow<EA_REQUESTS.TOOL_ID$> = ToolID
                Database_Services('WriteDataRow', 'EA_REQUESTS', RequestKeyID, RequestRow, False$, False$, False$)
                LogData<4>                       = 'EA request posted'
            end else
                LogData<4> = 'Duplicate EA request found, request ignored.'
            end
            Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
        end
    end else
        Error_Services('Add', 'Null parameter passed into service call. All parameters are required.')
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// ProcessEARequests
//
//----------------------------------------------------------------------------------------------------------------------
Service ProcessEARequests()

    hSysLists   = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID then
        Tablename = 'EA_REQUESTS'
        hEARequests = Database_Services('GetTableHandle', Tablename)
        If Error_Services('NoError') then
            Sentence = 'SELECT ':Tablename:' BY RESPONSE_DATE BY RESPONSE_TIME BY REQUEST_DATE BY REQUEST_TIME'
            Set_Status(0)
            GoSub ClearCursors
            RList(Sentence, TARGET_ACTIVELIST$, '', '', '')
            // Just process one request at a time so that this service doesn't tie up an OEngine for too long.
            ReadNext RequestKeyID then                      
                Lock hEARequests, RequestKeyID then
                    RequestRow = Database_Services('ReadDataRow', 'EA_REQUESTS', RequestKeyID)
                    RDSNo      = Field(RequestKeyID, '*', 1)
                    Stage      = Field(RequestKeyID, '*', 2)
*                    If ( (RDSNo EQ 463119) and (Stage EQ 'GROWTH') ) then debug
                    WfrID      = RequestRow<EA_REQUESTS.WFR_ID$>
                    ToolID     = RequestRow<EA_REQUESTS.TOOL_ID$>
                    RDSClosed  = False$ ; //Xlate('REACT_RUN', RDSNo, REACT_RUN_DISP_COMPLETE$, 'X')
                    If RDSClosed EQ True$ then
                        // No need to process this request
                        // Request was successful, so remove the request from the queue
                        Database_Services('DeleteDataRow', 'EA_REQUESTS', RequestKeyID, True$)
                        LogData    = ''
                        LogData<1> = Oconv(Date(), 'D4/')  : ' ' : Oconv(Time(), 'MTS')
                        LogData<2> = RDSNo
                        LogData<3> = Stage
                        LogData<4> = 'Run closed. Deleting EA request from queue.'
                        Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
                    end else
                        If Stage EQ 'GROWTH' then
                            // Ensure this GROWTH request is the oldest GROWTH request (i.e. ensure the GROWTH requests
                            // are processed in order).
                            Sentence         = 'SELECT ':Tablename:' WITH STAGE EQ "GROWTH" BY REQUEST_DATE BY REQUEST_TIME'
                            Set_Status(0)
                            GoSub ClearCursors
                            RList(Sentence, TARGET_ACTIVELIST$, '', '', '')
                            ReadNext GrowthKeyID then
                                If GrowthKeyID NE RequestKeyID then
                                    // This is not the oldest GROWTH request. Push this request to the back of the queue.
                                    Error_Services('Add', 'Older GROWTH request found in queue. Pushing this request to back of queue.')
                                end
                            end
                        end
                        If Error_Services('NoError') then
                            Engineering_Services('ProcessEARequest', RDSNo, Stage, WfrID, ToolID)
                            If Error_Services('NoError') then
                                // Request was successful, so remove the request from the queue
                                Database_Services('DeleteDataRow', 'EA_REQUESTS', RequestKeyID, True$)
                                LogData    = ''
                                LogData<1> = Oconv(Date(), 'D4/')  : ' ' : Oconv(Time(), 'MTS')
                                LogData<2> = RDSNo
                                LogData<3> = Stage
                                LogData<4> = 'Request successfully processed. Deleting EA request from queue.'
                                Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
                            end else
                                ErrorMsg                               = Error_Services('GetMessage')
                                LogData                                = ''
                                LogData<1>                             = Oconv(Date(), 'D4/')  : ' ' : Oconv(Time(), 'MTS')
                                LogData<2>                             = RDSNo
                                LogData<3>                             = Stage
                                LogData<4>                             = 'Request failed to process. Error message: ':ErrorMsg
                                Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)                        
                                // Keep request in the queue (i.e. do not delete it)
                                RequestRow<EA_REQUESTS.RESPONSE_TIME$> = Time()
                                RequestRow<EA_REQUESTS.RESPONSE_DATE$> = Date()
                                RequestRow<EA_REQUESTS.RESPONSE$>      = ErrorMsg
                                Database_Services('WriteDataRow', 'EA_REQUESTS', RequestKeyID, RequestRow, True$, False$, True$)
                                Unlock hEARequests, RequestKeyID else
                                    Error_Services('Add', 'Error unlocking ':RequestKeyID:', ':Tablename:' within ':Service)
                                end
                            end                            
                        end else
                            ErrorMsg                               = Error_Services('GetMessage')
                            LogData                                = ''
                            LogData<1>                             = Oconv(Date(), 'D4/')  : ' ' : Oconv(Time(), 'MTS')
                            LogData<2>                             = RDSNo
                            LogData<3>                             = Stage
                            LogData<4>                             = 'Request failed to process. Error message: ':ErrorMsg
                            Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)                        
                            // Keep request in the queue (i.e. do not delete it)
                            RequestRow<EA_REQUESTS.RESPONSE_TIME$> = Time()
                            RequestRow<EA_REQUESTS.RESPONSE_DATE$> = Date()
                            RequestRow<EA_REQUESTS.RESPONSE$>      = ErrorMsg
                            Database_Services('WriteDataRow', 'EA_REQUESTS', RequestKeyID, RequestRow, True$, False$, True$)
                            Unlock hEARequests, RequestKeyID else
                                Error_Services('Add', 'Error unlocking ':RequestKeyID:', ':Tablename:' within ':Service)
                            end    
                        end                       
                    end
                end
            end
        end else
            ErrorMsg = Error_Services('GetMessage')
        end
        Unlock hSysLists, ServiceKeyID else Null    
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// ProcessEARequest
//
// Input:
//      RDSNo  - [Required]
//      Stage  - [Required]
//      WfrID  - [Optional]
//      ToolID - [Optional]
//
//----------------------------------------------------------------------------------------------------------------------
Service ProcessEARequest(RDSNo, Stage, WfrID, ToolID)
    
    If ( (RDSNo NE '') and (Stage NE '') ) then
                
            Begin Case
                Case Stage EQ 'CHAR'
                    Engineering_Services('PostCHARData', RDSNo)
                    
                Case Stage EQ 'GROWTH'                   
                    Engineering_Services('PostGrowthData', RDSNo)
                    
                Case ( (Stage EQ 'UV') or (Stage EQ 'UV_PRE') or (Stage EQ 'UV_PST') )
                    Engineering_Services('PostUVData', RDSNo)
                    
                Case Stage EQ 'WARP'
                    Engineering_Services('PostWARPData', RDSNo)

                Case ( (Stage EQ 'CAN') or (Stage EQ 'CAN_PRE') or (Stage EQ 'CAN_PST') )
                    Engineering_Services('PostCANData', RDSNo) 
                    
                Case Stage EQ 'XRD'
                    Engineering_Services('PostXRDData', RDSNo)
                    
                Case Stage EQ 'AFM'                   
                    Engineering_Services('PostAFMData', RDSNo)
                    
                Case Stage EQ 'RPM'
                    Engineering_Services('PostRPMData', RDSNo)
                    
                Case Stage EQ 'PR'
                    Engineering_Services('PostPRData', RDSNo)
                    
                Case Stage EQ 'BV'
                    Engineering_Services('PostBVData', RDSNo)
                    
                Case ( (Stage EQ 'HALL') or (Stage EQ 'RTA_HALL') or (Stage EQ 'HALL_PGAN') )
                    Engineering_Services('PostHALLData', RDSNo)
    
            End Case

    end else
        Error_Services('Add', 'Null RDSNo passed into service ':Service:'.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// ClearEARequests
//
// Input:
//      RDSNo  - [Required]
//
//----------------------------------------------------------------------------------------------------------------------
Service ClearEARequests(RDSNo)

    If RDSNo NE ''then
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = RDSNo
        // Look for a pre-existing request in the queue
        hDict      = Database_Services('GetTableHandle', 'DICT.EA_REQUESTS')
        If Error_Services('NoError') then
            RequestKeyIDs = ''   
            Query         = 'RDS_NO' : @VM : RDSNo : @FM
            Option        = ''
            Flag          = ''
            Btree.Extract(Query, 'EA_REQUESTS', hDict, RequestKeyIDs, Option, Flag)
            If RequestKeyIDs NE '' then
                LogKeys    = RequestKeyIDs
                Swap @VM with ',' in LogKeys
                LogData<3> = 'Removing the following requests from the queue: ':LogKeys
                For each RequestKeyID in RequestKeyIDs using @VM setting vPos
                    Database_Services('DeleteDataRow', 'EA_REQUESTS', RequestKeyID)                   
                Next RequestKeyID
            end           
            Logging_Services('AppendLog', QueueObjLog, LogData, @RM, @FM)
        end
    end else
        Error_Services('Add', 'Null parameter passed into service call. All parameters are required.')
    end
    
end service


Service UpdateAll(RDSNo)

    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    WfrIDs      = ReactRunRec<REACT_RUN_IN_WFR_ID$>
    WfrStages   = ReactRunRec<REACT_RUN_WFR_STAGE$>
    EAStages    = 'UV,UV_PRE,WARP,CAN,CAN_PRE,UV_PST,CAN_PST,HALL,XRD,AFM,RPM,PR,BV'
    // All runs must complete the GROWTH stage so simply submit a request for that stage.
    Engineering_Services('PostEARequest', RDSNo, 'GROWTH')
    // The remaining stages may or may not be completed, so ensure at least one wafer has completed
    // the stage before submitting a request.
    For each Stage in EAStages using ','
        StageComp = False$
        Locate Stage in WfrStages using @VM setting vPos then           
            For each WfrID in WfrIDs using @VM
                FormattedWfrID = WfrID
                Swap '*' with '.' in FormattedWfrID
                RunStageWfrID  = RDSNo:'*':Stage:'*':FormattedWfrID
                StageStatus    = Xlate('RUN_STAGE_WFR', RunStageWfrID, 'STATUS', 'X')
                StageComp      = (StageStatus EQ 'COMP')
            Until StageComp EQ True$
            Next WfrID
        end
        If StageComp EQ True$ then Engineering_Services('PostEARequest', RDSNo, Stage)
    Next Stage
    
end service


Service GetQueueReport(Stage)
       
    Sentence   = 'SELECT EA_REQUESTS WITH STAGE EQ "':Stage:'"'
    Set_Status(0)
    GoSub ClearCursors
    RList(Sentence, TARGET_ACTIVELIST$, '', '', '')
    EOF        = False$
    ReportPath = Environment_Services('GetReportsRootPath')
    Filename   = @User4:' - ':Stage:' - Queue Report.csv'
    SavePath   = ReportPath:'\':Filename
    ReportText = ''
    LineNo     = 0
    Loop
        ReadNext RequestKeyID else EOF = True$
        If RequestKeyID NE '' then
            LineNo                += 1
            RDSNo                  = Field(RequestKeyID, '*', 1)
            GaNRunID               = Xlate('REACT_RUN', RDSNo, 'GAN_RUN_ID', 'X')
            ReportText<LineNo, 1>  = GaNRunID
            ReportText<LineNo, 2>  = RDSNo
        end
    Until EOF EQ True$
    Repeat
    Swap @VM with ',' in ReportText
    Swap @FM with CRLF$ in ReportText
    OSWrite ReportText to SavePath
    
end service


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ClearCursors:
    For counter = 0 to 8
        ClearSelect counter
    Next counter
return

