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

    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 SRP Computer Solutions, Inc.

    Name        :   Epi_Part_Services

    Description :   Handler program for all module related services.

    Notes       :   The generic parameters should contain all the necessary information to process the services. Often
                    this will be information like the data Record and Key ID.

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

    Metadata    :

    History     :   (Date, Initials, Notes)
        06/29/17    dmb     Original programmer. - [EPIOI-8]

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

#pragma precomp SRP_PreCompiler

$insert APP_INSERTS
$insert SERVICE_SETUP
$insert WO_LOG_EQUATES
$insert EPI_PART_EQUATES
$insert PROD_VER_EQUATES
$insert PROD_SPEC_EQUATES
$insert RLIST_EQUATES

Declare subroutine  Error_Services, Epi_Part_Services, Memory_Services, RList, Database_Services
Declare function    SRP_Array, Epi_Part_Services, Memory_Services, Database_Services, SRP_Sort_Array

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

Return Response else ''


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options REACTORTYPES        = 'ASM', 'ASM+', 'HTR', 'EpiPro', 'GaN'

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

//----------------------------------------------------------------------------------------------------------------------
// SetReactorUtilization
//
// Sets the utilization constant for the indicated reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service SetReactorUtilization(ReactorType=REACTORTYPES, ReactorUtilization)

    Convert @Lower_Case to @Upper_Case in ReactorType

    If ReactorType NE '' AND ReactorUtilization NE '' then
        Database_Services('WriteDataRow', 'CONFIG', 'ReactUtil*' : ReactorType, ReactorUtilization, True$)
        If Error_Services('NoError') then
            Memory_Services('SetValue', ServiceModule : '*GetReactorUtilization*' : ReactorType, ReactorUtilization)
        end
    end else
        Error_Services('Add', 'ReactorType or ReactorUtilization argument was missing from the ' : Service : ' service.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// GetReactorUtilization
//
// Returns the utilization constant for the indicated reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service GetReactorUtilization(ReactorType=REACTORTYPES)

    Convert @Lower_Case to @Upper_Case in ReactorType
    ServiceKeyID       := '*' : ReactorType
    ReactorUtilization  = Memory_Services('GetValue', ServiceKeyID)

    If ReactorUtilization EQ '' then
        If ReactorType NE '' then
            ReactorUtilization  = Database_Services('ReadDataRow', 'CONFIG', 'ReactUtil*' : ReactorType)
            If Error_Services('NoError') then
                Memory_Services('SetValue', ServiceKeyID, ReactorUtilization)
            end
        end else
            Error_Services('Add', 'ReactorType argument was missing from the ' : Service : ' service.')
        end
    end
    
    Response    = ReactorUtilization

end service


//----------------------------------------------------------------------------------------------------------------------
// GetProdVerNo
//
// Returns the minutes per wafer associated to the indicated Epi Part and reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service GetProdVerNo(EpiPartNo, ReactorType=REACTORTYPES)

    Convert @Lower_Case to @Upper_Case in ReactorType
    ServiceKeyID   := '*' : EpiPartNo : '*' : ReactorType
    ProdVerNo       = Memory_Services('GetValue', ServiceKeyID)

    If ProdVerNo EQ '' then
        If EpiPartNo NE '' AND ReactorType NE '' then
            EpiPartRec  = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
            If Error_Services('NoError') then
                // Convert the friendly reactor type name to the internal one used in the database.
                Begin Case
                    Case ReactorType _EQC 'EpiPro'  ; ReactorType = 'EPP'
                    Case ReactorType _EQC 'ASM'     ; ReactorType = 'EPS'
                End Case
                ProdVerNos      = EpiPartRec<EPI_PART_PROD_VER_NO$>
                NumProdVers     = DCount(ProdVerNos, @VM)
                FoundProdVer    = False$
                For ProdVerCnt = NumProdVers to 1 Step -1
                    ProdVerNo   = ProdVerNos<0, ProdVerCnt>
                    ProdVerRec  = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo)
                    If Error_Services('NoError') then
                        ProdVerReactorType  = ProdVerRec<PROD_VER_REACT_TYPE$>
                        If ProdVerReactorType _EQC ReactorType then FoundProdVer = True$
                    end
                Until FoundProdVer
                Next ProdVerCnt
                If FoundProdVer then
                    Memory_Services('SetValue', ServiceKeyID, ProdVerNo)
                end
            end
        end else
            Error_Services('Add', 'EpiPartNo or ReactorType argument was missing from the ' : Service : ' service.')
        end
    end
    
    Response    = ProdVerNo

end service


