Compile function NCR_Services(@Service, @Params)
#pragma precomp SRP_PreCompiler
/***********************************************************************************************************************

    Name        :   NCR_Services

    Description :   Handler program for all NCR services.

    Notes       :   Application errors should be logged using the Error Services module. There are a few methodological
                    assumptions built into way errors are managed which are important to understand in order to properly
                    work with Error Services:

                    - The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to
                      the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger
                      this will appear backwards since the originating procedure always appears at the bottom of the
                      list and the current routine appears at the top of the list. We are using this orientation because
                      it is common to refer to the process of calling other procedures as 'drilling down'.

                    - The reason for defining the orientation of the call stack is because Error_Services allows for
                      multiple error conditions to be appended to an original error. In most cases this will happen when
                      a procedure at the bottom of the stack generates an error condition and then returns to its
                      calling procedure. This higher level procedure can optionally add more information relevant to
                      itself. This continues as the call stack 'bubbles' its way back to the top to where the
                      originating procedure is waiting.

                    - Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus)
                      preserve their error state until explicitly cleared. This can hinder the normal execution of code
                      since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists.
                      Our philosophy is that error conditions should automatically be cleared before a new procedure
                      is executed to avoid this problem. However, the nature of Basic+ does not make this easy to
                      automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our
                      philosophy then it should include a call into the 'Clear' service request at the top of the
                      program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.)

                    - Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any
                      error conditions that were set before.

    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)
    08/29/24	djm		Add ProcessScanData service and Service header.

***********************************************************************************************************************/
$insert APP_INSERTS
$Insert SERVICE_SETUP
$Insert NCR_EQUATES
$Insert REACTOR_EQUATES

Declare subroutine Set_Status, Logging_Services, Error_Services, Obj_Notes, Set_Property, Obj_Wo_Mat, Hold_Services
Declare subroutine Btree.Extract, SRP_JSon
Declare function   Environment_Services, Logging_Services, SRP_Datetime, Utility, Scan_Services, Error_Services
Declare function   Database_Services, Signature_Services, Epipro_Services, Rds_Services, Memberof, Datetime
Declare function   Get_Property, SRP_Json, obj_ncr

EQU COL$RDS_NO		TO 1
EQU COL$REACTOR		TO 2
EQU COL$REJ_CNT		TO 3

GoToService

Return Response or ""

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options SCAN_TYPES        = 'CASSETTE_1', 'CASSETTE_2', 'WM_IN_1', 'WM_IN_2'


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


