Compile function TEST_RUN_Services(@Service, @Params)
#pragma precomp SRP_PreCompiler
$insert LOGICAL
$Insert APP_INSERTS
$Insert SERVICE_SETUP
$Insert TEST_WAFER_PROD_EQUATES
$Insert TEST_WAFER_TYPES_DISPO_OPTIONS_EQUATES
$Insert TEST_RUN_EQUATES
$Insert TEST_RUN_WAFER_EQUATES
$Insert TEST_RUN_OBJ_EQUATES
$Insert LOT_EQUATES
$Insert LOT_OPERATION_EQUATES

Declare function Nextkey, Error_Services, Environment_Services, OConv, Logging_Services, SRP_Hashtable, Lot_Services, GetTickCount, Xlate
Declare function SRP_Datetime, Database_Services, Test_Run_Services, File_Services, Status, delete, Insert, Datetime, SRP_Json, MemberOf
Declare subroutine Database_Services, Btree.Extract, Error_Services, Logging_Services, Rlist, Test_Run_Services, Lot_Services, SRP_Json
Declare subroutine Mona_Services

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

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

LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM

IsProd = Environment_Services('IsProd')
If IsProd EQ True$ then
	MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_TESTRUNSERVICES'
end else
	MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_TESTRUNSERVICES'
end

GoToService

Return Response or ""

//-----------------------------------------------------------------------------
// SERVICES
//-----------------------------------------------------------------------------


//Test Wafer Product Methods - related to TEST_WAFER_PROD table

/*
Create New Test Wafer Product
Creates a new entry in the TEST_WAFER_PROD table.
*/
Service CreateNewTestWaferProduct(ProductName)
    If ProductName NE '' then
        //Check for existing
        table = 'TEST_WAFER_PROD'
        Open "DICT ":table To @DICT Else
            Error_Services('Add', 'Error opening Test wafer product table')
            Return
        End
        Column          = "PART_NAME"
        Value           = ProductName
        search_criteria = Column:@VM:Value:@FM
        keylist         = ""
        option          = ""
        flag            = ""
        Btree.Extract(search_criteria, table, @DICT, keylist, option, flag)
        IF keylist EQ '' then
            NewTestWaferProdRec                             = ''
            NewTestWaferProdRec<TEST_WAFER_PROD_PART_NAME$> = ProductName
            NewTestWaferProdKey                             = Nextkey('TEST_WAFER_PROD')
            Database_Services('WriteDataRow', table, NewTestWaferProdKey, NewTestWaferProdRec)
            If Error_Services('NoError') then
                LogData       = ''
                LogData<1, 1> = LoggingDTM
                LogData<1, 2> = @User4
                LogData<1, 3> = 'Successfully created new product type with ID of ' : NewTestWaferProdKey
                Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
                Response      = NewTestWaferProdKey
            end else
                //Todo Add Logging for failure
                LogData       = ''
                
                LogData<1, 1> = LoggingDTM
                LogData<1, 2> = @User4
                LogData<1, 3> = 'Error Creating new test wafer product. ' : Error_Services('GetMessage')
                Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
                Response      = ''
            end
        end else
            
            LogData       = ''
            LogData<1, 1> = LoggingDTM
            LogData<1, 2> = @User4
            LogData<1, 3> = 'Error Creating new test wafer product. Product already exists!'
            Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
            Error_Services('Add', 'Product already exists!')
            Response      = ''
        end
    end else
        //Todo Add logging for failure
        LogData       = ''
        LogData<1, 1> = LoggingDTM
        LogData<1, 2> = @User4
        LogData<1, 3> = 'Product name was missing'
        Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
        Error_Services('Add', 'Product name was missing')
        Response      = ''
    end
end service

Service DisableTWProd(TWProdKey)
    If TWProdKey NE '' then
        TWProdRec                              = Database_Services('ReadDataRow', 'TEST_WAFER_PROD', TWProdKey)
        TWProdRec<TEST_WAFER_PROD_SORT_ORDER$> = 0
        Database_Services('WriteDataRow', 'TEST_WAFER_PROD', TWProdKey, TWProdRec)
    end else
        Error_Services('Add', 'Error in TEST_RUN_SERVICES -> DisableTWProd: No TWProdKeySupplied')
    end
end service

