Function Wafer_Counter_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        :   Wafer_Counter_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)
        09/26/23    djs     Adapted from FlatFinder_Services.

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

#pragma precomp SRP_PreCompiler

$Insert LOGICAL
$Insert SERVICE_SETUP
$Insert RDS_EQUATES
$Insert WO_LOG_EQUATES
$Insert WM_OUT_EQUATES
$Insert RLIST_EQUATES
$Insert WAFER_COUNTER_EQUATES
$Insert TOOL_EQUATES
$Insert TOOL_LOG_EQUATES

Equ Tab$       to \09\
Equ CRLF$      to \0D0A\
Equ LF$        to \0A\
Equ Comma$     to ','
* Reduce Modes
Equ new_exist$ To 0 
Equ next_cur$  To 1
Equ add_exist$ to 2

Declare subroutine  Error_Services, Database_Services, Logging_Services, Httpclient_Services, Reduce, SRP_JSON
Declare subroutine Wafer_Counter_Services, Lot_Event_Services
Declare function    Database_Services, Environment_Services, Logging_Services, Httpclient_Services, OConv
Declare function    RTI_CreateGuid, SRP_JSON, WO_Mat_Services, WM_Out_Services, Wafer_Counter_Services, Datetime

LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\WaferCounter'
LogDate     = Oconv(Date(), 'D4/')
LogTime     = Oconv(Time(), 'MTS')
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Wafer Counter Import Log.csv'
Headers     = 'Logging DTM':@FM:'ToolID':@FM:'Operation':@FM:'Datetime':@FM:'LotID':@FM:'CassNo':@FM:'Wafer Count':@FM:'Import Result'
objLog      = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM

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 BOOLEAN     = True$, False$
Options WAFER_SIZES = '6INCH', '8INCH'
Options LOCATIONS   = 'MU', 'QA', 'EPR-EAST', 'EPR-WEST' 


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

//----------------------------------------------------------------------------------------------------------------------
// ImportWaferCounterFiles
//
// Looks for available wafer counter CSV files that are ready to be imported into the RDS table.
//----------------------------------------------------------------------------------------------------------------------
Service ImportWaferCounterFiles()
    
    hSysLists   = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID then
        DataPath    = Environment_Services('GetApplicationRootPath') : '\WaferCounter\'
        BackupPath  = Environment_Services('GetApplicationRootPath') : '\WaferCounter\BackupFiles\'
        InitDir DataPath : '*.csv'
        FileList    = DirList()
        For Each File in FileList using @FM
            OSRead WaferCounterData from DataPath : File then
                BackupDate      = Oconv(Date(), 'D4/')
                BackupTime      = Oconv(Time(), 'MTS')
                Convert ':' to '-' in BackupTime
                BackupFileName  = BackupDate[7, 4] : '-' : BackupDate[1, 2] : '-' : BackupDate[4, 2] : '-' : BackupTime : ' ' : File
                If Count(File, '-') LE 1 then
                    OSWrite WaferCounterData to BackupPath : BackupFileName
                end
                Swap CRLF$ with '' in WaferCounterData
                Swap ',' with @FM in WaferCounterData
                ToolID         = Trim(WaferCounterData<2>)
                Operation      = Trim(WaferCounterData<3>)
                Datetime       = IConv(Trim(WaferCounterData<4>), 'DT')
                ReferenceKeyID = Trim(WaferCounterData<5>)
                CassetteNo     = Trim(WaferCounterData<6>)
                WaferCount     = Trim(WaferCounterData<7>)
                
                Result     = False$
                LogData    = LoggingDTM
                LogData<2> = ToolID
                LogData<3> = Operation
                LogData<4> = Trim(WaferCounterData<4>)
                LogData<5> = ReferenceKeyID
                LogData<6> = CassetteNo
                LogData<7> = WaferCount
                
                If ReferenceKeyID[1, 2] EQ '1T' or ReferenceKeyID[1, 1] EQ 'O' then
                    Convert 'O' to '' in ReferenceKeyID
                    Swap '1T' with '' in ReferenceKeyID                      
                    If Index(ReferenceKeyID, '.', 1) then
                        // A dot means this is a WorkOrderNo.Cassette value. Need to create a WM_OUT Key ID.
                        WorkOrderNo = ReferenceKeyID[1, '.']
                        WOStepKeyID = ReferenceKeyID[Col2() + 1, '.']
                        CassetteNo  = ReferenceKeyID[Col2() + 1, '.']
                        WMOutKeyID  = WorkOrderNo : '*' : WOStepKeyID : '*' : CassetteNo
                        WMOutRow    = Database_Services('ReadDataRow', 'WM_OUT', WMOutKeyID)
                        If Error_Services('NoError') then
                            WMOutRow<WM_OUT_WAFER_COUNTER_DTM$> = DateTime
                            WMOutRow<WM_OUT_WAFER_COUNTER_QTY$> = WaferCount
                            Database_Services('WriteDataRow', 'WM_OUT', WMOutKeyID, WMOutRow, True$, False$, True$)
                        end
                    end else
                        // No dot in the reference key means this is an RDS Key ID.
                        Transfer ReferenceKeyID to RDSKeyID
                        If Num(RDSKeyID) then RDSKeyID = RDSKeyID + 0
                        RDSRow      = Database_Services('ReadDataRow', 'RDS', RDSKeyID)
                        If Error_Services('NoError') then
                            RDSRow<RDS_WAFER_COUNTER_DTM$> = DateTime
                            RDSRow<RDS_WAFER_COUNTER_QTY$> = WaferCount
                            Database_Services('WriteDataRow', 'RDS', RDSKeyID, RDSRow, True$, False$, True$)
                        end
                    end
                    If Error_Services('NoError') then
                        Result = 'Success'
                    end else
                        Result = 'Failure: ':Error_Services('GetMessage')
                    end
                end else
                    // Throw error - either invalid barcode scanned or user manually entered data
                    Result = 'Failure: ReferenceKeyID "':ReferenceKeyID:'" is invalid.'
                end
                
                LogData<8> = Result
                Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
                
                OSDelete DataPath : File
            end
        Next File
        
        Unlock hSysLists, ServiceKeyID else Null
    end
    
