Function RDS_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        :   RDS_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)
        04/10/18    dmb     Original programmer.
        10/04/18    djs     Added a trigger within the WRITE_RECORD event, which fires when the reactor number has
                            changed. When this occurs the related RDS_LAYER records for this RDS record are populated
                            with the most recent associated TOOL_PARMS record. (related by PSN and Reactor)

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

#pragma precomp SRP_PreCompiler

$Insert FILE.SYSTEM.EQUATES
$Insert ACTION_SETUP
$Insert RDS_EQUATES
$Insert CLEAN_INSP_EQUATES
$Insert COMPANY_EQUATES
$Insert RDS_LAYER_EQUATES
$Insert TOOL_PARMS_EQUATES
$Insert RLIST_EQUATES
$Insert APP_INSERTS
$Insert QA_MET_EQUATES
$Insert PRS_STAGE_EQUATES
$Insert WO_MAT_EQUATES
$Insert WO_MAT_QA_EQUATES
$Insert LOT_EVENT_EQUATES

Equ COMMA$ to ','

Declare function    Error_Services, Database_Services, obj_RDS_Test, Logging_Services, Environment_Services
Declare function    Tool_Parms_Services, Signature_Services, obj_WO_Mat_QA, Datetime, Override_Services
Declare function	Rds_Services, SRP_DateTime, SRP_Math, obj_WO_Mat, Lot_Services, SRP_Array
Declare function    Lot_Event_Services
Declare subroutine  Error_Services, Database_Services, Logging_Services, Service_Services

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

LogPath      = Environment_Services('GetApplicationRootPath') : '\LogFiles\RDS'
LogFileName  = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' RDS Erase attempt.csv'
Headers      = 'Logging DTM' : @FM : 'RDSNo' : @FM : 'WONo' : @FM : 'CassNo' : @FM : 'Reactor Type'
objLog3      = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)

If KeyID then GoSub Initialize_System_Variables

Begin Case

    Case Action _EQC 'CalculateColumn'      ;   GoSub CalculateColumn
    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 Database_Services
//
// @ANS = Database_Services('CalculateColumn')
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CalculateColumn:

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

    Begin Case
        Case CalcColName EQ 'CURR_STAGE'            ; GoSub CURR_STAGE
        Case CalcColName EQ 'DATETIME_IN'           ; GoSub DATETIME_IN
        CAse CalcColName EQ 'DATETIME_OUT'          ; GoSub DATETIME_OUT
        Case CalcColName EQ 'PRE_INST'              ; GoSub PRE_INST
        Case CalcColName EQ 'PRE_INST_ACK_REQ'      ; GoSub PRE_INST_ACK_REQ
        Case CalcColName EQ 'FWI_INST'              ; GoSub FWI_INST
        Case CalcColName EQ 'FWI_INST_ACK_REQ'      ; GoSub FWI_INST_ACK_REQ
        Case CalcColName EQ 'LWI_INST'              ; GoSub LWI_INST
        Case CalcColName EQ 'LWI_INST_ACK_REQ'      ; GoSub LWI_INST_ACK_REQ
        Case CalcColName EQ 'QA_INST'               ; GoSub QA_INST
        Case CalcColName EQ 'QA_INST_ACK_REQ'       ; GoSub QA_INST_ACK_REQ
        Case CalcColName EQ 'LOAD_INST'             ; GoSub LOAD_INST
        Case CalcColName EQ 'LOAD_INST_ACK_REQ'     ; GoSub LOAD_INST_ACK_REQ
        Case CalcColName EQ 'UNLOAD_INST'           ; GoSub UNLOAD_INST
        Case CalcColName EQ 'UNLOAD_INST_ACK_REQ'   ; GoSub UNLOAD_INST_ACK_REQ
        Case CalcColName EQ 'ON_TW_FREQ'            ; GoSub ON_TW_FREQ
        Case CalcColName EQ 'POST_INST'             ; GoSub POST_INST
        Case CalcColName EQ 'POST_INST_ACK_REQ'     ; GoSub POST_INST_ACK_REQ
        Case CalcColName EQ 'ROTR_ACTION'           ; GoSub ROTR_ACTION
        Case CalcColName EQ 'RDS_LAYER_ACK_REQ'     ; GoSub RDS_LAYER_ACK_REQ
        Case CalcColName EQ 'RUNTIME'               ; GoSub RUNTIME
        Case CalcColName EQ 'SUPPL_ACK_REQ'         ; GoSub SUPPL_ACK_REQ
        Case CalcColName EQ 'WAFER_COUNT_ACK_REQ'   ; GoSub WAFER_COUNT_ACK_REQ
        Case CalcColName EQ 'TW_PROD'               ; GoSub TW_PROD
        Case CalcColName EQ 'ABBREV_OR_CO_NAME'     ; GoSub ABBREV_OR_CO_NAME
        Case CalcColName EQ 'LOAD_LOCK_REQ'         ; GoSub LOAD_LOCK_REQ
        Case CalcColName EQ 'UNLOAD_THICK_TEST'     ; GoSub UNLOAD_THICK_TEST
        Case CalcColName EQ 'WO_MAT_KEY'            ; GoSub WO_MAT_KEY
        Case CalcColName EQ 'HOT_LOT'               ; GoSub HOT_LOT
    End Case