/*
Add disposition option to a test wafer product
Creates a new disposition entry in the TEST_WAFER_TYPES_DISPO_OPTION table, related to a TEST_WAFER_PROD record
*/
Service AddTestWaferProductDispo(TestWaferProdID, DispoName)
    If TestWaferProdID NE '' then
        IF DispoName NE '' then
            MatchingIDs     = False$
            CurrentDispoIDs = XLATE('TEST_WAFER_PROD', TestWaferProdID, TEST_WAFER_PROD_WAFER_DISPO_IDS$, 'X')
            For i = 1 to DCount(CurrentDispoIDs, @VM)
                DispoID          = CurrentDispoIDs<1, i>
                DispoDescription = Xlate('TEST_WAFER_TYPES_DISPO_OPTIONS', DispoID, TEST_WAFER_TYPES_DISPO_OPTIONS_DISPO_DESCRIPTION$, 'X')
                if DispoName EQ DispoDescription then MatchingIDs = true$
            Until MatchingIDs    
            Next i
            if NOT(MatchingIDs) then
                NewTestWaferProdDispoRec                                                     = ''
                NewTestWaferProdDispoRec<TEST_WAFER_TYPES_DISPO_OPTIONS_TEST_WAFER_PROD_ID$> = TestWaferProdID
                NewTestWaferProdDispoRec<TEST_WAFER_TYPES_DISPO_OPTIONS_DISPO_DESCRIPTION$>  = DispoName
                NewTestWaferProdDispoKey                                                     = Nextkey('TEST_WAFER_TYPES_DISPO_OPTIONS')
                Database_Services('WriteDataRow', 'TEST_WAFER_TYPES_DISPO_OPTIONS', NewTestWaferProdDispoKey, NewTestWaferProdDispoRec)
                If Error_Services('NoError') then
                    LogData       = ''
                    LogData<1, 1> = LoggingDTM
                    LogData<1, 2> = @User4
                    LogData<1, 3> = 'Dispo added to ' : TestWaferProdID : '. The dispo key is ' : NewTestWaferProdDispoKey
                    Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
                end else
                    LogData       = ''
                    LogData<1, 1> = LoggingDTM
                    LogData<1, 2> = @User4
                    LogData<1, 3> = 'Error adding dispo to TW Prod, ' : Error_Services('GetMessage')
                    Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
                end
            end else
                LogData       = ''
                LogData<1, 1> = LoggingDTM
                LogData<1, 2> = @User4
                LogData<1, 3> = 'Error adding dispo to TW Prod. dispo option already existed.'
                Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
                Error_Services('Add', 'Error adding dispo to TW Prod. dispo option already existed.')
            end
        end else
            LogData       = ''
            LogData<1, 1> = LoggingDTM
            LogData<1, 2> = @User4
            LogData<1, 3> = 'Error adding dispo to TW Prod. The dispo name was missing'
            Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
        end
    end else
        LogData       = ''
        LogData<1, 1> = LoggingDTM
        LogData<1, 2> = @User4
        LogData<1, 3> = 'Error adding dispo to TW Prod. The product name was missing'
        Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
    end
end service

Service DeleteTestWaferProductDispo(DispoID)
    If DispoID NE '' then
        If RowExists('TEST_WAFER_TYPES_DISPO_OPTIONS', DispoID) then
            Database_Services('DeleteDataRow', 'TEST_WAFER_TYPES_DISPO_OPTIONS', DispoID, True$, False$)
        end else
            errorMessage  = 'Error deleting dispo to TW Prod. The dispo id was not found.'
            LogData       = ''
            LogData<1, 1> = LoggingDTM
            LogData<1, 2> = @User4
            LogData<1, 3> = errorMessage
            Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
            Error_Services('Add', errorMessage)
        end
    end else
        errorMessage  = 'Error deleting dispo to TW Prod. The dispo id was missing'
        LogData       = ''
        LogData<1, 1> = LoggingDTM
        LogData<1, 2> = @User4
        LogData<1, 3> = errorMessage
        Logging_Services('AppendLog', objLogTWP, LogData, @FM, @VM)
        Error_Services('Add', errorMessage)
    end
end service

Service GetAllTWProdKeysUnformatted()
    keylist  = ''
    RList("SELECT TEST_WAFER_PROD WITH SORT_ORDER NE '' BY SORT_ORDER", 5, '', '', '')
    Done     = False$
    Loop
        Readnext TWProdKey else Done = True$
    Until Done
        keylist<-1> = TWProdKey
    Repeat
    
    Response = keylist
end service

Service GetAllTWProdKeys(ShowSorted)
    keylist  = ''
    ProdList = ''
    If ShowSorted then
        RList("SELECT TEST_WAFER_PROD WITH SORT_ORDER NE 0 AND WITH SORT_ORDER NE '' BY SORT_ORDER", 5, '', '', '')
    end else
        RList("SELECT TEST_WAFER_PROD BY SORT_ORDER", 5, '', '', '')
    end
    
    Done     = False$
    Loop
        Readnext TWProdKey else Done = True$
    Until Done
        keylist<1, -1> = TWProdKey
    Repeat
    for each ProdKey in keylist using @VM setting pPos
        ProdName = Xlate('TEST_WAFER_PROD', ProdKey, TEST_WAFER_PROD_PART_NAME$, 'X')
        if ProdName NE '' then
            ProdList<1, pPos> = ProdKey
            ProdList<2, pPos> = ProdName
        end
    Next ProdKey
    Response = ProdList
end service

