Function WO_MAT_QA_Actions(Action, CalcColName, FSList, Handle, Name, FMC, Record, Status, OrigRecord, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10)
/***********************************************************************************************************************

    This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
    permission from Infineon.

    Name        :   WO_MAT_QA_Actions

    Description :   Handles calculated columns and MFS calls for the current table.

    Notes       :   This function uses @ID, @RECORD, and @DICT to make sure {ColumnName} references work correctly.
                    If called from outside of a calculated column these will need to be set and restored.

    Parameters  :
        Action          [in] -- Name of the action to be taken
        CalcColName     [in] -- Name of the calculated column that needs to be processed. Normally this should only be
                                populated when the CalcField action is being used.
        FSList          [in] -- The list of MFSs and the BFS name for the current file or volume. This is an @SVM
                                delimited array, with the current MFS name as the first value in the array, and the BFS
                                name as the last value. Normally set by a calling MFS.
        Handle          [in] -- The file handle of the file or media map being accessed. Note, this does contain the
                                entire handle structure that the Basic+ Open statement would provide. Normally set by a
                                calling MFS.
        Name            [in] -- The name (key) of the record or file being accessed. Normally set by a calling MFS.
        FMC             [in] -- Various functions. Normally set by a calling MFS.
        Record          [in] -- The entire record (for record-oriented functions) or a newly-created handle (for
                                "get handle" functions). Normally set by a calling MFS.
        Status      [in/out] -- Indicator of the success or failure of an action. Normally set by the calling MFS but
                                for some actions can be set by the action handler to indicate failure.
        OrigRecord      [in] -- Original content of the record being processed by the current action. This is
                                automatically being assigned by the WRITE_RECORD and DELETE_RECORD actions within
                                BASE_MFS.
        Param1-10   [in/out] -- Additional request parameter holders
        ActionFlow     [out] -- Used to control the action chain (see the ACTION_SETUP insert for more information.)
                                Can also be used to return a special value, such as the results of the CalcField
                                method.

    History     :   (Date, Initials, Notes)
        10/22/18    djs     Original programmer.


***********************************************************************************************************************/

#pragma precomp SRP_PreCompiler

$insert LOGICAL
$insert FILE.SYSTEM.EQUATES
$insert ACTION_SETUP
$insert RDS_TEST_EQUATES
$insert RDS_EQUATES
$insert WO_LOG_EQUATES
$insert WM_OUT_EQUATES
$insert WM_IN_EQUATES
$insert WO_MAT_EQUATES
$insert WO_MAT_QA_EQUATES
$insert PRS_STAGE_EQUATES
$insert TOOL_CLASS_EQUATES

Declare function    Database_Services, obj_NCR, obj_SAP, Qa_Services, SRP_Trim, Environment_Services, Logging_Services
Declare subroutine  Error_Services, Database_Services, obj_NCR, obj_SAP, Post_Metrology_Manual_Data_Entry_Log
Declare subroutine  Logging_Services, obj_Notes

Equ CRLF$                   to \0D0A\

LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_MAT_QA'
LogDate     = Oconv(Date(), 'D4/')
LogTime     = Oconv(Time(), 'MTS')
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' WO_MAT_QA Log.csv'
Headers     = 'Logging DTM' : @FM : 'User' : @FM : 'Key ID' : @FM : 'Call Stack' : @FM : 'Record'
objLog      = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM

LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\FQABlocked'
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' FQA Log.csv'
Headers     = 'Logging DTM' : @FM : 'RDS Key ID' : @FM : 'Table' : @FM : 'Key' : @FM : 'Notes'
FQAobjLog   = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)

If KeyID then GoSub Initialize_System_Variables