return


DATETIME_IN:
    
    TimeIn     = {TIME_IN}
    TimeIn     = TimeIn/86400
    TimeIn     = SRP_Math('ROUND', TimeIn, 5)
    TimeIn     = TimeIn[2, 6]
    ActionFlow = {DATE_IN}:TimeIn
    
return


DATETIME_OUT:
    
    TimeOut    = {TIME_OUT}
    TimeOut    = TimeOut/86400
    TimeOut    = SRP_Math('ROUND', TimeOut, 5)
    TimeOut    = TimeOut[2, 6]
    ActionFlow = {DATE_OUT}:TimeOut
    
return


LOAD_LOCK_REQ:
    
    ActionFlow  = False$
    ReactorType = {REACTOR_TYPE}
    LoadLock    = {LOAD_LOCK_SIDE}

    If ( ( (ReactorType EQ 'ASM+') or (ReactorType EQ 'HTR') ) and (LoadLock EQ '') ) then ActionFlow = True$
    
return


CURR_STAGE:
    
    Stage = ''
    RDSNo = @ID
    Stage = Rds_Services('GetCurrentStage', RDSNo)          
    ActionFlow = Stage
    
return


PRE_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*PRE', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


PRE_INST_ACK_REQ:
    
    If ( ({PRE_INST} NE '') and ( ({PRE_INST_ACK} EQ '') or ({PRE_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'VER') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


FWI_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*FWI', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


FWI_INST_ACK_REQ:
    
    If ( ({FWI_INST} NE '') and ( ({FWI_INST_ACK} EQ '') or ({FWI_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'LOAD') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


LWI_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*LWI', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


LWI_INST_ACK_REQ:
    
    If ( ({LWI_INST} NE '') and ( ({LWI_INST_ACK} EQ '') or ({LWI_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'UNLOAD') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


POST_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*POST', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


POST_INST_ACK_REQ:
    
    If ( ({POST_INST} NE '') and ( ({POST_INST_ACK} EQ '') or ({POST_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'POST') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


QA_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*QA', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


QA_INST_ACK_REQ:
    
    If ( ({QA_INST} NE '') and ( ({QA_INST_ACK} EQ '') or ({QA_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'QA') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


LOAD_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*LOAD', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


LOAD_INST_ACK_REQ:
    
    If ( ({LOAD_INST} NE '') and ( ({LOAD_INST_ACK} EQ '') or ({LOAD_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'LOAD') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


UNLOAD_INST:
    
    PSN = {PROD_SPEC_ID}
    StageInst = Xlate('PRS_STAGE', PSN:'*UNLOAD', 'INST', 'X')
    Convert @TM to ' ' in StageInst
    ActionFlow = StageInst
    
return


UNLOAD_INST_ACK_REQ:
    
    If ( ({UNLOAD_INST} NE '') and ( ({UNLOAD_INST_ACK} EQ '') or ({UNLOAD_INST_ACK} EQ False$) ) and ({CURR_STAGE} EQ 'UNLOAD') ) then
        ActionFlow = True$
    end else
        ActionFlow = False$
    end
    
return


UNLOAD_THICK_TEST:
    
    UnloadThickTest  = ''
    Parms            = {WO_MAT_KEY}:@RM
    Parms           := '':@RM
    Parms           := FIELD(@RECORD<256>,'*',2):@RM              ;* WOStep
    Parms           := 'UNLOAD':@RM
    Parms           := WO_MAT_QA_PROFILE$
    QAMetArray       = obj_WO_Mat_QA('GetQAMet', Parms)
    For each Row in QAMetArray using @FM setting fPos
        Locate 'THICK_ONLY' in Row using @VM setting vPos then
            UnloadThickTest = True$
        end
    Until UnloadThickTest EQ True$
    Next Row
    ActionFlow = UnloadThickTest
    
return


SUPPL_ACK_REQ:

    ActionFlow = False$
    SupplementFlag = {SUPPLEMENT}
    SupplAck       = {SUPPL_ACK}
    If ( (SupplementFlag EQ True$) and ( (SupplAck EQ '') or (SupplAck EQ False$) ) ) then
        ActionFlow = True$
    end
    
return


RDS_LAYER_ACK_REQ:
    
    ActionFlow  = False$
    IsTestWafer = {ON_TW_FREQ}
    RDSLayerAck = {RDS_LAYER_ACK}
    If ( (IsTestWafer EQ True$) and ( (RDSLayerAck EQ False$) or (RDSLayerAck EQ '') ) ) then
        ActionFlow = True$
    end

return


WAFER_COUNT_ACK_REQ:
    
    ActionFlow = False$
    If ( ({CURR_STAGE} EQ 'VER') and ( ({WAFER_COUNT_ACK} EQ False$) or ({WAFER_COUNT_ACK} EQ '') ) ) then
        ActionFlow = True$
    end
    
return


ON_TW_FREQ:
    
    ActionFlow  = False$
    CassNo      = {CASS_NO}
    PSNo        = {PROD_SPEC_ID}
    UnloadKey   = PSNo:'*UNLOAD'
    UnloadRec   = Database_Services('ReadDataRow', 'PRS_STAGE', UnloadKey)
    Intervals   = UnloadRec<PRS_STAGE_MET_INTERVAL$>
    Starts      = UnloadRec<PRS_STAGE_MET_START$>
    MetTests    = UnloadRec<PRS_STAGE_MET_TEST$>
    TestSlots   = UnloadRec<PRS_STAGE_MET_SLOT$>
    Found       = False$
    For each MetTest in MetTests using @VM setting vPos
        TestSlot = TestSlots<0, vPos>
        If (MetTest EQ 'THICK_ONLY') and (TestSlot EQ 'L') then Found = True$
    Until Found EQ True$
    Next MetTest
    If Found EQ True$ then
        TestWaferFlag = 0
        Interval      = Intervals<0, vPos>
        Start         = Starts<0, vPos>		
        IF Interval NE '' AND Start NE '' THEN
            IF Interval = Start THEN
                IF REM(CassNo,Interval) = 0 THEN TestWaferFlag = 1
            END ELSE
                IF ABS((Start + INT(CassNo/Interval)*Interval) - CassNo) = 0 THEN TestWaferFlag = 1
            END
        end
        ActionFlow = TestWaferFlag
    end    
return


ROTR_ACTION:

    CleanInspRow    = Database_Services('ReadDataRow', 'CLEAN_INSP', {LWI_CI_NO})
    ROTRAction      = CleanInspRow<CLEAN_INSP_ROTR_ACTION$>
    If ROTRAction EQ 'F' then
        // Before returning a Fail, check to see if there is an acceptance for the results.
        AcceptedSignature   = CleanInspRow<CLEAN_INSP_SIGN_ROTR_SIGNATURE$>
        AcceptedReason      = CleanInspRow<CLEAN_INSP_SIGN_ROTR_REASON$>
        If (AcceptedSignature NE '') AND (AcceptedReason NE '') then ROTRAction = 'A'
    end
    ActionFlow  = ROTRAction

return


TW_PROD:

    IF {REACTOR_TYPE} = 'EPP' THEN
        WaferChars = @RECORD<275>
        PktChars = @RECORD<268>
        OutCnt = COUNT(PktChars,@VM) + (PktChars NE '')
        Ans = 0
        FOR I = 1 TO OutCnt
             PktChar = PktChars<1,I>
             WfrChar = WaferChars<1,I>
             IF PktChar = 'TEST' AND WfrChar = 'PROD' THEN  Ans += 1
        NEXT I
        ActionFlow = Ans
    END ELSE
         TestKeys   = XLATE('RDS_LAYER',@RECORD<150>,3,'X')
         ActionFlow = obj_RDS_Test('ProdTestCount',TestKeys)
    END
    
return


ABBREV_OR_CO_NAME:

    CompRec         = Database_Services('ReadDataRow', 'COMPANY', {CUST_NO})
    CustomerName    = CompRec<COMPANY_ABBREV$>
    ActionFlow      = CustomerName
    
return


WO_MAT_KEY:
    
    ActionFlow = ''
    ReactType = {REACTOR_TYPE}
    IF {REACTOR_TYPE} NE 'EPP' THEN
       WONo = @RECORD<6>
       CassNo = @RECORD<260>
       IF WONo NE '' AND CassNo NE '' THEN
            ActionFlow = WONo:'*':CassNo
       END
    END
    
    If ActionFlow EQ '' and ReactType NE 'EPP' and ReactType NE 'GAN' then
        RDSNo = @ID
        // Log the above data to troubleshoot.
        LogData = ''
        LogData<1> = LoggingDTM
        LogData<2> = RDSNo
        LogData<3> = @Record<RDS_WO$>
        LogData<4> = @Record<RDS_CASS_NO$>
        LogData<5> = ReactType
        Logging_Services('AppendLog', objLog3, LogData, @RM, @FM)
    end
    
return


RUNTIME:

    LoadDTM      = IConv(OConv({DATE_IN}, 'D'):' ':OConv({TIME_IN}, 'MT'), 'DT')
    LoadEx2DTM   = IConv(OConv({OP_IN_EX2_DATE}, 'D'):' ':OConv({OP_IN_EX2_TIME}, 'MT'), 'DT')
    LoadEx3DTM   = IConv(OConv({OP_IN_EX3_DATE}, 'D'):' ':OConv({OP_IN_EX3_TIME}, 'MT'), 'DT')
    UnloadEx1DTM = IConv(OConv({OP_OUT_EX1_DATE}, 'D'):' ':OConv({OP_OUT_EX1_TIME}, 'MT'), 'DT')
    UnloadEx2DTM = IConv(OConv({OP_OUT_EX2_DATE}, 'D'):' ':OConv({OP_OUT_EX2_TIME}, 'MT'), 'DT')
    UnloadDTM    = IConv(OConv({DATE_OUT}, 'D'):' ':OConv({TIME_OUT}, 'MT'), 'DT')
    Begin Case
        Case UnloadEx2DTM NE ''
            Runtime = UnloadEx2DTM - LoadEx3DTM
        Case UnloadEx1DTM NE ''
            Runtime = UnloadEx1DTM - LoadEx2DTM
        Case Otherwise$
            Runtime = UnloadDTM - LoadDTM
    End Case
    
    ActionFlow = Runtime
    
return


HOT_LOT:
    
    ActionFlow = Xlate('WO_LOG', {WO}, 'HOT_FLAG', 'X')
    
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$
    
    * This code prevents an anomaly where an @SVM appears when the value is read
    * and causes an error when copying the record to SQL.
    WafersIn               = Record<RDS_WAFERS_IN$>
    WafersIn               = WafersIn<1, 1, 1>
    Record<RDS_WAFERS_IN$> = WafersIn
    
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          = ''
    
    Shift = Record<RDS_SHIFT$>
    If Num(Shift) then
    	Begin Case
    		Case Shift EQ 1
    			Record<RDS_SHIFT$> = 'A'
    		Case Shift EQ 2
    			Record<RDS_SHIFT$> = 'B'
    		Case Shift EQ 3
    			Record<RDS_SHIFT$> = 'C'
    		Case Shift EQ 4
    			Record<RDS_SHIFT$> = 'D'
    		Case Otherwise$
    			Null
    	End Case
    end     
    
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          = ''
    
	Shift = Record<RDS_SHIFT$>
    If Num(Shift) then
    	Begin Case
    		Case Shift EQ 1
    			Record<RDS_SHIFT$> = 'A'
    		Case Shift EQ 2
    			Record<RDS_SHIFT$> = 'B'
    		Case Shift EQ 3
    			Record<RDS_SHIFT$> = 'C'
    		Case Shift EQ 4
    			Record<RDS_SHIFT$> = 'D'
    		Case Otherwise$
    			Null
    	End Case
    end
    
return

WRITE_RECORD_PRE:

	Shift = Record<RDS_SHIFT$>
    If Num(Shift) then
    	Begin Case
    		Case Shift EQ 1
    			Record<RDS_SHIFT$> = 'A'
    		Case Shift EQ 2
    			Record<RDS_SHIFT$> = 'B'
    		Case Shift EQ 3
    			Record<RDS_SHIFT$> = 'C'
    		Case Shift EQ 4
    			Record<RDS_SHIFT$> = 'D'
    		Case Otherwise$
    			Null
    	End Case
    end
    SaveRecord = Record

    RDSNo    = Name
    RDSKeyID = Name
    
    OrigReactor = OrigRecord<RDS_REACTOR$>
    NewReactor  = Record<RDS_REACTOR$>
    
    // Verify / Pre-Epi Signature
    OrigVerSig      = Trim(OrigRecord<RDS_PRE_EPI_SIG$>)
    OrigVerDate     = Trim(OrigRecord<RDS_PRE_EPI_SIG_DATE$>)
    OrigVerTime     = Trim(OrigRecord<RDS_PRE_EPI_SIG_TIME$>)
    NewVerSig       = Trim(Record<RDS_PRE_EPI_SIG$>)
    NewVerDate      = Trim(Record<RDS_PRE_EPI_SIG_DATE$>)
    NewVerTime      = Trim(Record<RDS_PRE_EPI_SIG_TIME$>)
    If Not(Num(NewVerDate)) then Record<RDS_PRE_EPI_SIG_DATE$> = IConv(NewVerDate, 'D')
    If Not(Num(NewVerTime)) then Record<RDS_PRE_EPI_SIG_TIME$> = IConv(NewVerTime, 'MT')
    // Load Signature
    OrigLoadSig     = Trim(OrigRecord<RDS_OPERATOR_IN$>)
    OrigLoadDate    = Trim(OrigRecord<RDS_DATE_IN$>)
    OrigLoadTime    = Trim(OrigRecord<RDS_TIME_IN$>)
    NewLoadSig      = Trim(Record<RDS_OPERATOR_IN$>)
    NewLoadDate     = Trim(Record<RDS_DATE_IN$>)
    NewLoadTime     = Trim(Record<RDS_TIME_IN$>)
    If Not(Num(NewLoadDate)) then Record<RDS_DATE_IN$> = IConv(NewLoadDate, 'D')
    If Not(Num(NewLoadTime)) then Record<RDS_TIME_IN$> = IConv(NewLoadTime, 'MT')
    // Unload signature
    OrigDateOut    = OrigRecord<RDS_DATE_OUT$>
    OrigTimeOut    = OrigRecord<RDS_TIME_OUT$>
    OrigTimeOutSig = OrigRecord<RDS_OPERATOR_OUT$>
    DateOut        = Record<RDS_DATE_OUT$>
    TimeOut        = Record<RDS_TIME_OUT$>
    TimeOutSig     = Record<RDS_OPERATOR_OUT$>
    If Not(Num(DateOut)) then Record<RDS_DATE_OUT$> = IConv(DateOut, 'D')
    If Not(Num(TimeOut)) then Record<RDS_TIME_OUT$> = IConv(TimeOut, 'MT')
    // FQA signature
    OrigFQADate    = OrigRecord<RDS_SUP_VER_SIG_DATE$>
    OrigFQATime    = OrigRecord<RDS_SUP_VER_SIG_TIME$>
    OrigFQASig     = OrigRecord<RDS_SUP_VER_SIG$>
    FQADate        = Record<RDS_SUP_VER_SIG_DATE$>
    FQATime        = Record<RDS_SUP_VER_SIG_TIME$>
    FQASig         = Record<RDS_SUP_VER_SIG$>
    If Not(Num(FQADate)) then Record<RDS_SUP_VER_SIG_DATE$> = IConv(FQADate, 'D')
    If Not(Num(FQATime)) then Record<RDS_SUP_VER_SIG_TIME$> = IConv(FQATime, 'MT')
    
    CurrDTM      = Datetime()
    VerDTM       = IConv(OConv(NewVerDate, 'D'):' ':OConv(NewVerTime, 'MT'), 'DT')
    LoadDTM      = IConv(OConv(NewLoadDate, 'D'):' ':OConv(NewLoadTime, 'MT'), 'DT')
    LoadEx2DTM   = IConv(OConv({OP_IN_EX2_DATE}, 'D'):' ':OConv({OP_IN_EX2_TIME}, 'MT'), 'DT')
    LoadEx3DTM   = IConv(OConv({OP_IN_EX3_DATE}, 'D'):' ':OConv({OP_IN_EX3_TIME}, 'MT'), 'DT')
    UnloadEx1DTM = IConv(OConv({OP_OUT_EX1_DATE}, 'D'):' ':OConv({OP_OUT_EX1_TIME}, 'MT'), 'DT')
    UnloadEx2DTM = IConv(OConv({OP_OUT_EX2_DATE}, 'D'):' ':OConv({OP_OUT_EX2_TIME}, 'MT'), 'DT')
    UnloadDTM    = IConv(OConv(DateOut, 'D'):' ':OConv(TimeOut, 'MT'), 'DT')
    PostDTM      = IConv(OConv({POST_EPI_SIG_DATE}, 'D'):' ':OConv({POST_EPI_SIG_TIME}, 'MT'), 'DT')
    FQADTM       = IConv(OConv({SUP_VER_SIG_DATE}, 'D'):' ':OConv({SUP_VER_SIG_TIME}, 'MT'), 'DT')

	If NewLoadSig EQ '' then
		// Audit the LOT_EVENT table for missing LOAD event (i.e., signature)
		ExistingNGLotId = Lot_Services('GetLotIdByLegacyLotIdAndType', RDSNo, 'RDS')
		If RowExists('LOT', ExistingNGLotId) then
			LotEvents      = Lot_Event_Services('GetLotEventsInSequence', ExistingNGLotId)
			If LotEvents NE '' then 
				LotEventsRev      = SRP_Array('Reverse', LotEvents, @FM)
				LotEventsTypesRev = Xlate('LOT_EVENT', LotEventsRev, LOT_EVENT_LOT_EVENT_TYPE$, 'X')
				If LotEventsTypesRev NE '' then
					Done = False$
					For each LotEventType in LotEventsTypesRev using @FM setting EventPos
						Begin Case
							Case LotEventType EQ 'LOAD'
								Done = True$
								If NewLoadSig EQ '' then
									// Load signature is missing from RDS record
									NewLoadSig               = Xlate('LOT_EVENT', LotEventsRev<EventPos>, LOT_EVENT_EVENT_OPERATOR_ID$, 'X')
									NewLoadSigDtm            = Xlate('LOT_EVENT', LotEventsRev<EventPos>, LOT_EVENT_EVENT_DATETIME$, 'X')
									NewLoadDate              = SRP_DateTime("Date", NewLoadSigDtm)
									NewLoadTime              = SRP_DateTime("Time", NewLoadSigDtm)
									Record<RDS_OPERATOR_IN$> = NewLoadSig
									Record<RDS_DATE_IN$>     = NewLoadDate
									Record<RDS_TIME_IN$>     = NewLoadTime
									LogData                  = ''
									LogData<1>               = LoggingDTM
									LogData<2>               = @User4
									LogData<3>               = RDSKeyID
									LogData<4>               = 'Detected missing LOAD signature from RDS record. Restored signature from LOT_EVENT table.'
									Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)							
								end
							Case LotEventType EQ 'UNSIGN_LOAD'
								Done = True$
								// No need to check for missing load signature
						End Case
					Until Done
					Next LotEventType
				end
			end
		end
	end

    Begin Case
        Case LoadDTM NE '' and UnloadDTM EQ '' and UnloadEx1DTM EQ ''
            // Cassette still running...
            Runtime = 0
        Case LoadEx2DTM NE '' and UnloadEx2DTM EQ ''
            // Cassette still running...
            Runtime = 0
        Case LoadEx3DTM NE '' and UnloadDTM EQ ''
            // Cassette still running...
            Runtime = 0
        Case UnloadDTM NE '' and LoadEx3DTM EQ ''
            // No extra load/unload (typical path)
            Runtime = UnloadDTM - LoadDTM
        Case UnloadDTM NE '' and LoadEx3DTM NE ''
            // Three extra load/unloads
            RunTime = UnloadDTM - LoadEx3DTM
        Case UnloadEx2DTM NE ''
            // Two extra load/unloads
            Runtime = UnloadEx2DTM - LoadEx2DTM
        Case UnloadEx1DTM NE ''
            // One extra load/unload
            Runtime = UnloadEx1DTM - LoadDTM
        Case Otherwise$
            Runtime = 0
    End Case
    
    // Look for UNLOAD_DTM override
    Override = Override_Services('GetOverride', 'RDS', RDSNo, 'UNLOAD_DTM')

    Begin Case
    	
    	Case  ( (OrigLoadSig NE '') and (OrigReactor NE NewReactor) )
            LogData    = ''
            LogData<1> = LoggingDTM
            LogData<2> = @USER4
            LogData<3> = RDSKeyID
            LogData<4> = 'Reactor number cannot be changed or removed once the load signature is set!'
            Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$)
            
            // User is attempting to erase data that is prohibited from being erased Set the FS104 error and block the write.
            Error_Services('Add', 'Reactor number cannot be changed or removed once the load signature is set!')
            OrigFileError = 104:': Reactor number cannot be changed or removed once the load signature is set!'
            Status        = 0
            Record        = ''
            ActionFlow    = ACTION_STOP$            
        
        Case ( (OrigVerSig NE '')  and (NewVerSig EQ '') ) or ( (OrigVerDate NE '') and (NewVerDate EQ '') ) |
            or ( (OrigVerTime NE '') and (NewVerTime EQ '') ) or ( (NewVerDate EQ '')  and (NewVerTime NE '') ) |
            or ( (NewVerDate NE '')  and (NewVerTime EQ '') ) 
            LogData    = ''
            LogData<1> = LoggingDTM
            LogData<2> = @USER4
            LogData<3> = RDSKeyID
            LogData<4> = 'Signature data cannot be removed!'
            Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$)       
            
            // User is attempting to erase data that is prohibited from being erased Set the FS104 error and block the write.
            Error_Services('Add', 'Signature data cannot be removed!')
            OrigFileError = 104:': Signature data cannot be removed!'
            Status        = 0
            Record        = ''
            ActionFlow    = ACTION_STOP$
            
        Case ( (VerDTM GT CurrDTM) or (LoadDTM GT CurrDTM) or (LoadEx2DTM GT CurrDTM) or (LoadEx3DTM GT CurrDTM) |
            or (UnloadEx1DTM GT CurrDTM) or (UnloadEx2DTM GT CurrDTM) or (UnloadDTM GT CurrDTM) |
            or (PostDTM GT CurrDTM) or (FQADTM GT CurrDTM) )
            RDSKeyID   = Name
            LogData    = ''
            LogData<1> = LoggingDTM
            LogData<2> = @USER4
            LogData<3> = RDSKeyID
            LogData<4> = 'Signature datetime is greater than current datetime!'
            Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$)       
            
            Error_Services('Add', 'Signature datetime is greater than current datetime!')
            OrigFileError = 104:': Signature datetime is greater than current datetime!'
            Status        = 0
            Record        = ''
            ActionFlow    = ACTION_STOP$
            
        Case ( ((VerDTM NE '' and LoadDTM NE '') and (LoadDTM LE VerDTM)) |
            or (LoadDTM NE '' and UnloadDTM NE '') and (UnloadDTM LT LoadDTM) |
            or (UnloadEx1DTM NE '' and LoadDTM NE '') and (UnloadEx1DTM LT LoadDTM) |
            or (LoadEx2DTM NE '' and UnloadEx1DTM NE '') and (LoadEx2DTM LT UnloadEx1DTM) |
            or (UnloadEx2DTM NE '' and LoadEx3DTM NE '') and (LoadEx3DTM LT UnloadEx2DTM) |
            or (UnloadDTM NE '' and LoadEx3DTM NE '') and (UnloadDTM LT LoadEx3DTM) )
            // QA > POST > Unload > LoadEx3 > UnloadEx2 > LoadEx2 > UnloadEx1 > Load
            RDSKeyID   = Name
            LogData    = ''
            LogData<1> = LoggingDTM
            LogData<2> = @USER4
            LogData<3> = RDSKeyID
            LogData<4> = 'Signature datetime is less than a previous signature datetime!'
            Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$)       
            
            Error_Services('Add', 'Signature datetime is less than a previous signature datetime!')
            OrigFileError = 104:': Signature datetime is less than a previous signature datetime!'
            Status        = 0
            Record        = ''
            ActionFlow    = ACTION_STOP$
            
        Case ( (RunTime GT 3) and (Override EQ False$) )
            RDSKeyID   = Name
            LogData    = ''
            LogData<1> = LoggingDTM
            LogData<2> = @USER4
            LogData<3> = RDSKeyID
            LogData<4> = 'Cassette runtime exceeds three days!'
            Logging_Services('AppendLog', ObjLog, LogData, @RM, @FM, False$)       
            
            Error_Services('Add', 'Cassette runtime exceeds three days!')
            OrigFileError = 104:': Cassette runtime exceeds three days!'
            Status        = 0
            Record        = ''
            ActionFlow    = ACTION_STOP$
            
        Case Otherwise$
            SaveRecord = Record
            
    End Case
    
return


WRITE_RECORD:

    WONo          = Record<RDS_WO$>
    
    ReactType     = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X')
    EpiPro        = (ReactType EQ 'EPP')
    GaN           = (ReactType EQ 'GAN')
    NonEpiPro     = ( (ReactType NE 'EPP') and (ReactType NE 'GAN') )

    If NonEpiPro then
        // Sync up VER, LOAD, UNLOAD, POST, and QA signatures with WO_MAT signature profile.
        // The goal of signature services was to remove any dependency on the WO_MAT signature profile,
        // however Fabtime relies on this data being replicated to the Scrape SQL DB. 
        WOMatKey      = {WO_MAT_KEY}
        WOMatRec      = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
        WOMatSigProf  = obj_WO_Mat('CassSigProfile', WOMatKey)
        WOMatSig      = WOMatRec<WO_MAT_SIGNATURE$>
        WOMatSigDTM   = WOMatRec<WO_MAT_SIG_DTM$>

        // Update WO_MAT signature profile to match for the "1VER" stage
        Stage = '1VER'
        Locate Stage in WOMatSigProf using @VM setting vPos then
            WOMatSig<0, vPos>    = Record<RDS_PRE_EPI_SIG$>
            PreEpiDate           = Record<RDS_PRE_EPI_SIG_DATE$>
            If PreEpiDate NE '' then
                PreEpiTime           = Record<RDS_PRE_EPI_SIG_TIME$>
                PreEpiSigDTM = PreEpiDate + (PreEpiTime/86400)
            end else
                PreEpiSigDTM = ''
            end
            WOMatSigDTM<0, vPos> = PreEpiSigDTM
        end
        
        // Update WO_MAT signature profile to match for "1LOAD" stage
        Stage = '1LOAD'
        Locate Stage in WOMatSigProf using @VM setting vPos then
            WOMatSig<0, vPos>    = Record<RDS_OPERATOR_IN$>
            DateIn               = Record<RDS_DATE_IN$>
            If DateIn NE '' then
                LoadTime             = Record<RDS_TIME_IN$>
                LoadSigDTM = DateIn + (LoadTime/86400)
            end else
                LoadSigDTM = ''
            end
            WOMatSigDTM<0, vPos> = LoadSigDTM
        end
        
        // Update WO_MAT signature profile to match for the "1UNLOAD" stage
        Stage = '1UNLOAD'
        Locate Stage in WOMatSigProf using @VM setting vPos then
            WOMatSig<0, vPos>    = Record<RDS_OPERATOR_OUT$>
            DateOut              = Record<RDS_DATE_OUT$>
            If DateOut NE '' then
                TimeOut              = Record<RDS_TIME_OUT$>
                TimeOutSigDTM = DateOut + (TimeOut/86400)
            end else
                TimeOutSigDTM = ''
            end
            WOMatSigDTM<0, vPos> = TimeOutSigDTM
        end
        
        // Update WO_MAT signature profile to match for the "1POST" stage
        Stage = '1POST'
        Locate Stage in WOMatSigProf using @VM setting vPos then
            WOMatSig<0, vPos>    = Record<RDS_POST_EPI_SIG$>
            PostSigDate          = Record<RDS_POST_EPI_SIG_DATE$>
            If PostSigDate NE '' then
                PostSigTime          = Record<RDS_POST_EPI_SIG_TIME$>
                PostSigDTM = PostSigDate + (PostSigTime/86400)
            end else
                PostSigDTM = ''
            end
            WOMatSigDTM<0, vPos> = PostSigDTM
        end   
        
        // Update WO_MAT signature profile to match for the "1QA" stage
        Stage = '1QA'
        Locate Stage in WOMatSigProf using @VM setting vPos then
            WOMatSig<0, vPos>    = Record<RDS_SUP_VER_SIG$>
            FQADate              = Record<RDS_SUP_VER_SIG_DATE$>
            If FQADate NE '' then
                FQASigTime           = Record<RDS_SUP_VER_SIG_TIME$>
                FQASigDTM = FQADate + (FQASigTime/86400)
            end else
                FQASigDTM = ''
            end
            WOMatSigDTM<0, vPos> = FQASigDTM
        end        
        
        WOMatRec<WO_MAT_SIG_PROFILE$> = WOMatSigProf
        WOMatRec<WO_MAT_SIGNATURE$>   = WOMatSig
        WOMatRec<WO_MAT_SIG_DTM$>     = WOMatSigDTM
        Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)
    end
    
    OrigDateOut = OrigRecord<RDS_DATE_OUT$>
    NewDateOut  = Record<RDS_DATE_OUT$>
    
    If ( ( (OrigDateOut EQ '') and (NewDateOut NE '') ) ) then
        // Mark this cassette as processed in the schedule event record
        DatetimeOut = {DATETIME_OUT}
        If EpiPro then           
            WMIKeys   = {WM_IN_KEY}
            For each WMIKey in WMIKeys using @VM setting vPos
                CassNo = Field(WMIKey, '*', 3)
                Service_Services('PostProcedure', 'SCHEDULE_SERVICES', 'MarkCassProcessed':@VM:WONo:@VM:CassNo:@VM:DatetimeOut)
            Next WMIKey
        end else
            CassNo = Record<RDS_CASS_NO$>
            Service_Services('PostProcedure', 'SCHEDULE_SERVICES', 'MarkCassProcessed':@VM:WONo:@VM:CassNo:@VM:DatetimeOut)
        end
    end
    
return


DELETE_RECORD_PRE:

    Notes      = RetStack()
    Swap @FM with ' | ' in Notes
    LogData    = ''
    LogData<1> = LoggingDTM
    LogData<2> = @User4
    LogData<3> = Name
    LogData<4> = Notes
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM) 
   
return


DELETE_RECORD:
return


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

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


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