Service UpdateTWProdSortOrder(TWProdKey, NewOrder)
    //First get current sort order
    TWProdRec        = Database_Services('ReadDataRow', 'TEST_WAFER_PROD', TWProdKey)
    OldSortOrder     = TWProdRec<TEST_WAFER_PROD_SORT_ORDER$>
    AllTWProdsSorted = Test_Run_Services('GetAllTWProdKeysUnformatted')
    //Get rid of all zero value sort orders
    i                = 1
    LOOP
    UNTIL AllTWProdsSorted<i> = ''
        currSortOrder = Xlate('TEST_WAFER_PROD', AllTWProdsSorted<i>, TEST_WAFER_PROD_SORT_ORDER$, 'X')
        If currSortOrder EQ 0 then
            //Remove it from the list of TWProds with Sorting enabled. The zero's will screw up the next part.
            AllTWProdsSorted = delete(AllTWProdsSorted, i, 0, 0)
        end
        i = i + 1
    REPEAT
    If NewOrder GT 0 then
        AllTWProdsSorted = Insert(AllTWProdsSorted, NewOrder, 0, 0, TWProdKey)
        //Locate the old order and remove it
        i                = 1
        LOOP
        UNTIL AllTWProdsSorted<i> = ''
            If i NE NewOrder AND AllTWProdsSorted<i> EQ TWProdKey then
                //Remove it from the list of TWProds with Sorting enabled. The zero's will screw up the next part.
                AllTWProdsSorted = delete(AllTWProdsSorted, i, 0, 0)
            end
            i = i + 1
        REPEAT
    end else
        //Disabling
        Locate TWProdKey in AllTWProdsSorted using @FM setting tPos then
            AllTWProdsSorted                              = delete(AllTWProdsSorted, tPos, 0, 0)
        end
        TWProdRecToWrite                              = Database_Services('ReadDataRow', 'TEST_WAFER_PROD', TWProdKey)
        TWProdRecToWrite<TEST_WAFER_PROD_SORT_ORDER$> = 0
        Database_Services('WriteDataRow', 'TEST_WAFER_PROD', TWProdKey, TWProdRecToWrite, 1, 0, 1)
    end
    //Rewrite the entire sort order for all prods
    For each TWProd in AllTWProdsSorted using @FM setting SortOrder
        TWProdRecToWrite                              = Database_Services('ReadDataRow', 'TEST_WAFER_PROD', TWProd)
        TWProdRecToWrite<TEST_WAFER_PROD_SORT_ORDER$> = SortOrder
        Database_Services('WriteDataRow', 'TEST_WAFER_PROD', TWProd, TWProdRecToWrite, 1, 0, 1)
    Next TWProd
end service

Service GetAllTestRunTypes()
    StartTick = GetTickCount()
    MetricName = 'GetAllTestRunTypes'
    
    keylist = ''
    rtList  = ''
    RList('SELECT TEST_RUN_TYPE BY RUN_TYPE', 5, '', '', '')
    Done    = False$
    Loop
        Readnext RunTypeKey else Done = True$
    Until Done
        keylist<1, -1> = RunTypeKey
    Repeat
    for each RTKey in keylist using @VM setting rPos
        RunTypeName = Xlate('TEST_RUN_TYPE', RTKey, TEST_RUN_TYPE_RUN_TYPE$, 'X')
        if RunTypeName NE '' then
            rtList<1, rPos> = RTKey
            rtList<2, rPos> = RunTypeName
        end
    Next ProdKey
    Response = rtList
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
end service

Service GetTestWaferLots(ShowOnlyOpenLots)
    ErrorMessage     = ''
    TestWaferLotKeys = ''
    SearchString     = 'TYPE':@VM:'TW':@FM
    If ShowOnlyOpenLots then
        SearchString := 'OPEN':@VM:True$:@FM
    end
    Open 'DICT.LOT' to @DICT then
        Btree.Extract(SearchString, 'LOT', @DICT, TestWaferLotKeys, '', '')
    end else
        ErrorMessage = 'Error opening LOT dictionary.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
    Response = TestWaferLotKeys
end service

Service GetTestRunsByUsername(Username, StartDTM, StopDTM)
    
    If Username NE '' then
        Begin Case
            Case StartDTM EQ '' AND StopDTM EQ ''
                //Set search date for last 12 hour period
                StopDTM  = SRP_Datetime('Now')
                StartDTM = SRP_Datetime('AddHours', StopDTM, -12)
            Case StartDTM EQ '' AND StopDTM NE ''
                //Set search start date 12 hours prior to stopDTM
                StartDTM = SRP_Datetime('AddHours', StopDTM, -12)
            Case StartDTM NE '' AND StopDTM EQ ''
                //Set search start date to current dtm
                StopDTM = SRP_Datetime('Now')
        End Case
        
        table = "TEST_RUN"
        Open "DICT ":table To hTestRunDict Else
            Error_Services('Add', 'Error opening TEST_RUN dictionary')
        End
        If Error_Services('NoError') then
            srch_strng = "RUN_DTM":@VM:StartDTM:'~':StopDTM:@FM: "LSL_USER_ID":@VM:UserName:@FM
            keylist         = ""
            option          = ""
            flag            = ""
            Btree.Extract(srch_strng, table, hTestRunDict, keylist, option, flag)
            Swap @VM with @FM in keylist
            Response = keylist
        end
    end else
        Error_Services('Add', 'Username missing.')
    end
    
