Basic OO question.... Dataset exchage ABL - Class

Posted by OctavioOlguin on 22-Feb-2017 10:43

Greetings.   A basic question.

I have this abl callign that class:

DEFINE VARIABLE clFinger AS CLASS FingerPrint NO-UNDO.
        DO ON ERROR  UNDO, LEAVE
            ON ENDKEY UNDO, LEAVE
            ON STOP   UNDO, LEAVE
            ON QUIT   UNDO, LEAVE:
                
            clFinger = NEW FingerPrint (INPUT-OUTPUT dataset dsBlobTemplates, OUTPUT resultadoProceso ) .
            WAIT-FOR System.Windows.Forms.Application:Run ( clFinger).
            
            IF resultadoProceso <> "" THEN 
            DO:
                MESSAGE "se presentó el siguiente error:" SKIP
                    resultadoProceso
                    VIEW-AS ALERT-BOX ERROR.
            END.
            ELSE 
            DO: 
                FIND FIRST ttblobTemplates.
                COPY-LOB FROM ttblobTemplates.TemplateField TO FILE "c:\t\t1.txt". 
            END.
      END.

Class:

// output parameters    
    DEFINE TEMP-TABLE ttblobTemplates NO-UNDO
        FIELD TemplateField  AS BLOB
        FIELD TemplateLenght AS INTEGER.  // 
    DEFINE DATASET dsTT1 FOR ttblobTemplates.
    DEFINE VARIABLE Resultado AS CHARACTER NO-UNDO.
        
    CONSTRUCTOR PUBLIC FingerPrint ( INPUT-OUTPUT DATASET dsTT1, OUTPUT resultado AS CHARACTER):
        SUPER().
        InitializeComponent().
        THIS-OBJECT:ComponentsCollection:ADD(THIS-OBJECT:components).
        CATCH e AS Progress.Lang.Error:
            UNDO, THROW e.
        END CATCH.
    END CONSTRUCTOR.

@VisualDesigner. 
    METHOD PRIVATE VOID btnStart_Click( INPUT sender AS System.Object, INPUT e AS System.EventArgs ):
        DEFINE VARIABLE ufs_res       AS UFS_STATUS NO-UNDO.                      
        DEFINE VARIABLE EnrollQuality AS INTEGER    NO-UNDO.
        DEFINE VARIABLE myChar        AS CHARACTER  NO-UNDO.
        DEFINE VARIABLE j             AS INTEGER    NO-UNDO.
            
        ASSIGN
            Scanner        = ?
            scannerManager = NEW UFScannerManager(THIS-OBJECT)
            ufs_res        = scannerManager:Init().

        //  * * ** *   some initialisation code here...                      
                
            Scanner:ClearCaptureImageBuffer().
            ufs_res = Scanner:CaptureSingleImage().                
            ASSIGN 
                Template = NEW "System.Byte[]" (max_template_size)
                ufs_res  = Scanner:Extract(Template,TemplateSize, EnrollQuality).            
            CREATE ttblobTemplates.
            ASSIGN 
                ttblobTemplates.TemplateField  = ByteArrayToMemptr(template)     //  from knowledgebase
                ttblobTemplates.TemplateLenght = TemplateSize. 
            COPY-LOB FROM OBJECT ttblobTemplates.TemplateField TO FILE "e:\t\fingerprint.dat" NO-CONVERT.
            RELEASE ttblobTemplates.
        END. 
        ELSE
            ASSIGN 
                resultado = msgError(ufs_res:value__).
                
        ufs_res = scannerManager:Uninit() .
        THIS-OBJECT:close().
            
        RETURN.
    
    END METHOD.

There are two huge problems, I can't get on to...

1)  the class indeed creates de fingerprintfile.dat
2) back on ABL, the TT is empty, and 
2.1) The "resultado" parameter gets empty, no matter  any value I stored in the class..

I wonder, where in the road I missed all about OO?

Posted by Laura Stern on 23-Feb-2017 09:38

Either make a method that returns the table as an OUTPUT parameter (which will make a copy of it unless you use the BIND/BY-REFERENCE feature - look up passing table parameters), or provide methods in the class to return the data that you're looking for.

All Replies

Posted by jquerijero on 22-Feb-2017 10:51

This is expected. It's an Application:Run(...). You are very likely closing the form either using the Red X or calling THIS-OBJECT:Close(), so the temp-table might be cleaned up because the form is disposed.

Also, the resultado variable you pass to the constructor and the one you assign inside btnStart_Click are two different instances.

Posted by Laura Stern on 22-Feb-2017 11:04