end service


Service ConvertWaferData(FileData)
    
    ErrorMsg   = ''
    WaferArray = ''
    If FileData NE '' then
        Swap CRLF$ with @FM in FileData
        WaferTotal = FileData<1>
        WaferData  = FileData<2>
        If ( (Len(WaferTotal) EQ 3) and (WaferTotal[1, 1] EQ 'T') and (Len(WaferData) EQ 8 ) and (WaferData[1, 1] EQ 'P') )  then
            Convert 'T' to '' in WaferTotal
            WaferArray = ''
            For CharIndex = 2 to 8
                Val         = IConv(WaferData[CharIndex, 1], 'MX')
                Val         = OConv(Val, 'MB')
                If CharIndex GT 2 then Val = Fmt(Val, 'R(0)#4')
                WaferArray := Val
            Next CharIndex
            
            If (Len(WaferArray) NE 25) then
                ErrorMsg = 'Error in ':Service:' service. Invalid wafer array length.'
            end else
                // Reverse the array so it is in order from slot 1 to 25
                WaferArray = WaferArray[-1, -25]
                // Return @VM delimited array for ease of use in OI BASIC+
                DelimArray = ''
                For CharIndex = 1 to 25
                    DelimArray<0, CharIndex> = WaferArray[CharIndex, 1]
                Next CharIndex
                If (Sum(DelimArray) EQ WaferTotal) then
                    Response = DelimArray
                end else
                    ErrorMsg = 'Error in ':Service:' service. Wafer total does not match array total.'
                end
            end
            
        end else
            ErrorMsg = 'Error in ':Service:' service. Invalid data format passed in FileData variable.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null FileData passed into service.'
    end
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service GetWaferCounterJSON(WaferSize, ToolLocation, CassID)

    If ( (WaferSize NE '') and (ToolLocation NE '') and (CassID NE '') ) then 
        URL      = 'https://oi-metrology-viewer-prod.mes.infineon.com:4438/api/v1/WaferCounter/{waferSize}/last-quantity-and-slot-map/?area={area}&text={cassID}'
        Swap '{waferSize}' with WaferSize in URL
        Swap '{area}' with ToolLocation in URL
        Swap '{cassID}' with CassID in URL
        // Set timeout to 24 seconds. This value was decided upon with Mike Phares to allow time
        // for EAF to receive the new wafer counter file from the tool and serve it up.
        Httpclient_Services('SetTimeoutDuration', 24) 
        Response = Httpclient_Services('SendHTTPRequest', 'GET', URL, '', '', '', '', False$, False$, '')
        If Error_Services('NoError') then
            StatusCode = Httpclient_Services('GetResponseStatusCode')
            If StatusCode NE 200 then
				Begin Case
					Case Response EQ ''
						ErrorMsg = 'Unexpected server error.'
					Case Response EQ 'No files!'
						ErrorMsg = 'No data. Please re-seat cassette and try again.'
					Case Otherwise$
						// Use message in response body to populate error services
						ErrorMsg = Response
				End Case
                Error_Services('Add', ErrorMsg)
            end
        end
    end
    