end service

Service GetTestWaferUsageStats(Username, StartDTM, StopDTM)
    
    MostRecentDTM = ''
    WafersUsed = 0
    If Username NE '' then
        KeyList = Test_Run_Services('GetTestRunsByUsername', Username, StartDTM, StopDTM)
        If KeyList NE '' then
            For Each Key in KeyList
                TestRunRec = Xlate('TEST_RUN', Key, '', 'X')
                If MostRecentDTM EQ '' then
                    MostRecentDTM = TestRunRec<TEST_RUN_RUN_DTM$>
                end else
                    If MostRecentDTM LT TestRunRec<TEST_RUN_RUN_DTM$> then
                        MostRecentDTM = TestRunRec<TEST_RUN_RUN_DTM$>
                    end
                end
                WaferQty = Dcount(TestRunRec<TEST_RUN_TEST_RUN_WAFER_IDS$>, @VM)
                WafersUsed += WaferQty
            Next Key
        end
        Response = ''
        Response<1> = MostRecentDTM
        Response<2> = WafersUsed
     end else
        Error_Services('Add', 'Username missing.')
     end
    
end service


Service CreateTestRunRecord(RunTypeID, EqpType, EqpID, PSNo, RDSNo, UserID, TWLotIds, TWLotQtys, ManualQtyAdjust)
    
    StartTick = GetTickCount()
    MetricName = 'CreateTestRunRecord'
    
    TWRunKey     = ''
    Response     = ''
    ErrorMessage = ''
    If ManualQtyAdjust EQ '' then ManualQtyAdjust = False$
	If TWLotIds NE '' then
		for each TWLotId in TWLotIds using @VM setting twLotPos
			ThisTWLotCurrQty = Database_Services('ReadDataColumn', 'LOT', TWLotId, LOT_WAFER_QTY$, True$, 0, False$)
			If ThisTWLotCurrQty then
				ThisTWLotUsageQty = TWLotQtys<1, twLotPos>
				If ThisTWLotCurrQty LT ThisTWLotUsageQty then
					ErrorMessage = 'Error in Create Test Run Record routine. Lot # ' : TWLotId : ' does not have enough wafers. Please choose ' : ThisTWLotCurrQty : ' wafers or less.'
				end
			end else
				ErrorMessage = 'Error in Create Test Run Record routine. Lot # ' : TWLotId : ' has a zero wafer quantity.'
			end
		Until ErrorMessage NE ''
		Next TWLotId
	end else
		ErrorMessage = 'Error in Create Test Run Record routine. No test wafer lots entered.'
	end
    If ErrorMessage EQ '' then
        If (RunTypeID NE '' AND UserID NE '' AND EqpType NE '' AND EqpID NE '') OR (RunTypeID NE '' AND UserID NE '' AND ManualQtyAdjust EQ True$) then
            RunDTM                             = SRP_Datetime('Now')
            TWRunRec                           = ''
            TWRunRec<TEST_RUN_RUN_DTM$>        = RunDTM
            TWRunRec<TEST_RUN_RUN_TYPE_ID$>    = RunTypeID
            TWRunRec<TEST_RUN_EQUIPMENT_ID$>   = EqpID
            TWRunRec<TEST_RUN_EQUIPMENT_TYPE$> = EqpType
            TWRunRec<TEST_RUN_PROD_SPEC_ID$>   = PSNo
            TWRunRec<TEST_RUN_LSL_USER_ID$>    = UserID
            TWRunRec<TEST_RUN_RDS_ID$>         = RDSNo
            TWRunKey                           = nextkey('TEST_RUN')
            Database_Services('WriteDataRow', 'TEST_RUN', TWRunKey, TWRunRec)
            If Error_Services('NoError') then
                If RowExists('TEST_RUN', TWRunKey) then
                    LogData       = ''
                    LogData<1, 1> = LoggingDTM
                    LogData<1, 2> = UserID
                    LogData<1, 3> = 'Successfully created new TEST_RUN record with ID of ' : TWRunKey
                    Logging_Services('AppendLog', objLogTR, LogData, @FM, @VM)
                    //Now create the test wafer usages
                    Success       = False$
                    If TWLotIds NE ''  then
                        //Supports new logging methods.
                        for each TWLotId in TWLotIds using @VM setting twlPos
                            LotRec                 = Database_Services('ReadDataRow', 'LOT', TWLotId, True$, 0, False$)
                            IsThisLotOnHold        = LotRec<LOT_HOLD$>
                            IsThisLotOpen          = LotRec<LOT_OPEN$>
                            IsThisLotATWLot        = LotRec<LOT_TYPE$> EQ 'TW'
                            ThisLotCurrQty         = LotRec<LOT_WAFER_QTY$>
                            ThisLotPartId          = LotRec<LOT_PROD_ID$>
                            
                            //
                            ThisUsageQty           = TWLotQtys<1, twlPos>
                            
                            If IsThisLotOpen then
                                If Not(IsThisLotOnHold) then
                                    If IsThisLotATWLot then
                                        InAtTWInUse = False$
                                        Loop
                                            LotMovedIn             = Lot_Services('IsLotMovedIn', TWLotId)
                                            ThisLotCurrOperationId = Lot_Services('GetLotCurrOperationId', TWLotId)
                                            ThisLotCurrOperation   = Database_Services('ReadDataColumn', 'LOT_OPERATION', ThisLotCurrOperationId, LOT_OPERATION_OPERATION_ID$, True$, 0, False$)
                                            Begin Case
                                                Case ThisLotCurrOperation EQ 'TW_IN_USE'
                                                    
                                                        If Not(LotMovedIn) then
                                                            Lot_Services('MoveInLot', TWLotId, UserId) 
                                                            If Error_Services('HasError') then
                                                                ErrorMessage = Error_Services('GetMessage')
                                                            end
                                                        end else
                                                            InAtTWInUse = True$
                                                        end
                                                    
                                                Case ThisLotCurrOperation EQ 'TW_READY_TO_USE'
                                                    
                                                    If Not(LotMovedIn) then
                                                       Lot_Services('MoveInLot', TWLotId, UserId)
                                                    end else
                                                       Lot_Services('MoveOutLot', TWLotId, UserId) 
                                                    end
                                                    If Error_Services('HasError') then
                                                        ErrorMessage = Error_Services('GetMessage')
                                                    end
                                                Case Otherwise$
                                                    ErrorMessage = 'Error: Lot ':TWLotId:' is currently at  ' : ThisLotCurrOperation : '. It cannot be used at this time.'
                                            End Case
                                        Until ErrorMessage NE '' Or InAtTWInUse
                                        Repeat
                                        If InAtTWInUse then
                                            if Num(ThisUsageQty) then
                                                If ThisUsageQty GT 0 then
                                                   //Now check that the usage QTY can be consumed from the TW Lot
                                                    ThisLotCurrQty = Database_Services('ReadDataColumn', 'LOT', TWLotId, LOT_WAFER_QTY$, True$, 0, False$)
                                                    If ThisUsageQty LE ThisLotCurrQty then
                                                        
                                                        Abort   = false$
                                                        for i = 1 to ThisUsageQty
                                                            Test_Run_Services('CreateTestRunWaferRecord',TWRunKey, ThisLotPartId, RunTypeID, TWLotId, UserID)
                                                            If Error_Services('HasError') then
                                                                ErrorMessage = Error_Services('GetMessage')
                                                            end
                                                        until ErrorMessage NE ''
                                                        Next i
                                                        If ErrorMessage EQ '' then
                                                            Lot_Services('ReduceLotWaferCount', TWLotId, ThisUsageQty, UserID) 
                                                        end 
                                                    end else
                                                        ErrorMessage = 'Error in Create Test Run Record routine, ' : TWLotId : ' does not have enough wafers.'
                                                    end
                                                end
                                            end else
                                                ErrorMessage = 'Error in Create Test Run Record routine, Invalid qty for lot ' : TWLotId : ' passed to routine.'
                                            end
                                        end
                                    end else
                                        //Lot is not classified as a test wafer lot.
                                        ErrorMessage = 'Lot is not a test wafer lot.'
                                    end
                                end else
                                    //Lot is on hold
                                    ErrorMessage = 'Lot is currently on hold.'
                                end
                            end else
                                //Lot is not open
                                ErrorMessage = 'Lot is currently not in an open status.'
                            end
                        until ErrorMessage NE ''
                        Next TWLotId
                    end else
                        //Supports legacy TW logging
                        Response = TWRunKey
                    end
                end else
                    ErrorMessage = 'Error in Create Test Run Record routine, Generated Test Run Key did not exist in TEST Run table.'
                end
                
                Response      = TWRunKey
            end else
                ErrorMessage = Error_Services('GetMessage')
            end
        end else
            If RunTypeID EQ '' then
                ErrorMessage := 'Run Type ID Missing. '
            end
            If EqpType EQ '' then
                ErrorMessage := 'Equipment Type ID Missing. '
            end
            If EqpID EQ '' then
                ErrorMessage := 'Equipment ID Missing. '
            end
            If UserID EQ '' then
                ErrorMessage := 'User ID Missing. '
            end
        end    
    end
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
    
    If ErrorMessage NE '' then
        LogData       = ''
        LogData<1, 1> = LoggingDTM
        LogData<1, 2> = @User4
        LogData<1, 3> = 'Error writing TEST_RUN Record ' : TWRunKey : ': ' : ErrorMessage 
        Logging_Services('AppendLog', objLogTR, LogData, @FM, @VM)
        Error_Services('Add', ErrorMessage)
        Response      = ''
    end
    