Begin Case

    Case Action _EQC 'CalcField'            ;   GoSub Calc_Field
    Case Action _EQC 'READ_RECORD_PRE'      ;   GoSub READ_RECORD_PRE
    Case Action _EQC 'READ_RECORD'          ;   GoSub READ_RECORD
    Case Action _EQC 'READONLY_RECORD_PRE'  ;   GoSub READONLY_RECORD_PRE
    Case Action _EQC 'READONLY_RECORD'      ;   GoSub READONLY_RECORD
    Case Action _EQC 'WRITE_RECORD_PRE'     ;   GoSub WRITE_RECORD_PRE
    Case Action _EQC 'WRITE_RECORD'         ;   GoSub WRITE_RECORD
    Case Action _EQC 'DELETE_RECORD_PRE'    ;   GoSub DELETE_RECORD_PRE
    Case Action _EQC 'DELETE_RECORD'        ;   GoSub DELETE_RECORD
    Case Otherwise$                         ;   Status = 'Invalid Action'

End Case

If KeyID then GoSub Restore_System_Variables

If Assigned(ActionFlow) else ActionFlow = ACTION_CONTINUE$

Return ActionFlow


// ----- Calculated Columns --------------------------------------------------------------------------------------------
//
// The typical structure of a calculated column will look like this:
//
// Declare function TableName_Actions
//
// A = {COL1} ; * Reference as many data columns in this way to ensure the dictionary dependency is generated.
//
// @ANS = TableName_Actions('CalcField', 'CalcColName')
//
// ---------------------------------------------------------------------------------------------------------------------

Calc_Field:
    // Make sure the ActionFlow return variable is cleared in case nothing is calculated.
    ActionFlow = ''

return


// ----- MFS calls -----------------------------------------------------------------------------------------------------

READ_RECORD_PRE:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
    //    ActionFlow      = ACTION_STOP$
return

READ_RECORD:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
return

READONLY_RECORD_PRE:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
    //    ActionFlow      = ACTION_STOP$
return

READONLY_RECORD:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
return