end service


Service AddScan(LotID, ScanQty, ScanDtm, ScanTool, ScanUser, ScanLocation, WaferMap)

    KeyID                                = RTI_CreateGuid()
    Record                               = ''
    Record<WAFER_COUNTER.LOT_ID$>        = LotID
    Record<WAFER_COUNTER.SCAN_QTY$>      = ScanQty
    If Not(Num(ScanDtm)) then
        ScanYear  = ScanDtm[1, 'F-']
        ScanMonth = ScanDtm[Col2() + 1, 'F-']
        ScanDay   = ScanDtm[Col2() + 1, 'F ']
        ScanTime  = ScanDtm[Col2() + 1, 999]
        Convert ' ' to '' in ScanTime
        ScanDtm   = ScanMonth:'/':ScanDay:'/':ScanYear:' ':ScanTime
        ScanDtm   = IConv(ScanDtm, 'DT')
    end
    Record<WAFER_COUNTER.SCAN_DTM$>       = ScanDtm
    Record<WAFER_COUNTER.SCAN_TOOL$>      = ScanTool
    Record<WAFER_COUNTER.SCAN_USER$>      = ScanUser
    If ScanLocation EQ 'FQA' then ScanLocation = 'QA'
    Record<WAFER_COUNTER.SCAN_LOCATION$>  = ScanLocation
    Record<WAFER_COUNTER.SCAN_WAFER_MAP$> = WaferMap
    Database_Services('WriteDataRow', 'WAFER_COUNTER', KeyID, Record)
    
    Response = KeyId
    
end service


Service GetLastScan(LotID, ScanLocation=LOCATIONS)
    
    ErrorMsg = ''
    WCRec    = ''
    If LotID NE '' then 
        Tablename = "WAFER_COUNTER"
        Flag      = ""
        Done      = False$
        CursorVar = ""
        
        GoSub ClearCursors
        
        SortList     = "#SCAN_DTM"
        ReduceScript = 'WITH {LOT_ID} EQ ':Quote(LotID)
        If ScanLocation NE '' then
            ReduceScript := ' AND WITH {SCAN_LOCATION} EQ ':Quote(ScanLocation)
        end
        Mode         = NEXT_CUR$
        
        Reduce(ReduceScript, SortList, Mode, Tablename, Cursorvar, Flag)
        If Flag then
            Select Tablename By SortList Using Cursorvar then
                Open Tablename To hTable then
                    KeyID = ''
                    Loop
                        ReadNext KeyID Using Cursorvar By AT Else Done = TRUE$
                        If KeyID NE '' then
                            Done = True$
                            Read WCRec from hTable, KeyID then
                                WCRec<WAFER_COUNTER.SCAN_DTM$> = OConv(WCRec<WAFER_COUNTER.SCAN_DTM$>, 'DT/^2H')
                            end else
                                ErrorMsg = 'Error in ':Service:' service. Error reading ':KeyID:' from ':Tablename:' table.'
                            end
                        end
                    Until Done or (KeyID NE '')
                    Repeat
                End Else
                    ErrorMsg = 'Error in ':Service:' service. Error in opening ':Tablename:'.'
                End
            End Else
                ErrorMsg = 'Error in ':Service:' service. Error in selecting ':Tablename:'.'
            end
        End Else
            ErrorMsg = 'Error in ':Service:' service. Error in Reduce call.'
        End
    end else
        ErrorMsg = 'Error in ':Service:' service. Null LotID passed into service.'
    end
    
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end 
    
    Response = WCRec
    
end service