end service

Service CreateTestRunWaferRecord(TestRunID, TWPartID, TWUsageTypeID, SourceLotId, Username)
    StartTick = GetTickCount()
    MetricName = 'CreateTestRunWaferRecord'
    
    ErrorMessage = ''
    If TestRunID NE '' AND TWPartID NE '' then
        If RowExists('TEST_RUN', TestRunID) then
                //Legacy Code: Pre LOT usage
                TRWaferRec                                     = ''
                TRWaferRec<TEST_RUN_WAFER_TEST_RUN_ID$>        = TestRunID
                TRWaferRec<TEST_RUN_WAFER_TEST_WAFER_PROD_ID$> = TWPartID
                TRWaferRec<TEST_RUN_WAFER_TEST_USAGE_TYPE_ID$> = TWUsageTypeID
                TRWaferRec<TEST_RUN_WAFER_SOURCE_LOT_ID$>      = SourceLotID
                TRWaferKey                                     = Nextkey('TEST_RUN_WAFER')
                Database_Services('WriteDataRow', 'TEST_RUN_WAFER', TRWaferKey, TRWaferRec)
                If Error_Services('NoError') AND RowExists('TEST_RUN_WAFER', TRWaferKey) then
                    LogData       = ''
                    LogData<1, 1> = LoggingDTM
                    LogData<1, 2> = @User4
                    LogData<1, 3> = 'Successfully wrote ' : TRWaferKey : ' to the TEST_RUN_WAFER table.'
                    Logging_Services('AppendLog', objLogTR, LogData, @FM, @VM)
                    //Increment Usage in TEST_WAFER_PROD Record
                    If ErrorMessage = '' then
                        Test_Run_Services('IncrementTWProdUsage', TWPartID)
                        Response      = TRWaferKey
                    end
                end else
                    ErrorMessage := 'Error writing TEST_RUN_WAFER record with key of ' : TRWaferKey '. '
                end
        end else
            ErrorMessage := 'Unable to find parent TEST_RUN record. '
        end
    end else
        If TestRunID EQ '' then
            ErrorMessage := 'TestRunID parameter was not passed to CreateTestRunWaferRecord method. '
        end
        If TWPartID EQ '' then
            ErrorMessage := 'TWPartID parameter was not passed to CreateTestRunWaferRecord method. '
        end
    end
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
    
    If ErrorMessage NE ''  then
        LogData       = ''
        LogData<1, 1> = LoggingDTM
        LogData<1, 2> = @User4
        LogData<1, 3> = 'Error Writing to the TEST_RUN_WAFER table. ' : ErrorMessage
        Logging_Services('AppendLog', objLogTR, LogData, @FM, @VM)
        Error_Services('Add', 'Error creating new test run record: ' : ErrorMessage)
    end