//----------------------------------------------------------------------------------------------------------------------
// ProcessScanData
//
// ScanData - [Required]
// ScanType - [Required]
// FirstCassID - [Required]
// OperatorID - [Required]
//
// Returns the current stage of a lot. Replaces CURR_STAGE calculated column.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessScanData(ScanData, ScanType = SCAN_TYPES, FirstCassID, OperatorID)

    If (ScanData NE '') then
    	Convert @Lower.Case to @Upper.Case in ScanData
        // Code 3of9 encodes the asterisk and underscore characters. These need to be decoded.
        ScanData    = Scan_Services('DecodeScanData', ScanData)
        
        If Error_Services('NoError') then
            
            ColumnIndex = ''
            ColumnValue = ''
            // Identify the scan data based on the data identifier prefix (if any). Otherwise, assume this is a
            // type of cassette ID (i.e., RDS or WMO).
            Begin Case
                
                Case ScanType EQ 'WM_IN_1'
                    ValidCassID   = False$
                    cnt = Count(ScanData, '|')
                    if cnt NE 0 then
                        Error_Services('Add', 'Wrong label - Scan 2D Top Label')
                        return
                    end else
                        CassetteID = ScanData
                        Swap 'FDI' with '' in CassetteID
                        Swap '^FS' with '' in CassetteID
                        Swap 'I'   with '' in CassetteID
                        Convert '.' to '*' in CassetteID
                        WMIRow  = Database_Services('ReadDataRow', 'WM_IN', CassetteID)
                        If Error_Services('NoError') then
                            ValidCassID   = True$
                        end
                        ScanData = CassetteID
                        If ScanData NE FirstCassID then
                            Error_Services('Add', 'Lot Label does not match the expected lot in OI. | Please correct this issue and then retry.')
                        end
                    end
                    
                    If ValidCassID EQ False$ then
                        Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                    end 
                    
                    Response = ScanData
                    
                Case ScanType EQ 'WM_IN_2'
                    ValidCassID   = False$
                    ScanMismatch = False$
                    cnt = Count(ScanData, '|')
                    if cnt NE 0 then
                        Error_Services('Add', 'Wrong label - Scan 2D Bottom Label')
                        return
                    end else
                        CassetteID = ScanData
                        Swap 'FDI' with '' in CassetteID
                        Swap '^FS' with '' in CassetteID
                        Swap 'I'   with '' in CassetteID
                        Convert '.' to '*' in CassetteID
                        WMIRow  = Database_Services('ReadDataRow', 'WM_IN', CassetteID)
                        If Error_Services('NoError') then
                            ValidCassID   = True$
                        end
                        ScanData = CassetteID
                        If ScanData NE FirstCassID then
                            ScanMismatch = True$
                        end
                    end
                    
                    If ScanMismatch EQ True$ then
                        ValidCassID = False$
                        If (RowExists('WM_IN', FirstCassID) EQ True$) And (RowExists('WM_IN', CassetteID) EQ True$) then
                            // Place first cassette on hold
                            WONo         = Field(FirstCassID, '*', 1, 1)
                            CassNo       = Field(FirstCassID, '*', 3, 1)
                            HoldEntity   = 'WM_IN'
                            HoldEntityID = FirstCassID 
                            
                            WOMatKey    = WONo:'*':CassNo
                            CtrlEntID   = 'NCR_SERVICES'	;* Control checked/unchecked
                            OriginFlag	= ''       ;* Flag to indicate a hold initiated from the packagaing form
                            HoldData = ''
                            HoldData<1> = OperatorID
                            HoldData<2> = 'Lot placed on hold due to NCR Barcode verification mismatch.'
                            HoldData<3> = False$
                            Hold_Services('OnHold', WOMatKey, HoldEntity, HoldEntityID, 'HOLD', HoldData, OperatorID)
                            
                            // Place Second cassette on hold
                            WONo         = Field(CassetteID, '*', 1, 1)
                            CassNo       = Field(CassetteID, '*', 3, 1)
                            HoldEntity   = 'WM_IN'
                            HoldEntityID = CassetteID 
                            
                            WOMatKey    = WONo:'*':CassNo
                            CtrlEntID   = 'NCR_SERVICES'	;* Control checked/unchecked
                            OriginFlag	= ''       ;* Flag to indicate a hold initiated from the packagaing form
                            HoldData = ''
                            HoldData<1> = OperatorID
                            HoldData<2> = 'Lot placed on hold due to NCR Barcode verification mismatch.'
                            HoldData<3> = False$
                            Hold_Services('OnHold', WOMatKey, HoldEntity, HoldEntityID, 'HOLD', HoldData, OperatorID)
                            Message		= 'NCR Label verification failed due to mismatched barcodes. Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.':CRLF$ |
                            : 'WM_IN Label #1: ':FirstCassID:CRLF$ | 
                            : 'WM_IN Label #2: ':CassetteID:CRLF$ |
                            : 'Operator: ':OperatorID
                            Error_Services('Add', Message)
                        end else
                            Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                        end
                    end
                    
                    If ValidCassID EQ False$ then
                        Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                    end 
                    
                    Response = ScanData
                    
                Case ScanType EQ 'CASSETTE_1'
                    
                    // Assume this is intended to be a Cassette ID scan (either WMO or RDS). Only if this is a
                    // non-existent carrier will the scan data be considered invalid.
                    
                    cnt = DCount(ScanData, '|')
                    if cnt NE 8 then
                        CassetteID = 0
                        Error_Services('Add', 'Wrong label - Scan 2D Top Label')
                        return
                    end else
                        // RDS should be 3rd position
                        CassetteID = Field(ScanData, '|', 3)
                        Seq1 = Field(ScanData, '|', 8)
                    end
                    
                    
                    // Strip '1T', 'I', and 'O' prefixes.
                    If CassetteID[1, 2] EQ '1T' then
                        CassetteID[1, 2] = ''
                        If ((CassetteID[1, 1] EQ 'O') or (CassetteID[1, 1] EQ 'I')) then CassetteID[1, 1] = ''
                    end                    
                    If ( (CassetteID[1, 1] EQ 'O') or (CassetteID[1, 1] EQ 'I') ) then CassetteID[1, 1] = ''
                    
                    ValidCassID = False$  ; // Assume Cassette ID is not valid for now.
                    RDSCass	= Count(CassetteID, '.') EQ 0 AND Num(CassetteID)
                    GaNCass	= Count(CassetteID, '.') EQ 1 AND not(RDSCass)
                    EPPCass	= Count(CassetteID, '.') EQ 2 AND not(RDSCass) AND not(GaNCass)
                    
                    Begin Case
                        Case GaNCass
                            Convert '.' to '*' in CassetteID
                            WOMatRow    = Database_Services('ReadDataRow', 'WO_MAT', CassetteID)
                            If Error_Services('NoError') then 
                                ValidCassID   = True$
                            end
                        Case EPPCass
                            Convert '.' to '*' in CassetteID
                            WMOutRow    = Database_Services('ReadDataRow', 'WM_OUT', CassetteID)
                            If Error_Services('NoError') then
                                ValidCassID   = True$
                            end
                        Case RDSCass
                            RDSRow  = Database_Services('ReadDataRow', 'RDS', CassetteID)
                            If Error_Services('NoError') then
                                ValidCassID   = True$
                            end 
                    End Case
                    
                    If ( (Seq1 EQ 'SEQ1') or (Seq1 EQ 'SEQ2') ) then
                    	Set_Property(@Window, '@SEQ1', Seq1)
                    end else
                    	Error_Services('Add', 'Error reading sequence information')
                    end
                    
                    If ValidCassID EQ False$ then
                        Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                    end 
                    
                    ScanData = CassetteID
                    If ScanData NE FirstCassID then
                        Error_Services('Add', 'Lot Label does not match the expected lot in OI. | Please correct this issue and then retry.')
                    end
                    
                    Response = ScanData
                    
                Case ScanType EQ 'CASSETTE_2'
                    
                    // Assume this is intended to be a Cassette ID scan (either WMO or RDS). Only if this is a
                    // non-existent carrier will the scan data be considered invalid.
                    cnt = DCount(ScanData, '|')
                    if cnt NE 8 then
                        CassetteID = 0
                        Error_Services('Add', 'Wrong label - Scan 2D Bottom Label')
                        return
                    end else
                        // RDS should be 3rd position
                        SecondCassID = Field(ScanData, '|', 3)
                        Seq2 = Field(ScanData, '|', 8)
                    end
                    
                    // Strip '1T', 'I', and 'O' prefixes.
                    If SecondCassID[1, 2] EQ '1T' then
                        SecondCassID[1, 2] = ''
                        If ( (SecondCassID[1, 1] EQ 'O') or (SecondCassID[1, 1] EQ 'I') ) then SecondCassID[1, 1] = ''
                    end                    
                    If ( (SecondCassID[1, 1] EQ 'O') or (SecondCassID[1, 1] EQ 'I') ) then SecondCassID[1, 1] = ''
                    
                    ValidCassID  = False$  ; // Assume Cassette ID is not valid for now.
                    ScanMismatch = False$   
                    SeqMismatch  = False$
                    SeqError     = False$
                    Convert '*' to '.' in FirstCassID
                    
                    If Error_Services('NoError') then
                        ValidCassID = True$
                        If (SecondCassID NE FirstCassID) then
                            ScanMismatch = True$
                        end else
                        	Seq1 = Get_Property(@Window, '@SEQ1')
                        	Begin Case
                        		Case ( (Seq2 NE 'SEQ1') and (Seq2 NE 'SEQ2') )
                        			SeqError = True$
                        		Case (Seq2 EQ Seq1)
                        			SeqMismatch = True$
                        	End Case
                        end
                    end
                    
                    If ValidCassID EQ False$ then Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                    
                    If (ScanMismatch EQ True$) AND (ValidCassID EQ True$) then
                        // Check if first cassette ID is a valid RDS or WM_OUT key
                        Convert '.' to '*' in FirstCassID
                        ValidCassID = False$
                        Begin Case
                            Case ( RowExists('RDS', FirstCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Xlate('RDS', FirstCassID, 'WO', 'X')
                                CassNo       = Xlate('RDS', FirstCassID, 'CASS_NO', 'X')
                                HoldEntity   = 'RDS'                             
                            Case ( RowExists('WM_OUT', FirstCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Field(FirstCassID, '*', 1, 1)
                                CassNo       = Field(FirstCassID, '*', 3, 1)
                                HoldEntity   = 'WM_OUT'
                            Case ( RowExists('WM_IN', FirstCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Field(FirstCassID, '*', 1, 1)
                                CassNo       = Field(FirstCassID, '*', 3, 1)
                                HoldEntity   = 'WM_IN'
                            Case ( RowExists('WO_MAT', FirstCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Field(FirstCassID, '*', 1, 1)
                                CassNo       = Field(FirstCassID, '*', 2, 1)
                                HoldEntity   = 'WO_MAT'                    
                            Case Otherwise$
                                Null
                        End Case
                        HoldEntityID = FirstCassID 
                        // Place first cassette on hold
                        WOMatKey    = WONo:'*':CassNo
                        CtrlEntID   = 'NCR_SERVICES'	;* Control checked/unchecked
                        OriginFlag	= ''       ;* Flag to indicate a hold initiated from the packagaing form
                        HoldData = ''
                        HoldData<1> = OperatorID
                        HoldData<2> = 'Lot placed on hold due to NCR Barcode verification mismatch.'
                        HoldData<3> = False$
                        Hold_Services('OnHold', WOMatKey, HoldEntity, HoldEntityID, 'HOLD', HoldData, OperatorID)
                        
                        // Check if second cassette ID is a valid RDS or WM_OUT key
                        Convert '.' to '*' in SecondCassID                           
                        ValidCassID = False$
                        Begin Case
                            Case ( RowExists('RDS', SecondCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Xlate('RDS', SecondCassID, 'WO', 'X')
                                CassNo       = Xlate('RDS', SecondCassID, 'CASS_NO', 'X')
                                HoldEntity   = 'RDS'
                                ValidCassID = True$
                            Case ( RowExists('WM_OUT', SecondCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Field(SecondCassID, '*', 1, 1)
                                CassNo       = Field(SecondCassID, '*', 3, 1)
                                HoldEntity   = 'WM_OUT'
                            Case ( RowExists('WM_IN', SecondCassID) EQ True$ )
                                WONo         = Field(SecondCassID, '*', 1, 1)
                                CassNo       = Field(SecondCassID, '*', 3, 1)
                                HoldEntity   = 'WM_IN'
                                ValidCassID = True$
                            Case ( RowExists('WO_MAT', SecondCassID) EQ True$ )
                                WONo         = Field(SecondCassID, '*', 1, 1)
                                CassNo       = Field(SecondCassID, '*', 2, 1)
                                HoldEntity   = 'WO_MAT'
                                ValidCassID = True$
                            Case Otherwise$
                                Null
                                
                        End Case
                        If ValidCassID EQ True$ then  
                            HoldEntityID = SecondCassID
                            WOMatKey    = WONo:'*':CassNo
                            // Place second cassette on hold
                            CtrlEntID   = 'NCR_SERVICES'	;* Control checked/unchecked
                            OriginFlag	= ''       ;* Flag to indicate a hold initiated from the packagaing form
                            HoldData = ''
                            HoldData<1> = OperatorID
                            HoldData<2> = 'Lot placed on hold due to NCR Barcode verification mismatch.'
                            HoldData<3> = False$
                            Hold_Services('OnHold', WOMatKey, HoldEntity, HoldEntityID, 'HOLD', HoldData, OperatorID)
                        end
                        Message		= 'NCR Label verification failed due to mismatched barcodes. Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.':CRLF$ |
                        : 'Cassette Label #1: ':FirstCassID:CRLF$ | 
                        : 'Cassette Label #2: ':SecondCassID:CRLF$ |
                        : 'Operator: ':OperatorID
                        Error_Services('Add', Message)
                    end else if (SeqMismatch EQ True$) then
                        //do sequence mismatch stuff here
                        Message = 'NCR Label verification failed due to operator double-scanning barcode.':CRLF$ |
                        : 'Cassette: ':FirstCassID:CRLF$ | 
                        : 'Operator: ':OperatorID
                        Error_Services('Add', Message)
                    end else if (SeqError EQ True$) then
                    	Error_Services('Add', 'Error reading sequence information')
                    end
                    
                    ScanData = ''
                    ScanData = SecondCassID
                    Response = ScanData
                    
            End Case
            
        end
    end else
        Error_Services('Add', 'ScanData argument was missing in the ' : Service : ' service.')
    end
    
end service


Service SendToSPC(NCRNo, RDSList, ReactorType, PSNo, DeptResp, LossCode, LossDesc, LossStage, LossBy, ACCode, ACDesc)
	
    Headers = 'Logging DTM' : @FM : 'Machine' : @FM : 'Level' : @FM : 'Log'
    ColumnWidths = 20 : @FM : 12 : @FM : 7 : @FM : 500
    Machine         = Environment_Services('GetServer')
    LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\NCR'
    makeDirSuccess = Utility("MAKEDIR", LogPath)
    LogDate = Oconv(Date(), 'D4/')
    LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : '_NcrSendToSpc.csv'
    objLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ' ', Headers, ColumnWidths, False$, False$)
    
    RdsNos = ''
    RdsCount = 0
    ReactorList = ''
    ReactorCount = 0
    RejCntList = ''
    RejCntCount = 0
    
    RDSCnt = DCOUNT(RDSList<1>,@VM)
    If RDSCnt GT 0 then
        FOR I = 1 TO RDSCnt
            If RdsNos NE '' then
                RdsNos := ', '
            end
            RdsNos := RDSList<COL$RDS_NO, I>
            RdsCount += Num(RDSList<COL$RDS_NO, I>)
            If ReactorList NE '' then
                ReactorList := ', '
            end
            ReactorList := RDSList<COL$REACTOR, I>
            ReactorCount += Num(RDSList<COL$REACTOR, I>)
            If RejCntList NE '' then
                RejCntList := ', '
            end
            RejCntList := RDSList<COL$REJ_CNT, I>
            RejCntCount += Num(RDSList<COL$REJ_CNT, I>)
        Next I
    end
    
    LogMessage = 'Attempting to send NCR data to SPC. Data: {'
    LogMessage := 'NCR No: ' : NCRNo
    LogMessage := ', RDS Nos: ' : RdsNos
    LogMessage := ', Reactors: ' : ReactorList
    LogMessage := ', Reject Counts: ' : RejCntList
    LogMessage := ', Reactor Type: ' : ReactorType
    LogMessage := ', PSN: ' : PSNo
    LogMessage := ', Responsible Dept: ' : DeptResp
    LogMessage := ', Loss Code: ' : LossCode
    LogMessage := ', Loss Desc: ' : LossDesc
    LogMessage := ', Loss Stage: ' : LossStage
    LogMessage := ', Loss By: ' : LossBy
    LogMessage := ', AC Code: ' : ACCode
    LogMessage := ', AC Desc: ' : ACDesc
    LogMessage := '}'
    
    LogDate = Oconv(Date(), 'D4/')
    LogData     = ''
    LogData<1>  = Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTS') ; // Logging DTM
    LogData<2>  = Machine
    LogData<3> = 'INFO'
    LogData<4>  = LogMessage
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)
    
    ArgsNotNull = NCRNo NE '' and RDSList NE '' and ReactorType NE '' and PSNo NE ''
    ArgsNotNull = ArgsNotNull EQ True$ and DeptResp NE '' and LossCode NE ''
    ArgsNotNull = ArgsNotNull EQ True$ and LossDesc NE '' and LossStage NE ''
    ArgsNotNull = ArgsNotNull EQ True$ and LossBy NE '' and ACCode NE '' and ACDesc NE ''
    
    RdsListCountsAtLeastOne = RdsCount GE 1 and ReactorCount GE 1 and RejCntCount GE 1
    RdsListCountsEqual = RdsCount EQ ReactorCount and ReactorCount EQ RejCntCount
    
    If ArgsNotNull EQ True$ and RdsListCountsAtLeastOne NE False$ and RdsListCountsEqual NE False$ then
        DataLines = ''
        
        FOR I = 1 TO RDSCnt
            
            DataLine  = QUOTE(NCRNo):TAB$
            DataLine := QUOTE(RDSList<COL$RDS_NO, I>):TAB$
            
            IF ReactorType = 'P' OR ReactorType = 'EPP' THEN
                DataLine := QUOTE('EP':RDSList<COL$REACTOR, I>):TAB$		;* EpiPRO reactors in SPC have "EP" on the front.
            END ELSE
                DataLine := QUOTE(RDSList<COL$REACTOR, I>):TAB$
            END
            
            DataLine := QUOTE(RDSList<COL$REJ_CNT, I>):TAB$
            DataLine := QUOTE(PSNo):TAB$
            DataLine := QUOTE(DeptResp):TAB$
            DataLine := QUOTE(LossCode):TAB$
            DataLine := QUOTE(LossDesc):TAB$
            DataLine := QUOTE(LossStage):TAB$
            DataLine := QUOTE(LossBy):TAB$
            DataLine := QUOTE(ACCode):TAB$
            DataLine := QUOTE(ACDesc):TAB$
            
            DataLines := DataLine:CRLF$
            
        NEXT I
        
        DateTime = SRP_Datetime("Now")
        Year = SRP_Datetime("Year", DateTime)
        Month = SRP_Datetime("Month", DateTime)
        Day = SRP_Datetime("Day", DateTime)
        
        FileName = NCRNo : '_NCR_Data_' : Year : Month : Day : '.txt'
        Path = Environment_Services('GetSPCDataPath')
        
        DOSFile = Path : '\' : FileName
        
        Set_Status(0)
        
        OSRead DOSRecord FROM DOSFile ELSE
            ErrCode = Status()
            IF ErrCode = 4 THEN
                DOSRecord = ''	;* File doesn't exist
            END ELSE
                * ErrMsg('Unable to read DOS file "SPC_Data.TXT" for update. ':ErrCode:' Please try again.')	;* Remarked out per Chad Becker request - dkk 12/9/14
                RETURN
            END
        END
        
        DOSRecord := DataLines
        
        OSWrite DOSRecord ON DOSFile
        
        ErrCode = Status()
        ErrorMessage = ''
        Begin Case
            Case ErrCode EQ 1
                ErrorMessage = 'Bad filename.'
            Case ErrCode EQ 2
                ErrorMessage = 'Cannot write to file. Access denied.'
            Case ErrCode EQ 3
                ErrorMessage = 'Disk or directory full.'
            Case ErrCode EQ 4
                ErrorMessage = 'File does not exist.'
            Case ErrCode EQ 5
                ErrorMessage = 'Unknown error.'
            Case ErrCode EQ 6
                ErrorMessage = 'Attempt to write to a read-only file.'
        End Case
        
        LogDate = Oconv(Date(), 'D4/')
        LogData     = ''
        LogData<1>  = Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTS') ; // Logging DTM
        LogData<2>  = Machine
        If ErrorMessage NE '' then
            ErrorMessage = 'Error in ' : Service : '. When attempting to write the NCR data to a file the following error occurred. ' : ErrorMessage
            LogData<3> = 'ERROR'
            LogData<4>  = ErrorMessage
            Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)
            Error_Services('Add', ErrorMessage)
        end else
            LogData<3> = 'INFO'
            LogData<4>  = 'Sucessfully sent NCR ' : NCRNo : ' data to SPC in file ' : DOSFile : '.'
            Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)
        end
    end else
        ErrorMessage = 'Error in ' : Service : '. One or more arguments were null or malformed.'	
        LogDate = Oconv(Date(), 'D4/')
        LogData     = ''
        LogData<1>  = Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTS') ; // Logging DTM
        LogData<2>  = Machine
        LogData<3> = 'ERROR'
        LogData<4>  = ErrorMessage
        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)
        Error_Services('Add', ErrorMessage)
    end
    
end service

Service GetOpenNCRKeys()
    table                    = "NCR"
	ErrorMessage             = ''
	RDSRecords               = ''
	Open "DICT ":table To @DICT Else
		ErrorMessage = 'Error opening NCR dictionary'
	End
	If ErrorMessage EQ '' then
		srch_strng  = "STATUS":@VM:'O':@FM
		option      = ""
		flag        = ""
		Btree.Extract(srch_strng, table, @DICT, RDSRecords, option, flag)
		If Flag NE 0 then
		    ErrorMessage = 'Error performing BTree Extract on NCR table.'
		end
	end
	If ErrorMessage NE '' then
	    Error_Services('Add', ErrorMessage)
	end
	Response = RDSRecords
end service

Service ConvertRecordToJson(NCRId)
    NCRJson      = ''
    ErrorMessage = ''
    If RowExists('NCR', NCRId) then
        NCRRecord = Database_Services('ReadDataRow', 'NCR', NCRId, True$, 0, False$)
        If Error_Services('NoError') then
                objNCR = ''
                If SRP_JSON(objNCR, 'New', 'Object') then
                    SRP_JSON(objNCR, 'SetValue', 'NCRId', NCRId)
                    ThisNCREntryDate     = NCRRecord<NCR_ENTRY_DATE$>
                    ThisNCREntryTime     = NCRRecord<NCR_ENTRY_TIME$>
                    If ThisNCREntryTime EQ '' then ThisNCREntryTime = 0
                    ThisNCREntryDateTime = OConv(SRP_DateTime('Combine', ThisNCREntryDate, ThisNCREntryTime), 'DT')
                    SRP_JSON(objNCR, 'SetValue', 'EntryDateTime', ThisNCREntryDateTime, 'String')
                    SRP_JSON(objNCR, 'SetValue', 'Shift', NCRRecord<NCR_SHIFT$>)
                    ThisNCRReactorNo     = NCRRecord<NCR_REACTOR$>
                    SRP_JSON(objNCR, 'SetValueArray', 'Reactor', ThisNCRReactorNo, @VM);//This needs to be a list to support EpiPro
                    ThisNCRCassId        = ''
                    ThisNCRReactorType   = Xlate('REACTOR', ThisNCRReactorNo, REACTOR_REACT_TYPE$, 'X')
                    If ThisNCRReactorType NE 'EPP' then
                        ThisNCRCassId = NCRRecord<NCR_RDS_ID$>
                    end else
                        ThisNCRWONo   = NCRRecord<NCR_WO_NO$>
                        ThisNCRStepNo = NCRRecord<NCR_WO_STEP$>
                        ThisNCRCassNo = NCRRecord<NCR_WO_MAT_CASS_NO$>
                        ThisNCRCassId = ThisNCRWONo : '-' : ThisNCRStepNo : '-' : ThisNCRCassNo
                    end
                    SRP_JSON(objNCR, 'SetValueArray', 'CassetteId', ThisNCRCassId, @VM)
                    SRP_JSON(objNCR, 'SetValue', 'LossStage', NCRRecord<NCR_LOSS_STAGE$>)
                    ThisNCRRejectCount = 0
                    ThisNCRRejectCount = Obj_Ncr('RejQty', NCRId:@RM:NCRRecord)
                    SRP_JSON(objNCR, 'SetValueArray', 'TotalRejected', ThisNCRRejectCount, @VM)
                    SRP_JSON(objNCR, 'SetValue', 'LossComments', NCRRecord<NCR_LOSS_COMMENTS$>)
                    NCRJson = SRP_JSON(objNCR, 'Stringify', 'Styled')
                    SRP_JSON(objNCR, 'Release')
                end else
                    ErrorMessage = 'Error creating NCR Json Object'
                end
                
        end else
            ErrorMessage = Error_Services('GetMessage')
        end
    end else
        ErrorMessage = 'Non-existent NCR Id passed to routine'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
    Response     = NCRJson
end service