Service GetProdWaferCounter(WaferSize=WAFER_SIZES, Location=LOCATIONS)
    
    ToolID   = ''
    ErrorMsg = ''
    If ( (WaferSize NE '') and (Location NE '') ) then
        Query   = "SELECT TOOL WITH @ID CONTAINING 'WC":WaferSize:"' AND WITH TOOL_LOC EQ '":Location:"' AND WITH CURR_MODE EQ 'PROD'"
        GoSub ClearCursors
        RList(Query, TARGET_ACTIVELIST$, '', '', '')
        ErrCode = ''
        If Not(Get_Status(ErrCode)) then
            Begin Case
                Case @RecCount EQ 0
                    ErrorMsg = 'Error in ':Service:' service. No production wafer counter found!'
                Case @RecCount EQ 1
                    ReadNext ToolID else
                        ErrorMsg = 'Error in ':Service:' service. Error reading production wafer counter tool ID!'
                    end
                Case @RecCount GT 1
                    ErrorMsg = 'Error in ':Service:' service. More than one production wafer counter found!'
            End Case
            GoSub ClearCursors
        end else
            ErrorMsg = 'Error in ':Service:' service. Error calling RList. Error code: ':ErrCode:'.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WaferSize or Location passed into service.'
    end
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    Response = ToolID
    
end service


Service GetWaferCounterToolID(WaferSize=WAFER_SIZES, Location=LOCATIONS)
    
    ToolID   = ''
    ErrorMsg = ''
    If ( (WaferSize NE '') and (Location NE '') ) then
        Query   = "SELECT TOOL WITH @ID CONTAINING 'WC":WaferSize:"' AND WITH TOOL_LOC EQ '":Location:"'"
        GoSub ClearCursors
        RList(Query, TARGET_ACTIVELIST$, '', '', '')
        ErrCode = ''
        If Not(Get_Status(ErrCode)) then
            Begin Case
                Case @RecCount EQ 0
                    ErrorMsg = 'Error in ':Service:' service. No wafer counter tool found for ':WaferSize:' ':Location:'.'
                Case @RecCount EQ 1
                    ReadNext ToolID else
                        ErrorMsg = 'Error in ':Service:' service. Error reading wafer counter tool ID!'
                    end
                Case @RecCount GT 1
                    ErrorMsg = 'Error in ':Service:' service. More than one wafer counter tool found for ':WaferSize:' ':Location:'.'
            End Case
            GoSub ClearCursors
        end else
            ErrorMsg = 'Error in ':Service:' service. Error calling RList. Error code: ':ErrCode:'.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WaferSize or Location passed into service.'
    end
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    Response = ToolID
    
end service

//----------------------------------------------------------------------------------------------------------------------
// ProcessCass2DBarcode
//
// Takes in the raw barcode data for a 2D Cassette label. Returns a single field array with 3 field delimited by @VM
// CassId, Expected Qty, and Cass Type
//----------------------------------------------------------------------------------------------------------------------
Service ProcessCass2DBarcode(BarcodeText)
    
    ErrorMessage = ''
    ExpectedSlotWaferMap = ''
    ExpectedQty = ''
    CassId = ''
    CassType = ''
    If BarcodeText NE '' then
        DelimCnt      = DCount(BarcodeText, '|')
        If DelimCnt EQ 8 then
            CassId = Field(BarcodeText, '|', 3)
            If CassId NE '' then
                If CassId[1, 2] EQ '1T' then
                    CassId[1, 2] = ''
                    Begin Case
                        Case CassId[1, 1] EQ 'O'
                            CassId[1, 1] = ''
                            CassType = 'WM_OUT'
                        Case (CassId[1, 1] EQ 'I')
                            CassId[1, 1] = ''
                            CassType = 'WM_IN'
                        Case Otherwise$
                            CassType = 'RDS'
                    End Case
                    Convert '.' to '*' in CassId
                    Begin Case
                        Case RowExists('RDS', CassId)
                            ExpectedQty = Xlate('RDS', CassId, 'WFRS_OUT', 'X')
                        Case RowExists('WM_OUT', CassId)
                            ExpectedQty = Xlate('WM_OUT', CassId, 'WAFER_CNT', 'X')
                    End Case
                end                    
                
            end else
                ErrorMessage = 'Invalid Lot Label Scan. Unable to determine Cass Id'
            end
        end else
            ErrorMessage = 'Invalid Lot Label Scan.'
        end
    end else
        ErrorMessage = 'Scan data was null.'
    end
    
    If ErrorMessage EQ '' then
       Response = CassId : @VM: ExpectedQty : @VM : CassType
    end else
       Error_Services('Add', ErrorMessage) 
    end
    
end service