WRITE_RECORD_PRE:

    WorkOrderNo  = Field(Name, '*', 1, 1)
    CassNo       = Field(Name, '*', 2, 1)
    WOMatKey     = WorkOrderNo : '*' : CassNo
    FinalQA      = False$
    WorkOrderRow = Database_Services('ReadDataRow', 'WO_LOG', WorkOrderNo)
    ReactType    = WorkOrderRow<WO_LOG_REACT_TYPE$>
    // Check to see if the Final QA signature is in place.
    If ReactType EQ 'EPP' then
        WMOutKey = WorkOrderNo : '*1*' : CassNo
        WMOutRow = Database_Services('ReadDataRow', 'WM_OUT', WMOutKey)
        If WMOutRow<WM_OUT_SUP_VER_SIG$> NE '' then
            FinalQA = True$
        end
    end else
        RDSNo  = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
        RDSRow = Database_Services('ReadDataRow', 'RDS', RDSNo)
        If RDSRow<RDS_SUP_VER_SIG$> NE '' then
            FinalQA = True$
        end
    end

    Begin Case
        
        Case Record EQ ''
            
            // Unscheduled record write attempt. Block the write and log the event.
            ErrorMessage   = 'Empty WO_MAT_QA profile. Unscheduled record write attempt!'
            Error_Services('Add', 'FS104: ':ErrorMessage)
            OrigFileError  = 104 : @FM : ErrorMessage
            Stack          = RetStack()
            Swap @FM with ' || ' in Stack
            RecordSnapshot = Record
            Swap @FM  with CRLF$    in RecordSnapshot
            Swap @VM  with ' @VM '  in RecordSnapshot
            Swap @SVM with ' @SVM ' in RecordSnapshot
            Swap @TM  with ' @TM '  in RecordSnapshot
            Swap @STM with ' @STM ' in RecordSnapshot
            LogData        = ''
            LogData<1>     = LoggingDTM
            LogData<2>     = @User4
            LogData<3>     = Name
            LogData<4>     = Stack
            LogData<5>     = CRLF$:RecordSnapshot:CRLF$    
            LogData<6>     = 'FS104: ':ErrorMessage

            // Sending a null message will send the LogData instead
            Logging_Services('AppendLog', objLog, LogData, @RM, @FM, '', '', '')
            
            // Send an internal OI message to OI admins
            Recipients   = Xlate('SEC_GROUPS', 'OI_ADMIN', 'USER', 'X')
            SentFrom     = 'SYSTEM'
            Subject      = 'Unscheduled WO_MAT_QA record write attempt.'
            Message      = ErrorMessage
            AttachWindow = ''
            AttachKey    = ''
            SendToGroup  = ''
            Parms        = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
            obj_Notes('Create',Parms)
            
            Status         = 0
            Record         = ''
            ActionFlow     = ACTION_STOP$
        
        Case FinalQA EQ True$ 
            
            // User is attempting to modify this row. Set the FS104 error and log the event.
            OrigFileError   = 104 : @FM : 'FQA has already been signed. WO_MAT_QA record cannot be updated!'
            LogData         = ''
            LogData<1>      = LoggingDTM
            LogData<2>      = @User4
            LogData<3>      = WOMatKey
            LogData<4>      = 'WO_MAT_QA'
            LogData<5>      = Name
            LogData<6>      = 'FS104:FQA has already been signed. WO_MAT_QA record cannot be updated!'
            Logging_Services('AppendLog', FQAobjLog, LogData, @RM, @FM)
            Status          = 0
            Record          = ''
            ActionFlow      = ACTION_STOP$
            Error_Services('Add', 'FS104:FQA has already been signed. WO_MAT_QA record cannot be updated!')
        
        Case Otherwise$
            // Validate metrology data
            
            // Current values in record
            OrigDataPoints   = OrigRecord<WO_MAT_QA_DATA_POINTS$>
            OrigProfiles     = OrigRecord<WO_MAT_QA_PROFILE$>
            OrigResult       = OrigRecord<WO_MAT_QA_RESULT$>
            OrigSigs         = OrigRecord<WO_MAT_QA_SIG$>
            OrigSpecMin      = OrigRecord<WO_MAT_QA_MIN$>
            OrigSpecMax      = OrigRecord<WO_MAT_QA_MAX$>
            
            // New values to be saved
            DataPoints       = Record<WO_MAT_QA_DATA_POINTS$>
            Profiles         = Record<WO_MAT_QA_PROFILE$>            ; // THICK_ONLY, CRES
            Sigs             = Record<WO_MAT_QA_SIG$>
            SigDTMS          = Record<WO_MAT_QA_SIG_DTM$>
            Result           = Record<WO_MAT_QA_RESULT$>             ; // Average of Data Points
            MinResult        = Record<WO_MAT_QA_MIN_RESULT$>         ; // Min of Data Points
            MaxResult        = Record<WO_MAT_QA_MAX_RESULT$>         ; // Max of Data Points
            RangePctResult   = Record<WO_MAT_QA_RANGE_PCT_RESULT$>   ; // Range % of Data Points
            EdgeMeanResult   = Record<WO_MAT_QA_EDGE_MEAN_RESULT$>   ; // Edge Mean Delta of Data Points
            SpecMin          = Record<WO_MAT_QA_MIN$>            
            SpecMax          = Record<WO_MAT_QA_MAX$>
            SpecPhaseMin     = Record<WO_MAT_QA_PHASE_MIN$>
            OutOfSpec        = Record<WO_MAT_QA_OUT_OF_SPEC$>
            FailReasons      = Record<WO_MAT_QA_FAIL_REASON$>
            ScanRecipes      = Record<WO_MAT_QA_RECIPE$>
            Stages           = Record<WO_MAT_QA_STAGE$>
            PSN              = {PS_NO}
            RawThicknessData = ''
            RawHgCVData      = ''
            RawPhaseData     = ''

            // Only enter validation code below if relevant data has changed.
            If ( (OrigDataPoints NE DataPoints) or (OrigProfiles NE Profiles) or (OrigResult NE Result) |
               or (OrigSigs NE Sigs) or (OrigSpecMin NE SpecMin) or (OrigSpecMax NE SpecMax) ) then

                Profile = ''
                For each Profile in Profiles using @VM setting ProfileIndex
                    Stage        = Stages<1, ProfileIndex>
                    PRSStageKey  = PSN:'*':Stage
                    PRSStageRec  = Database_Services('ReadDataRow', 'PRS_STAGE', PRSStageKey)
                    SpecRecipes  = PRSStageRec<PRS_STAGE_MET_RECIPE$>
                    SpecProfiles = PRSStageRec<PRS_STAGE_MET_TEST$>
                    Begin Case
                        Case ( (Profile EQ '1THICK_ONLY') and (Stage EQ 'UNLOAD') )
                            FailReason         = ''
                            ThickOutOfSpec     = False$
                            ToolClassID        = Record<WO_MAT_QA_TOOL_CLASS$, ProfileIndex>
                            ToolClassRec       = Database_Services('ReadDataRow', 'TOOL_CLASS', ToolClassID)
                            RecipePattern      = Record<WO_MAT_QA_RECIPE_PATTERN$, ProfileIndex>
                            RequiredDataPoints = ''
                            Locate RecipePattern in ToolClassRec<TOOL_CLASS_PATTERN$> setting rPos then
                                RequiredDataPoints = ToolClassRec<TOOL_CLASS_PATTERN_SIZE$, rPos>
                            end
                            ScanProfile      = Profile
                            ScanProfile[1,1] = ''
                            Locate ScanProfile in SpecProfiles using @VM setting vPos then
                                SpecRecipe = SpecRecipes<1, vPos>
                                ScanRecipe = ScanRecipes<1, ProfileIndex>
                                If ScanRecipe NE SpecRecipe then
                                    ThickOutOfSpec = True$
                                    FailReason     = 'Scanned THICK_ONLY UNLOAD recipe, ':ScanRecipe:', does not match spec ' |
                                                   : 'recipe, ':SpecRecipe:'.'
                                end
                            end
                            SaveList           = ''
                            ThickSig           = Sigs<1, ProfileIndex>
                            ThickSigDTM        = SigDTMS<1, ProfileIndex>
                            ThickSpecMin       = SpecMin<1, ProfileIndex>
                            ThickSpecMax       = SpecMax<1, ProfileIndex>
                            ThicknessData      = DataPoints<1, ProfileIndex>
                            ResAvg             = Result<1, ProfileIndex>
                            ThicknessDataCount = 0
                            For each DataPoint in ThicknessData using @SVM setting ThickIndex
                            Until DataPoint EQ ''
                                Begin Case
                                    Case ThickIndex GT 10
                                        Null
                                    Case (DataPoint EQ '')
                                        ThickOutOfSpec = True$
                                        FailReason     = 'Thickness data point ':ThickIndex:' is missing.'
                                    Case (DataPoint GT ThickSpecMax) OR (DataPoint LT ThickSpecMin)
                                        ThickOutOfSpec = True$
                                        FailReason     = 'Thickness data point ':ThickIndex:' is out of bounds.'
                                End Case
                                SaveList<1, 1, ThickIndex>       = DataPoint
                                RawThicknessData<1, ThickIndex>  = DataPoint
                                ThicknessDataCount              += 1
                            Next DataPoint
                            DataPoints<1, ProfileIndex> = SaveList
                            
                            If ThicknessDataCount LT RequiredDataPoints then
                                ThickOutOfSpec = True$
                                FailReason     = 'Number of data points is less than the required amount of ' : RequiredDataPoints : '!'
                            end
                            
                            If RawThicknessData NE '' then 
                                Response = QA_Services('CalculateThickData', RawThicknessData)
                                ThickAvg = Response<1,1>
                                ThickMin = Response<1,2>
                                ThickMax = Response<1,3>
                                Result<1, ProfileIndex>    = ThickAvg
                                MinResult<1, ProfileIndex> = ThickMin
                                MaxResult<1, ProfileIndex> = ThickMax
                            end else
                                // Lead/Supervisor/Engineer is overriding the typical workflow and is
                                // entering the Thickness average only.
                                ThickAvg = ResAvg
                                Begin Case
                                    Case (ThickAvg EQ '')
                                        ThickOutOfSpec = True$
                                        FailReason     = 'Thickness average is missing.'
                                    Case (ThickAvg GT ThickSpecMax) OR (ThickAvg LT ThickSpecMin)
                                        ThickOutOfSpec = True$
                                        FailReason     = 'Thickness average is out of bounds.'                                  
                                End Case
                            end
                            If ThickOutOfSpec EQ False$ then
                                // Final failure check. Only check if a previous failure reason has not been set as to not
                                // ovewrite the previous failure reason.
                                If (ThickSig EQ '') or (ThickSigDTM EQ '') then
                                    ThickOutOfSpec = True$
                                    FailReason     = 'THICK_ONLY UNLOAD product measurement test has not been signed.'
                                end
                            end
                            OutOfSpec<1, ProfileIndex>   = ThickOutOfSpec
                            FailReasons<1, ProfileIndex> = FailReason
                            
                        Case ( (Profile EQ '1CRES') and (Stage EQ 'UNLOAD') )
           
                            CresOutOfSpec   = False$
                            FailReason      = ''
                            ToolClassID        = Record<WO_MAT_QA_TOOL_CLASS$, ProfileIndex>
                            ToolClassRec       = Database_Services('ReadDataRow', 'TOOL_CLASS', ToolClassID)
                            RecipePattern      = Record<WO_MAT_QA_RECIPE_PATTERN$, ProfileIndex>
                            RequiredDataPoints = ''
                            Locate RecipePattern in ToolClassRec<TOOL_CLASS_PATTERN$> setting rPos then
                                RequiredDataPoints = ToolClassRec<TOOL_CLASS_PATTERN_SIZE$, rPos>
                            end
                            ScanProfile    = Profile
                            ScanProfile[1,1] = ''
                            Locate ScanProfile in SpecProfiles using @VM setting vPos then
                                SpecRecipe = SpecRecipes<1, vPos>
                                ScanRecipe = ScanRecipes<1, ProfileIndex>
                                If ScanRecipe NE SpecRecipe then
                                    CresOutOfSpec = True$
                                    FailReason    = 'Scanned CRES UNLOAD recipe, ':ScanRecipe:', does not match spec ' |
                                                  : 'recipe, ':SpecRecipe:'.'
                                end
                            end                   
                            SaveList        = ''
                            CriticalPoints  = '1,2,5,6,9'
                            CRESSig         = Sigs<1, ProfileIndex>
                            CRESSigDTM      = SigDTMS<1, ProfileIndex>
                            HgCVSpecMin     = SpecMin<1, ProfileIndex>
                            HgCVSpecMax     = SpecMax<1, ProfileIndex>
                            ThisPhaseMin    = SpecPhaseMin<1, ProfileIndex>
                            HgCVData        = DataPoints<1, ProfileIndex>
                            
                            // Adding Result Average here in the case that a lead manually only enters the averages. 
                            ResAvg          = Result<1, ProfileIndex>
                            If ThisPhaseMin EQ '' then
                                // Spec Phase Min was not added when the record initially created, so add it here.
                                PSN = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X')
                                If PSN NE '' then
                                    PRSStageKey = PSN:'*UNLOAD'
                                    PRSStageRec = Database_Services('ReadDataRow', 'PRS_STAGE', PRSStageKey)
                                    MetProps = PRSStageRec<PRS_STAGE_MET_PROP$>
                                    Locate 'CRES' in MetProps using @VM setting PropPos then
                                        SpecPhaseMinCol = PRSStageRec<PRS_STAGE_MET_PHASE_MIN$>
                                        ThisPhaseMin    = SpecPhaseMinCol<1, PropPos>
                                        SpecPhaseMin<1, ProfileIndex> = ThisPhaseMin
                                        Record<WO_MAT_QA_PHASE_MIN$>  = SpecPhaseMin
                                    end 
                                end                                                
                            end
                            ResDataCount = 0
                            For each DataPoint in HgCVData using @SVM setting CresIndex
                            Until DataPoint EQ ''
                                HgCVDataPoint  = DataPoint[1, 'F':@TM]
                                PhaseDataPoint = DataPoint[-1, 'B':@TM]
                                CriticalPoint  = Index(CriticalPoints, CresIndex, 1)
                                Begin Case
                                    Case (HgCVDataPoint EQ '') and CriticalPoint
                                        CresOutOfSpec = True$
                                        FailReason    = 'HgCV data point: ':CresIndex:' is missing data.'
                                    Case (PhaseDataPoint EQ '') and CriticalPoint
                                        CresOutOfSpec = True$
                                        FailReason    = 'Phase data point: ':CresIndex:' is missing data.'
                                    Case (HgCVDataPoint GT HgCVSpecMax) OR (HgCVDataPoint LT HgCVSpecMin) and CriticalPoint
                                        CresOutOfSpec = True$
                                        FailReason    = 'HgCV data point: ':CresIndex:' is out of bounds.'
                                    Case (PhaseDataPoint LT ThisPhaseMin) and CriticalPoint
                                        CresOutOfSpec = True$
                                        FailReason    = 'Phase data point: ':CresIndex:' is out of bounds.'
                                End Case
                                RawHgCVData<1, CresIndex>   = HgCVDataPoint
                                RawPhaseData<1, CresIndex>  = PhaseDataPoint
                                ResDataCount               += 1
                            Next DataPoint
                            If ResDataCount LT RequiredDataPoints then
                                CresOutOfSpec = True$
                                FailReason    = 'Number of data points is less than the required amount of ' : RequiredDataPoints : '!'
                            end
                            If (RawHgCVData NE '') AND (RawPhaseData NE '') then
                                
                                Response = QA_Services('CalculateHgCVData', RawHgCVData)
                                HgCVAvg      = Response<1,1>
                                HgCVMin      = Response<1,2>
                                HgCVMax      = Response<1,3>
                                HgCVEdgeMean = Response<1,4>
                                HgCVRangePct = Response<1,5>
                                Response = QA_Services('CalculateHgCVData', RawPhaseData)
                                PhaseAvg      = Response<1,1>
                                PhaseMin      = Response<1,2>
                                PhaseMax      = Response<1,3>
                                PhaseEdgeMean = Response<1,4>
                                PhaseRangePct = Response<1,5>
                                DataPoints<1, ProfileIndex>     = HgCVData
                                Result<1, ProfileIndex>         = HgCVAvg : @SVM : PhaseAvg
                                MinResult<1, ProfileIndex>      = HgCVMin : @SVM : PhaseMin
                                MaxResult<1, ProfileIndex>      = HgCVMax : @SVM : PhaseMax
                                EdgeMeanResult<1, ProfileIndex> = HgCVEdgeMean : @SVM : PhaseEdgeMean
                                RangePctResult<1, ProfileIndex> = HgCVRangePct : @SVM : PhaseRangePct
                                
                            end else
                                // Lead/Supervisor/Engineer is overriding the typical workflow and is
                                // entering the HgCV average and Phase average only.
                                HgCVAvg  = ResAvg<1, 1, 1>
                                PhaseAvg = ResAvg<1, 1, 2>
                                Begin Case
                                    Case (HgCVAvg EQ '')
                                        CresOutOfSpec   = True$
                                        FailReason      = 'HgCV average is missing.'                                      
                                    Case (PhaseAvg EQ '')
                                        CresOutOfSpec   = True$
                                        FailReason      = 'Phase average is missing.'
                                    Case (HgCVAvg GT HgCVSpecMax) OR (HgCVAvg LT HgCVSpecMin)
                                        CresOutOfSpec   = True$
                                        FailReason      = 'HgCV is out of bounds.'
                                    Case (PhaseAvg LT ThisPhaseMin)
                                        CresOutOfSpec   = True$
                                        FailReason      = 'Phase is out of bounds.'
                                End Case                          
                            end
                            
                            If CresOutOfSpec EQ False$ then
                                // Final failure check. Only check if a previous failure reason has not been set as to not
                                // overwrite the previous failure reason.
                                If (CRESSig EQ '') or (CRESSigDTM EQ '') then
                                    CresOutofSpec = True$
                                    FailReason = 'CRES UNLOAD product measurement test has not been signed.'
                                end
                            end

                            OutOfSpec<1, ProfileIndex>   = CresOutOfSpec
                            FailReasons<1, ProfileIndex> = FailReason
                            
                            OrigHgCVAvg = OrigRecord<WO_MAT_QA_RESULT$, ProfileIndex, 1>
                            If HgCVAvg NE OrigHgCVAvg then
                                Post_Metrology_Manual_Data_Entry_Log(@USER4, 'HgCRes', RDSNo : ' / ' : WorkOrderNo : '*' : CassNo)
                            end

						Case ( (Profile EQ '1LW_RHO') and (Stage EQ 'UNLOAD') )
							
							ThisOutOfSpec = False$
							FailReason    = ''
							
                            ThisSig       = Sigs<1, ProfileIndex>
                            ThisSigDTM    = SigDTMS<1, ProfileIndex>
                            ThisSpecMin   = SpecMin<1, ProfileIndex>
                            ThisSpecMax   = SpecMax<1, ProfileIndex>
                            ThisResult    = Result<1, ProfileIndex>
                            ThisSpecTest  = SpecProfiles<1, ProfileIndex>
                            
                            If ( (ThisResult LT ThisSpecMin) or (ThisResult GT ThisSpecMax) ) then
                            	ThisOutOfSpec = True$
                            	FailReason      = 'RHO is out of bounds.'
                            end
                            
                            If ThisOutOfSpec EQ False$ then
                            	If (ThisSig EQ '') or (ThisSigDTM EQ '') then
                            		ThisOutOfSpec = True$
                            		FailReason    = Stage:' ':ThisSpecTest:' product measurement test has not been signed.'
                            	end
                            end
                            
                            OutOfSpec<1, ProfileIndex>   = ThisOutOfSpec
                            FailReasons<1, ProfileIndex> = FailReason
                            
                        Case Otherwise$
                            // This profile & stage do not currently have any specific validation code associated with them.
                            // Ensure 'out of spec' flag and 'fail reason' field for this profile are clear.
                            OutOfSpec<1, ProfileIndex>   = False$
                            FailReasons<1, ProfileIndex> = ''
                            
                    End Case

                Next Profile
                Record<WO_MAT_QA_DATA_POINTS$>       = DataPoints
                Record<WO_MAT_QA_RESULT$>            = Result
                Record<WO_MAT_QA_MIN_RESULT$>        = MinResult
                Record<WO_MAT_QA_MAX_RESULT$>        = MaxResult
                Record<WO_MAT_QA_EDGE_MEAN_RESULT$>  = EdgeMeanResult
                Record<WO_MAT_QA_RANGE_PCT_RESULT$>  = RangePctResult
                Record<WO_MAT_QA_OUT_OF_SPEC$>       = OutOfSpec
                Record<WO_MAT_QA_FAIL_REASON$>       = FailReasons
                SaveRecord                           = Record
            end
            
    End Case