Re the last answer.  Not sure what the Application:Run() has to do with this. But yes - you called the constructor with an output parameter and the constructor did not populate it.  The constructor is just like a method - it has to do whatever you want it to do before it returns.  But since you do have the temp-table has a class member (labelling it "output parameter" makes no sense - it is just a class member), it should indeed be populated once the button is clicked and so would still be available once the Application:Run() is over (yes - from hitting the X or whatever).

Posted by OctavioOlguin on 22-Feb-2017 11:10

Sorry!!!! I missed the edit when pasting... the application:run() is on the same class....

Posted by jquerijero on 22-Feb-2017 11:26

Isn't the temp-table a copy when it was passed to the constructor? I would assume the copy is scoped to the form. The form is non-modal and will be disposed when Application:Run() ends, so I think the copy should go away too. If the form is not disposed after Application;Run() then that's a memory leak.

I always thought that, for this case, you need to call a method that outputs the temp-table before the form is disposed.

Posted by OctavioOlguin on 22-Feb-2017 11:49

On pursue of a solution, I want to conver this form to a class I can call from ABL to do it's stuff,  this would be the resulting class:

BLOCK-LEVEL ON ERROR UNDO, THROW.
    
USING Suprema.UFS_STATUS FROM ASSEMBLY.
USING Suprema.UFScanner FROM ASSEMBLY.
USING Suprema.UFScannerManager FROM ASSEMBLY.
    
      
CLASS Procs.enroll.Act.FingerPrint: 
         
    
    DEFINE PRIVATE VARIABLE scannerManager    AS Suprema.UFScannerManager NO-UNDO.
    DEFINE PRIVATE VARIABLE Scanner           AS Suprema.UFScanner        NO-UNDO.
    DEFINE PRIVATE VARIABLE max_template_size AS INTEGER                  NO-UNDO INITIAL 512.
    DEFINE PRIVATE VARIABLE Template          AS "System.Byte[]"          NO-UNDO.
    DEFINE PRIVATE VARIABLE TemplateSize      AS INTEGER                  NO-UNDO.
    DEFINE VARIABLE ufs_res       AS UFS_STATUS NO-UNDO.                      
    DEFINE VARIABLE EnrollQuality AS INTEGER    NO-UNDO.        
       
    DEFINE TEMP-TABLE ttblobTemplates NO-UNDO
        FIELD TemplateField  AS BLOB
        FIELD TemplateLenght AS INTEGER. 
    DEFINE DATASET dsTT1 FOR ttblobTemplates.
        
    DEFINE VARIABLE Resultado AS CHARACTER NO-UNDO.
        
    CONSTRUCTOR PUBLIC FingerPrint ( INPUT-OUTPUT DATASET dsTT1, OUTPUT resultado AS CHARACTER):
        SUPER().
            
        ASSIGN
            Scanner        = ?
            scannerManager = NEW UFScannerManager(THIS-OBJECT)
            ufs_res        = scannerManager:Init()
            resultado      = "OK".
            
        IF STRING(ufs_res) = STRING(UFS_STATUS:OK)  THEN
            ASSIGN 
                Scanner               = scannerManager:Scanners[0]
                Scanner:Timeout       = 5000
                Scanner:TemplateSize  = max_template_size
                Scanner:DetectCore    = FALSE
                Scanner:nTemplateType = 2001. 
        ELSE 
        DO:
            ASSIGN 
                resultado = msgError(ufs_res:value__).
            ufs_res = scannerManager:Uninit() .
            RETURN.
        END.
             
        Scanner:ClearCaptureImageBuffer().
        ufs_res = Scanner:CaptureSingleImage().
                
        IF STRING(ufs_res) <> STRING(UFS_STATUS:OK) THEN 
        DO:
            ASSIGN 
                Resultado = "No se pudo leer ".
            ufs_res = scannerManager:Uninit() .
            RETURN.
        END.                
        ASSIGN 
            Template = NEW "System.Byte[]" (max_template_size)
            ufs_res  = Scanner:Extract(Template,TemplateSize, EnrollQuality).
        IF  STRING(UFS_STATUS:OK) <> "OK" THEN 
            ASSIGN 
                resultado = "Lectura incorrecta".
                            
        IF EnrollQuality < 40 THEN 
            ASSIGN 
                resultado = "Repita la lectura de la huella".
        ELSE 
        DO:
            CREATE ttblobTemplates.
            ASSIGN 
                ttblobTemplates.TemplateField  = ByteArrayToMemptr(template)
                ttblobTemplates.TemplateLenght = TemplateSize. 
            COPY-LOB FROM OBJECT ttblobTemplates.TemplateField TO FILE "e:\t\fingerprint.dat" NO-CONVERT.
            RELEASE ttblobTemplates.
        END.                    
       
                
        ufs_res = scannerManager:Uninit() .
        THIS-OBJECT:close().
            
        RETURN.
        
        
        CATCH e AS Progress.Lang.Error:
            UNDO, THROW e.
        END CATCH.
    END CONSTRUCTOR.     
        
        
    
    /*------------------------------------------------------------------------------
     Purpose:  msgError.    Genera un mensje de error, de acuerdo a los codigos enviados por ufs_res:value__
     Notes:
    ------------------------------------------------------------------------------*/
    METHOD PUBLIC CHARACTER msgError( INPUT ndx AS INTEGER):
    	    
        DEFINE VARIABLE Cadena AS CHARACTER NO-UNDO.
    	    
        CASE ndx: 
            WHEN 0 THEN 
                cadena = "Success".
            WHEN -1 THEN 
                cadena = "(1) General error".
            WHEN -101 THEN 
                cadena = "(101) System has no license".
            WHEN -102 THEN 
                cadena = "(102) License is not match".
            WHEN -103 THEN 
                cadena = "(103) License is expired".
            WHEN -111 THEN 
                cadena = "(111) This function is not supported".
            WHEN -112 THEN 
                cadena = "(112) Input parameters are invalid".
            WHEN -201 THEN 
                cadena = "(201) Module is already initialized".
            WHEN -202 THEN 
                cadena = "(202) Module is not initialized".
            WHEN -203 THEN 
                cadena = "(203) Device number is exceed".
            WHEN -204 THEN 
                cadena = "(204) Error on loading scanner library".
            WHEN -211 THEN 
                cadena = "(211) Capturing is started USING UFS_CaptureSingleImage OR UFS_StartCapturing".
            WHEN -212 THEN 
                cadena = "(212) Capturing is timeout or aborted".
            WHEN -301 THEN 
                cadena = "(301) Input image is not good".
            WHEN -302 THEN 
                cadena = "(302) Extraction is failed".
            WHEN -351 THEN 
                cadena = "(351) Core is not detected".
            WHEN -352 THEN 
                cadena = "(352) Move finger to left".
            WHEN -353 THEN 
                cadena = "(353) Move finger to left-top".
            WHEN -354 THEN 
                cadena = "(354) Move finger to top".
            WHEN -355 THEN 
                cadena = "(355) Move finger to right-top".
            WHEN -356 THEN 
                cadena = "(356) Move finger to right".
            WHEN -357 THEN 
                cadena = "(357) Move finger to right-bottom".
            WHEN -358 THEN 
                cadena = "(358) Move finger to bottom".
            WHEN -359 THEN 
                cadena = "(359) Move finger to left-bottom".            
        END CASE.
                
        RETURN Cadena.
    
    END METHOD.
    
    
    
    METHOD PUBLIC STATIC MEMPTR ByteArrayToMemptr (poBytes AS "System.Byte[]":U):
 
        DEFINE VARIABLE myMemptr    AS MEMPTR        NO-UNDO .
        DEFINE VARIABLE oIntPointer AS System.IntPtr NO-UNDO .
 
        SET-SIZE (myMemptr) = poBytes:Length .
 
        oIntPointer = NEW System.IntPtr (GET-POINTER-VALUE (myMemptr)).
 
        System.Runtime.InteropServices.Marshal:Copy (poBytes, 0, oIntPointer, poBytes:Length).
 
        RETURN myMemptr .
 
        FINALLY:
            DELETE OBJECT oIntPointer.
        END FINALLY.
 
    END METHOD .
    
    
    DESTRUCTOR PUBLIC FingerPrint ( ):
        DEFINE VARIABLE ufs_res AS UFS_STATUS NO-UNDO.
            
        ufs_res = scannerManager:Uninit() .
        
    END DESTRUCTOR.
        