//----------------------------------------------------------------------------------------------------------------------
// ProcessTool2DBarcode
//
// Takes in the raw barcode data for a wafer counter label, Cassette Id, Cassette Type, and UserId(Optional)
// Returns a key ID for a completed WAFER_COUNTER record.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessTool2DBarcode(BarcodeText, CassId, CassType, UserId)
    
    ErrorMessage = ''

    If BarcodeText NE '' then
        DelimCnt      = DCount(BarcodeText, '|')
        If DelimCnt EQ 2 then
            WaferSize   = Field(BarcodeText, '|', 1)
            Area        = Field(BarcodeText, '|', 2)
            If Environment_Services('IsProd') then
               WcJson      = Wafer_Counter_Services('GetWaferCounterJSON', WaferSize, Area, CassID) 
            end else
               WcJson      = Wafer_Counter_Services('GetWaferCounterJSONTestData', WaferSize, Area, CassID) 
            end
            
            If Error_Services('NoError') then
                objJSON = ''
                If SRP_JSON(objJSON, 'Parse', WcJson) EQ '' then
                    ScanDtm          = SRP_JSON(objJSON, 'GetValue', 'dateTimeFormatted')
                    ToolID           = SRP_JSON(objJSON, 'GetValue', 'equipmentId')
                    WaferCount       = SRP_JSON(objJSON, 'GetValue', 'total')
                    SlotMap          = SRP_JSON(objJSON, 'GetValue', 'slotMap')
                    SRP_JSON(objJSON, 'Release')
                    ToolCurrModeKey = Database_Services('ReadDataColumn', 'TOOL', ToolId, TOOL_CURR_MODE_KEY$, True$, 0, False$)
                    ToolCurrMode = Database_Services('ReadDataColumn', 'TOOL_LOG', ToolCurrModeKey, TOOL_LOG_TOOL_MODE$, True$, 0, False$)
                    If ToolCurrMode EQ 'PROD' OR ToolCurrMode EQ 'LIM' then
                        WaferCounterId = Wafer_Counter_Services('AddScan', CassID, WaferCount, ScanDtm, ToolID, UserId, Area, SlotMap)
                        If Error_Services('NoError') then
                            Lot_Event_Services('CreateLotEvent', CassId, ScanDtm, 'WAFER_COUNTER', 'Wafer counter data received for lot. ' : WaferCount : ' wafers found.', ToolId, UserId, True$, CassType)
                        end
                    end else
                        ErrorMessage = 'Wafer counter is currently down and cannot be used to log wafer counts.'
                    end
                end else
                    ErrorMessage = 'Error parsing response from wafer counter.'
                end
            end else
                ErrorMessage = Error_Services('GetMessage')
            end
        end else
            ErrorMessage = 'Invalid tool scan.'    
        end
    end else
       ErrorMessage = 'Scan data was null.' 
    end
    
    If ErrorMessage EQ '' then
        Response = WaferCounterId
    end else
       Error_Services('Add', ErrorMessage) 
    end
    
end service

//----------------------------------------------------------------------------------------------------------------------
// GetWaferCounterJSONTestData
//
// Emulates GetWaferCounterJson service. 
//----------------------------------------------------------------------------------------------------------------------
Service GetWaferCounterJSONTestData(WaferSize, ToolLocation, CassID)

    If ( (WaferSize NE '') and (ToolLocation NE '') and (CassID NE '') ) then 
        Response = ''
        objJson = ''
        If SRP_JSON(objJson, 'New', 'Object') then
            SRP_JSON(objJson, 'SetValue', 'dateTimeFormatted', Datetime(), 'String')
            SRP_JSON(objJson, 'SetValue', 'equipmentId', 'WC8INCH1', 'String')
            SRP_JSON(objJson, 'SetValue', 'total', 24, 'Number')
            SRP_JSON(objJson, 'SetValue', 'slotMap', '1111111110111111111111111', 'String') 
            Response = SRP_Json(objJson, 'Stringify', 'Styled')
            SRP_JSON(objJSON, 'Release')
        end
        
        If Error_Services('NoError') then
            StatusCode = 200
            If StatusCode NE 200 then
				Begin Case
					Case Response EQ ''
						ErrorMsg = 'Unexpected server error.'
					Case Response EQ 'No files!'
						ErrorMsg = 'No data. Please re-seat cassette and try again.'
					Case Otherwise$
						// Use message in response body to populate error services
						ErrorMsg = Response
				End Case
                Error_Services('Add', ErrorMsg)
            end
        end
    end
    
end service

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

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