return

WRITE_RECORD:   
return

DELETE_RECORD_PRE:    
return

DELETE_RECORD:    
return


// ----- Internal Methods ----------------------------------------------------------------------------------------------

Initialize_System_Variables:
    // Save these for restoration later
    SaveDict        = @DICT
    SaveID          = @ID
    SaveRecord      = @RECORD
    OrigFileError   = @FILE.ERROR

    // Now make sure @DICT, ID, and @RECORD are populated
    CurrentDictName = ''
    If @DICT then
        DictHandle = @DICT<1, 2>
        Locate DictHandle in @TABLES(5) Using @FM Setting fPos then
            CurrentDictName = Field(@TABLES(0), @FM, fPos, 1)
        end
    end

    If CurrentDictName NE DictName then
         Open DictName to @DICT else Status = 'Unable to initialize @DICT'
    end    

    @ID = KeyID
    If Record else
        // Record might not have been passed in. Read the record from the database table just to make sure.
        @FILE.ERROR     = ''
        Open TableName to hTable then
            FullFSList  = hTable[1, 'F' : @VM]
            BFS         = FullFSList[-1, 'B' : @SVM]
            LastHandle  = hTable[-1, 'B' : \0D\]
            FileHandle  = \0D\ : LastHandle[1, @VM]

            Call @BFS(READO.RECORD, BFS, FileHandle, KeyID, FMC, Record, ReadOStatus)
        end
    end
    @RECORD = Record
return

Restore_System_Variables:
    Transfer SaveDict   to @DICT
    Transfer SaveID     to @ID
    Transfer SaveRecord to @RECORD
    @FILE.ERROR = OrigFileError
return