end service

Service UseTWFromLot(LotId, UsageQty, Username)
    ErrorMessage = ''
    If RowExists('LOT', LotId) then
        Lot_Services('ReduceLotWaferCount', LotId, UsageQty, Username)
    end else
        ErrorMessage = 'Error logging wafer consumption from Lot # ' : LotId : '. Lot does not exist in LOT table.'
    end
    If ErrorMessage EQ '' then
        
    end else
        //Log Error
    end
end service

Service GetTestRunById(TestRunID)
    If TestRunID NE '' then
        If RowExists('TEST_RUN', TestRunID) then
            TRRec    = Database_Services('ReadDataRow', 'TEST_RUN', TestRunID)
            Response = TRRec
        end else
            Error_Services('Add', 'Test Run ID does not exist in TEST_RUN table')
        end
    end else
        Error_Services('Add', 'Test Run ID parameter was not passed to routine.')
    end
end service

Service IncrementTWProdUsage(TWProdKey)
    If TWProdKey NE '' then
        TWProdRec                         = Database_Services('ReadDataRow', 'TEST_WAFER_PROD', TWProdKey)
        TWProdRec<TEST_WAFER_PROD_USAGE$> = TWProdRec<TEST_WAFER_PROD_USAGE$> + 1
        Database_Services('WriteDataRow', 'TEST_WAFER_PROD', TWProdKey, TWProdRec)
    end
end service