END CLASS.



The problem came on:  

scannerManager = NEW UFScannerManager(THIS-OBJECT),

it fires a (12905) error on the THIS-OBJECT, as I will have no visuals of the process no more...

and on   THIS-OBJECT:close()..   I bet there should be no CLOSE()

TIA 

Jorge

Posted by OctavioOlguin on 22-Feb-2017 12:14

Per documentation of suprema.dll, the instatiation should like this (this is the C# doc)

UFS_STATUS ufs_res;
UFScannerManager ScannerManager;
int nScannerNumber;
// Create an instance of ScannerManager
ScannerManager = new UFScannerManager(this);
// Initialize scanner module
ufs_res = ScannerManager.Init();
// Always check status return codes after running SDK
functions
// Meaning of status return code can be retrieved using
UFScanner.GetErrorString()
// In the tutorial, we omit error check codes
// Check number of scanners
nScannerNumber = ScannerManager.Scanners.Count;
Posted by Laura Stern on 22-Feb-2017 12:26

Re the post by jquerijero on Wed, Feb 22 2017 12:26: I would like to clear up this common misconception. Being disposed and being deleted are 2 different things.  When the form is closed, Disposed is called on it, but the object still exists as long as there is a reference to it.  The purpose of being disposed is really to clean up "unmanaged" resources (i.e., not managed by the .NET runtime) in a timely manner, such as file handles. So unless the class explicitly clears the temp-table, the temp-table and its contents will still be there when the form is closed, given that the object reference clFinger, is still in scope.

Posted by OctavioOlguin on 22-Feb-2017 12:43

So, changing the visual to a simple class to be called from ABL, will let me pass the temp-table back?  Still not able to run, as can't compile because the : NEW UFScannerManager(THIS-OBJECT) , that is present..

Before when the form inherited from FORM, there were no error.   now I supose it whould inherit from other class, but can't figure wich...  any clue?

Posted by Mike Fechner on 22-Feb-2017 12:59

The UFScanerManager relies on communicating with a Form. This is not uncommon to WinForms applications communicating with devices to be able to synchronize events from background threads into the UI thread. .

See my remark on this subject from another thread: It does not need to be THIS-OBJECT. If you plan to use the UFScannerManager from a simple class, you can use the application's main menu form or any other form. Just pass the reference of that form to the Constructor of your simple class, so that you can pass it along to the constructor of the UFScannerManager class.

Posted by OctavioOlguin on 22-Feb-2017 13:19

Thanks Mike...

Just to learn more...   My main menu is an ABL UI design, from top to bottom.... in the middle there is no form realized... so you think I should pop a simple form to show "processing..." or something like that?  In any case, how would this  be achieved?

Because, if i revert to the version where a full form made the process..  I'll be back ar square 1, where I can't pass the temp-table back to the calling procedure....

Posted by OctavioOlguin on 23-Feb-2017 09:07
Posted by Laura Stern on 23-Feb-2017 09:38

Either make a method that returns the table as an OUTPUT parameter (which will make a copy of it unless you use the BIND/BY-REFERENCE feature - look up passing table parameters), or provide methods in the class to return the data that you're looking for.

Posted by Tim Kuehn on 23-Feb-2017 09:56

Another option would be to pass a TT or Dataset handle through a method and move the data that way.

Posted by OctavioOlguin on 23-Feb-2017 10:07

But, (having a real hard time trying to imagine what's hapening at runtime) what should be steps with this kind of procedure...

Just got a strike of ligth..  I'll explain so newbies like me, don't strugle with old mental patterns for days like me....

I was trying to use old sequential flow of instructions, wanted the class do all the process on instatiation...

the "provide methods" on Ms Laura's post, as simple and natural for her to say it, created a breaktru in my mind. Finally I got it, after several years trying to accomodate the OO paradigm in my head. and countless readings, finally make sense...

Thanks!.

Posted by Peter Judge on 23-Feb-2017 10:16
I found that the jump from a single .P to a set of small internal procedures is the important one. Classes & methods are that point just a compiler-enforced equivalent with some extra syntactic sugar (like access levels, interfaces, abstract classes etc).
 
If you are used to working with persistent procedures that are relatively small in the scope of what they do, and that use internal procedures that are also small in scope then you should take to classes/methods like a duck to water.
 
That’s not to say you can’t emulate the spaghetti .P with a single “god” class + many static methods either. But please don’t :)
Posted by jquerijero on 23-Feb-2017 10:33

Think of class as if it is a procedure (.p) called persistently.

Posted by OctavioOlguin on 23-Feb-2017 11:50

Just an extra question.....

As this class needs visual representation, for what comented Mike Fechner, how should I do this task at hand?

define a class, which in several  methods which in turn, just initialize the scanner (scannerManager = NEW UFScannerManager(THIS-OBJECT) and pops a dialog.. no.. that would leave this-object with no suitable properties to be hooked by the device...

How valid (solid) is the first object (class) to instantiate another object wich in turn has visuals and do the fingerprint reader stuff?

I can't imagine if this is a solid approach

Posted by Thomas Mercer-Hursh on 23-Feb-2017 12:01

Of course, if you encapsulate the TT in the class and provide methods on the class to do all the work on the TT, then you don't need to pass the TT in or out.  This is really the OO thing to do, i.e., hide the implementation details so that nothing else needs to know how things are done, just that certain services are available.

This thread is closed