//----------------------------------------------------------------------------------------------------------------------
// GetMinutesPerWaferCost
//
// Returns the minutes per wafer needed for costing associated to the indicated Epi Part and reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service GetMinutesPerWaferCost(EpiPartNo, ReactorType=REACTORTYPES)

    Convert @Lower_Case to @Upper_Case in ReactorType
    ServiceKeyID       := '*' : EpiPartNo : '*' : ReactorType
    MinutesPerWafer     = Memory_Services('GetValue', ServiceKeyID)

    If MinutesPerWafer EQ '' then
        If EpiPartNo NE '' AND ReactorType NE '' then
            ProdVerNo   = Epi_Part_Services('GetProdVerNo', EpiPartNo, ReactorType)
            If Error_Services('NoError') then
                ProdVerRec  = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo)
                If Error_Services('NoError') then
                    ProdSpecNo  = ProdVerRec<PROD_VER_PROC_STEP_PSN$>[-1, 'B' : @VM]   ; // Always get the last Prod Spec No to use.
                    ProdSpecRec = Database_Services('ReadDataRow', 'PROD_SPEC', ProdSpecNo)
                    If Error_Services('NoError') then
                        MinutesPerWafer = ProdSpecRec<PROD_SPEC_MINUTES_PER_WAFER$>
                        MinutesPerWafer = Oconv(MinutesPerWafer, 'MD3')
                        Memory_Services('SetValue', ServiceKeyID, MinutesPerWafer)
                    end
                end
            end
        end else
            Error_Services('Add', 'EpiPartNo or ReactorType argument was missing from the ' : Service : ' service.')
        end
    end
    
    Response    = MinutesPerWafer

end service


//----------------------------------------------------------------------------------------------------------------------
// SetMinutesPerWaferScheduler
//
// Sets the minutes per wafer needed for scheduling associated to the indicated Epi Part and reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service SetMinutesPerWaferScheduler(EpiPartNo, ReactorType=REACTORTYPES, MinutesPerWafer)

    Convert @Lower_Case to @Upper_Case in ReactorType

*    If EpiPartNo NE '' AND ReactorType NE '' AND MinutesPerWafer NE '' then
	If EpiPartNo NE '' AND ReactorType NE '' then
        EpiPartRec  = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
        If Error_Services('NoError') then
            ReactorTypes    = 'ASM,ASM+,HTR,EPIPRO,GAN'
            Locate ReactorType in ReactorTypes using ',' setting cPos then
            	If MinutesPerWafer NE '' then MinutesPerWafer = Iconv(MinutesPerWafer, 'MD3')
                EpiPartRec<EPI_PART_MINUTES_PER_WAFER_SCHEDULER$, cPos> = MinutesPerWafer
                Database_Services('WriteDataRow', 'EPI_PART', EpiPartNo, EpiPartRec, True$)
                If Error_Services('NoError') then
                    Memory_Services('SetValue', ServiceModule : '*GetMinutesPerWaferScheduler*' : EpiPartNo : '*' : ReactorType, Oconv(MinutesPerWafer, 'MD3'))
                end
            end
        end
    end else