Service GetTestRunObj(TestRunID)
    TestRunObj                             = ''
    TestRunRec                             = Test_Run_Services('GetTestRunById', TestRunID)
    //Start getting translated values for object
    TestRunObj<TEST_RUN_OBJ_TEST_RUN_ID$>  = TestRunID
    TestRunObj<TEST_RUN_OBJ_RUN_DTM$>      = OConv(TestRunRec<TEST_RUN_RUN_DTM$>, 'DT')
    TestRunObj<TEST_RUN_OBJ_RUN_TYPE$>     = Xlate('TEST_RUN_TYPE', TestRunRec<TEST_RUN_RUN_TYPE_ID$>, TEST_RUN_TYPE_RUN_TYPE$, 'X')
    TestRunObj<TEST_RUN_OBJ_EQUIPMENT_ID$> = TestRunRec<TEST_RUN_EQUIPMENT_ID$>
    EqpType                                = TestRunRec<TEST_RUN_EQUIPMENT_TYPE$>
    Begin Case
        Case EqpType EQ 'R'
            TestRunObj<TEST_RUN_OBJ_EQUIPMENT_TYPE$> = 'Reactor'
        Case EqpType EQ 'T'
            TestRunObj<TEST_RUN_OBJ_EQUIPMENT_TYPE$> = 'Non-Reactor'
    End Case
    TestRunObj<TEST_RUN_OBJ_PROD_SPEC_ID$> = TestRunRec<TEST_RUN_PROD_SPEC_ID$>
    TestRunObj<TEST_RUN_OBJ_LSL_USER_ID$>  = TestRunRec<TEST_RUN_LSL_USER_ID$>
    TestRunObj<TEST_RUN_OBJ_RDS_ID$>       = TestRunRec<TEST_RUN_RDS_ID$>
    
    TWKeys                                = TestRunRec<TEST_RUN_TEST_RUN_WAFER_IDS$>
    TWUsageProds                       = ''
    TWUsageQtys                        = ''
    for each TWKey in TWKeys using @VM setting tPos
        TRWRec            = Test_Run_Services('GetTestRunWaferByID', TWKey)
        TestWaferProdName = XLATE('TEST_WAFER_PROD', TRWRec<TEST_RUN_WAFER_TEST_WAFER_PROD_ID$>, TEST_WAFER_PROD_PART_NAME$, 'X')
        Locate TestWaferProdName in TWUsageProds setting iPos then
            TWUsageProds<1,iPos> = TestWaferProdName
            TWUsageQtys<1,iPos>  = TWUsageQtys<1,iPos> + 1
        end else
            TWUsageProds<1,-1> = TestWaferProdName
            TWUsageQtys<1,-1>  = 1
        end
    Next TWKey
    TestRunObj<TEST_RUN_OBJ_TEST_RUN_WAFER_PRODS$>      = TWUsageProds
    TestRunObj<TEST_RUN_OBJ_TEST_RUN_WAFER_PRODS_QTYS$> = TWUsageQtys
    
    Response = TestRunObj
end service

Service GetTestWaferUsageByDateSpan(StartDtm, StopDtm)
    
    TestWaferData = ''
    if StopDTM EQ '' then
        StopDTM = SRP_Datetime('Now')
    end
    If StartDTM EQ '' then
        StartDTM = SRP_Datetime('AddDays', StopDtm, -30)
    end
    //Get a list of Test Run keys
    TestRunKeys = Test_Run_Services('GetTestRunKeysByDateSpan', StartDtm, StopDtm)
    
    For each TestRunKey in TestRunKeys using @VM setting tPos
        //Get all Product usages and their quantities
        TestRunRec  = Database_Services('ReadDataRow', 'TEST_RUN', TestRunKey)
        TWUsageKeys = TestRunRec<TEST_RUN_TEST_RUN_WAFER_IDS$>
        TWUsageData = ''
        for each TWUsageKey in TWUsageKeys using @VM setting twPos
            TWProdTypeKey = Xlate('TEST_RUN_WAFER', TWUsageKey, TEST_RUN_WAFER_TEST_WAFER_PROD_ID$, 'X')
            TWProdType    = Xlate('TEST_WAFER_PROD', TWProdTypeKey, TEST_WAFER_PROD_PART_NAME$, 'X')
            Locate TWProdType in TWUsageData<1>  using @VM setting twuPos then
                TWUsageData<2, twuPos> = TWUsageData<2, twuPos> + 1
            end else
                TWUsageData<1, -1> = TWProdType
                TWUsageData<2, -1> = 1
            end
            
            
        Next TWUsageKey
        
        For each TWUsage in TWUsageData<1> using @VM setting aPos
            TWLine                                             = ''
            TWLine<1, TEST_RUN_OBJ_TEST_RUN_ID$>               = TestRunKey
            TWLine<1, TEST_RUN_OBJ_RUN_DTM$>                   = OConv(TestRunRec<TEST_RUN_RUN_DTM$>, 'DT')
            TWLine<1, TEST_RUN_OBJ_RUN_TYPE$>                  = XLATE('TEST_RUN_TYPE', TestRunRec<TEST_RUN_RUN_TYPE_ID$>, 'RUN_TYPE', 'X')
            TWLine<1, TEST_RUN_OBJ_EQUIPMENT_ID$>              = TestRunRec<TEST_RUN_EQUIPMENT_ID$>
            TWLine<1, TEST_RUN_OBJ_EQUIPMENT_TYPE$>            = TestRunRec<TEST_RUN_EQUIPMENT_TYPE$>
            TWLine<1, TEST_RUN_OBJ_PROD_SPEC_ID$>              = TestRunRec<TEST_RUN_PROD_SPEC_ID$>
            TWLine<1, TEST_RUN_OBJ_LSL_USER_ID$>               = TestRunRec<TEST_RUN_LSL_USER_ID$>
            TWLine<1, TEST_RUN_OBJ_RDS_ID$>                    = TestRunRec<TEST_RUN_RDS_ID$>
            TWLine<1, TEST_RUN_OBJ_TEST_RUN_WAFER_PRODS$>      = TWUsage
            TWLine<1, TEST_RUN_OBJ_TEST_RUN_WAFER_PRODS_QTYS$> = TWUsageData<2, aPos>
            TestWaferData<-1>                                  = TWLine
        Next TWUsage
    Next TestRunKey
    
    Response = TestWaferData