*        Error_Services('Add', 'EpiPartNo, ReactorType or MinutesPerWafer argument was missing from the ' : Service : ' service.')
		Error_Services('Add', 'EpiPartNo or ReactorType argument was missing from the ' : Service : ' service.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// GetMinutesPerWaferScheduler
//
// Returns the minutes per wafer needed for scheduling associated to the indicated Epi Part and reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service GetMinutesPerWaferScheduler(EpiPartNo, ReactorType=REACTORTYPES)

    Convert @Lower_Case to @Upper_Case in ReactorType
    ServiceKeyID       := '*' : EpiPartNo : '*' : ReactorType
    MinutesPerWafer     = Memory_Services('GetValue', ServiceKeyID)

    If MinutesPerWafer EQ '' then
        If EpiPartNo NE '' AND ReactorType NE '' then
            EpiPartRec  = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
            If Error_Services('NoError') then
                ReactorTypes    = 'ASM,ASM+,HTR,EPIPRO,GAN'
                Locate ReactorType in ReactorTypes using ',' setting cPos then
                    MinutesPerWafer = EpiPartRec<EPI_PART_MINUTES_PER_WAFER_SCHEDULER$, cPos>
                    If (MinutesPerWafer + 0) NE 0 then
                        MinutesPerWafer = Oconv(MinutesPerWafer, 'MD3')
                        Memory_Services('SetValue', ServiceKeyID, MinutesPerWafer)
                    end else
                        Error_Services('Add', 'Minutes per wafer for Epi Part #' : EpiPartNo : ' is zero. Reactor Type: ' : ReactorType)
                    end
                end
            end
        end else
            Error_Services('Add', 'EpiPartNo or ReactorType argument was missing from the ' : Service : ' service.')
        end
    end
    
    Response    = MinutesPerWafer

end service


//----------------------------------------------------------------------------------------------------------------------
// GetIdealWafersPerDayScheduler
//
// Returns the ideal number of wafers per day associated to the indicated Epi Part and reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service GetIdealWafersPerDayScheduler(EpiPartNo, ReactorType=REACTORTYPES, MinutesPerWafer)

    Convert @Lower_Case to @Upper_Case in ReactorType
    ServiceKeyID := '*' : EpiPartNo : '*' : ReactorType : '*' : MinutesPerWafer
    WafersPerDay  = Memory_Services('GetValue', ServiceKeyID)

    If WafersPerDay EQ '' then
        If EpiPartNo NE '' AND ReactorType NE '' then
            If MinutesPerWafer EQ '' then MinutesPerWafer = Epi_Part_Services('GetMinutesPerWaferScheduler', EpiPartNo, ReactorType)
            If MinutesPerWafer EQ '' OR MinutesPerWafer EQ 0 then
                Error_Services('Add', 'Unexpected minutes per wafer value in EPI_PART #' : EpiPartNo)
            end
            If Error_Services('NoError') then
                WafersPerDay    = 1440 / MinutesPerWafer
                WafersPerDay    = Oconv(Iconv(WafersPerDay, 'MD2'), 'MD2')
                Memory_Services('SetValue', ServiceKeyID, WafersPerDay)
            end
        end else
            Error_Services('Add', 'EpiPartNo or ReactorType argument was missing from the ' : Service : ' service.')
        end
    end
    Response    = WafersPerDay

end service


//----------------------------------------------------------------------------------------------------------------------
// GetAdjustedWafersPerDayScheduler
//
// Returns the adjusted number of wafers per day associated to the indicated Epi Part and reactor type.
//----------------------------------------------------------------------------------------------------------------------
Service GetAdjustedWafersPerDayScheduler(EpiPartNo, ReactorType=REACTORTYPES, MinutesPerWafer)

    Convert @Lower_Case to @Upper_Case in ReactorType
    ServiceKeyID := '*' : EpiPartNo : '*' : ReactorType
    
    WafersPerDay = Memory_Services('GetValue', ServiceKeyID)

    If WafersPerDay EQ '' then
        If EpiPartNo NE '' AND ReactorType NE '' then
            If MinutesPerWafer EQ '' then MinutesPerWafer = Epi_Part_Services('GetMinutesPerWaferScheduler', EpiPartNo, ReactorType)
            If Error_Services('NoError') then
                Utilization = Epi_Part_Services('GetReactorUtilization', ReactorType)
                If Error_Services('NoError') then
                    IdealWafersPerDay   = Epi_Part_Services('GetIdealWafersPerDayScheduler', EpiPartNo, ReactorType, MinutesPerWafer)
                    WafersPerDay        = IdealWafersPerDay * (Utilization / 100)
                    WafersPerDay        = Oconv(Iconv(WafersPerDay, 'MD2'), 'MD2')
                    If ReactorType _EQC 'EpiPro' then WafersPerDay = WafersPerDay * 2
                    Memory_Services('SetValue', ServiceKeyID, WafersPerDay)
                end
            end
        end else
            Error_Services('Add', 'EpiPartNo or ReactorType argument was missing from the ' : Service : ' service.')
        end
    end
    
    Response    = WafersPerDay

end service


//----------------------------------------------------------------------------------------------------------------------
// GetEpiParts
//
// Input:  ReactType
//
// Output: A @FM delimited list of Epi Part keys.
//----------------------------------------------------------------------------------------------------------------------
Service GetEpiParts(ReactType=REACTORTYPES)
    
    ErrorMsg    = ''
    EpiPartList = ''
    If ReactType NE '' then
        If ReactType EQ 'EpiPro' then ReactType = 'EPP'
        EpiPartList = ''
        Query       = 'SELECT EPI_PART WITH REACT_TYPE EQ ':Quote(ReactType)
        RList(Query, TARGET_ACTIVELIST$, '', '', '')
        StatusCode  = ''
        If Not(Get_Status(StatusCode)) then
            EOF = False$
            Loop
                Readnext EpiPartKey else EOF = True$
            Until EOF
                EpiPartList<-1> = EpiPartKey
            Repeat
        end else
            ErrorMsg = 'Error in ':Service:' service. RList call failed. Error code: ':StatusCode:'.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null ReactType passed in!'
    end
    
    If ErrorMsg EQ '' then
        Response = EpiPartList
    end else
        Error_Services('Add', ErrorMsg)
    end

End Service

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