end service

Service GetTestRunKeysByDateSpan(StartDtm, StopDtm)
    
    Begin Case
        Case StartDTM EQ '' AND StopDTM EQ ''
            //Set search date for last 24 hour period
            StopDTM  = SRP_Datetime('Now')
            StartDTM = SRP_Datetime('AddHours', StopDTM, -24)
        Case StartDTM EQ '' AND StopDTM NE ''
            //Set search start date 24 hours prior to stopDTM
            StartDTM = SRP_Datetime('AddHours', StopDTM, -24)
        Case StartDTM NE '' AND StopDTM EQ ''
            //Set search start date to current dtm
            StopDTM = SRP_Datetime('Now')
    End Case
    
    table = "TEST_RUN"
    Open "DICT ":table To @DICT Else
        Error_Services('Add', 'Error opening TEST_RUN dictionary')
    End
    If Error_Services('NoError') then
        srch_strng = "RUN_DTM":@VM:StartDTM:'~':StopDTM:@FM
        keylist         = ""
        option          = ""
        flag            = ""
        Btree.Extract(srch_strng, table, @DICT, keylist, option, flag)
        Response = keylist
    end
    
end service

Service GetTestRunKeysByEqp(EquipType, EquipID)
    
end service

Service GetTestRunKeysByEqpType(EqpType)
    
end service

Service GetTestRunKeysByPSN(PSNo)
    
end service

Service GetTestRunKeysByRDS(RDSNo)
    StartTick = GetTickCount()
    MetricName = 'GetTestRunKeysByRDS'
    
    table = "TEST_RUN"
    Open "DICT ":table To @DICT Else
        Error_Services('Add', 'Error opening TEST_RUN dictionary')
    End
    If Error_Services('NoError') then
        srch_strng = "RDS_ID":@VM:RDSNo:@FM
        keylist         = ""
        option          = ""
        flag            = ""
        Btree.Extract(srch_strng, table, @DICT, keylist, option, flag)
        Response = keylist
    end
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
end service

Service GetTestRunWaferByID(TRWaferID)
    If TRWaferID NE '' then
        If RowExists('TEST_RUN_WAFER', TRWaferID) then
            Response = Database_Services('ReadDataRow', 'TEST_RUN_WAFER', TRWaferID)
            If Error_Services('HasError') then
                Response = ''
                Error_Services('Add', 'Error reading TEST_RUN_WAFER record with ID ' : TRWaferID)
            end
        end else
            Error_Services('Add', 'No TEST_RUN_WAFER record found that matches key value.')
        end
    end else
        Error_Services('Add', 'TRWaferID was null')
    end
end service

Service GetTestRunWaferKeysByProdID(ProdID)
    
end service

Service GenerateTWCSVReport(StartDTM, StopDTM, SavePath)
    If SavePath NE '' then
        CSVData  = 'ID' : @VM : 'Run Time' : @VM : 'Test Run Type' : @VM : 'Equipment ID' : @VM : 'Equipment Type' : @VM : 'PSN' : @VM : 'User' : @VM : 'RDS' : @VM : 'Test Wafer Type' : @VM : 'Wfr Qty' : @FM
        CSVData := Test_Run_Services('GetTestWaferUsageByDateSpan', StartDTM, StopDTM)
        swap @VM with ',' in CSVData
        swap @FM with CRLF$ in CSVData
        If Not(File_Services('CheckFileExtension', SavePath, 'csv')) then
            SavePath = SavePath : '.csv'
        end
        OSWrite CSVData To SavePath
        WriteFailure = Status()
        If WriteFailure then
            Begin Case
                Case WriteFailure EQ 1
                    Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, Bad OS filename.')
                Case WriteFailure EQ 2
                    Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, Access denied by operating system.')
                Case WriteFailure EQ 3
                    Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, Disk or directory full.')
                Case WriteFailure EQ 4
                    Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, File does not exist.')
                Case WriteFailure EQ 5
                    Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, Unknown error.')
                Case WriteFailure EQ 6
                    Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, Attempt to write to a read-only file.')
            End Case
        end
    end else
        Error_Services('Add', 'Error in TEST_RUN_SERVICES -> GenerateTWCSVReport, No SavePath provided.')
    end
    
end service

Service IsNewTWSystemActive(UserId)
    IsTWSystemActive       = False$
    If UserId EQ '' then
        UserId = @USER4
    end
    TWTrackingSystemActive = Database_Services('ReadDataColumn', 'APP_INFO', 'NEW_TW_SYSTEM_ACTIVE_SWITCH', 1, True$, 0, False$)
    If TWTrackingSystemActive EQ True$ then
       IsTWSystemActive = True$ 
    end else
        If MemberOf(UserId, 'TW_TRACKING_TEST') then
            IsTWSystemActive = True$ 
        end
    end
    
end service

