FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posts: 563
Joined: Sun Oct 09, 2005 07:23 PM
Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 10:18 AM

Tengo dos errores con la actualización que no logro solucionar:

1º.- Una clase hecha hace varios años que con las versiones que venía usando de FW y XHarbour cuando creaba la clase con el constructor NEW devolvía NIL en vez de Self y esto funcionaba correctamente.
Ahora sustituyo ese NIL por SELF y aunque compila bien el programa arranca pero me arroja un "Abnormal Program Termination", de modo que no puedo averigüar el error donde está.

2º.- Así mismo, no veo la manera de una vez invocado el método New haga para o bien crear la clase o no llegar a crearla si no se cumplen las condiciones necesarias.
Esto antes lo tenía resuelto con un simple return .F. pero ahora, con la actualización, si no devuelvo Self en el método New el programa falla. ¿Como puedo hacer para que una vez invocado el método New éste mísmo opte por crear la clase o, si no se dan las condiciones requeridas, no la cree?.

Posts: 1303
Joined: Tue Jul 21, 2009 08:12 AM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 10:39 AM

Hola,

Prueba con ::Self.

Muchas gracias. Many thanks.



Un saludo, Best regards,



Harbour 3.2.0dev, Borland C++ 5.82 y FWH 13.06 [producción]



Implementando MSVC 2010, FWH64 y ADO.



Abandonando uso xHarbour y SQLRDD.
Posts: 6755
Joined: Wed Feb 15, 2012 08:25 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 10:45 AM

Y quizás, si no se cumple la condicion que requieres, puedes antes del Return Self, poner el Self := Nil

Cristobal Navarro

Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo

El secreto de la felicidad no está en hacer lo que te gusta, sino en que te guste lo que haces
Posts: 563
Joined: Sun Oct 09, 2005 07:23 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 12:21 PM

Probé poniendo SELF := NIL, para los casos en que no se verifica la condición para crear la clase, pero arroja el error de ejecución:

ERROR BASE/1065 Invalid Self: New

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 04:06 PM

Verhoven,

Puedes copiar aqui el código de tu clase ?

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 563
Joined: Sun Oct 09, 2005 07:23 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 08:06 PM
Esta clase la llevo usando años tal cual está sin problemas. Ahora cuando he actualizado las versiones de FW y xHarbour es cuando me da problemas.
La copio:

Code (fw): Select all Collapse
/*
  TCOMCLASS.prg
  21/01/2008
  Simple clase para manejo de los puertos serie.
  Para incluirla en nuestro programa basta con poner despues de la función main de nuestro programa:
      #include 'TCOMCLASS.prg'
      y añadir la libreria hbcomm.lib
  
  Ultima Actualización: 07/08/2012
*/

#include "fivewin.ch"

// Valores de comandos para comunicaciones por puerto serie.
#define STX   chr(2)  //0x02
#define ETX   chr(3)  //0x03   
#define EOT   chr(4)  //0x04   
#define ENQ   chr(5)  //0x05   
#define ACK   chr(6)  //0x06   
#define NAK   chr(21) //0x15   
#define NACK  chr(21) //0x05
#define SOH   chr(1)  //0x01
#define ETB   chr(23) //0x17

CLASS TComClass
     DATA cCom     // ej: COM1, COM2,...
     DATA nComm    // Manejador del puerto COM
     DATA nBufferEntrada
     DATA nBufferSalida
     
     DATA nError
     
     METHOD new(nPuerto,nBaudios,nBits,cParidad,nStops,nBufferSalida,nBufferEntrada) CONSTRUCTOR

     METHOD End()
     
     METHOD COM_EscribePuerto(cTexto)
     
     METHOD COM_LimpiaBuffer()
     
     METHOD COM_LeePuerto(nLong)
     
     METHOD COM_LeePuerto_nChars(nChars)
     
     METHOD COM_LeePuertoHastaChrmasBcc(cChar)
     
     METHOD COM_nCharsEnInBuffer()
     
     METHOD COM_nCharsEnOutBuffer()
     
     /*
     METHOD Escape( nCode ) BLOCK { |Self,nCode| IIF( EscapeCommFunction( ::nId, nCode ) < 0, ;
                                        MsgInfo( OemToAnsi( "Error enviando c¢digo de escape" ) ), ) }

     METHOD Flush( nQueue ) BLOCK { |Self,nQueue| IIF( FlushComm( ::nId, nQueue ) != 0, ;
                                        MsgInfo( "Error al vaciar el buffer de las comunicaciones" ), ) }

     METHOD UnBreak() BLOCK { |Self| IIF( ClearCommBreak( ::nId ) < 0, ;
                                        MsgInfo( "Error desbloqueando el puerto de comunicaciones" ), ) }

     METHOD Break() BLOCK { |Self| IIF( SetCommBreak( ::nId ) < 0, ;
                                        MsgInfo( "Error al bloquear el puerto de comunicaciones" ), ) }
     */
ENDCLASS

//----------------------------------------------------------------------------//

METHOD New(nPuerto,nBaudios,nParidad,nBits,nStops,nBufferSalida,nBufferEntrada) class TComClass
   default nPuerto := 1
   default nBaudios:= 9600
   default nParidad:= 0  // 0,1,2,3 -> none, odd, mark, even
   default nBits   := 8
   default nStops  := 1  // 0,1,2   -> 1,  1.5  , 2
   default nBufferSalida :=1024
   default nBufferEntrada:=1024
   
   ::cCom    :='COM'+alltrim(str(nPuerto)) //AS STRING
   ::nBufferEntrada := nBufferEntrada
   ::nBufferSalida  := nBufferSalida
   ::nError  :=0
  
   //msgwait("Abriendo puerto: "+::cCom,::cCom,1)
   ::nComm := init_Port( ::cCom, nBaudios,nBits,nParidad,nStops, nBufferEntrada )
   if ::nComm > 0
     if !ISWORKING(::nComm)
        msgstop( "Puerto "+::cCom+' desconectado',                        ;
                 "Error intentando abrir puerto serie" )
        return( .F. )
     endif
     ::COM_LimpiaBuffer()
   endif
return NIL

//----------------------------------------------------------------------------//

METHOD End() class TComClass
   //Close_Port( ::nComm )
   UNint_Port()
return NIL

//----------------------------------------------------------------------------//

METHOD COM_EscribePuerto(cTexto) class TComClass
  local lResult  := .t.
  local nRetardo := 1  //Segundos
  local nIntentos:= 3, ii:=1
  local nBytes
  
  default cTexto :=time()+" PRUEBA DE ESCRITURA EN EL PUERTO "+::cCom+ Chr(13)
  OutChr( ::nComm, cTexto, len(cTexto) )
/*
   for ii:=1 TO nIntentos
    
    if !OutChr( ::nComm, cTexto, len(cTexto) )
      //MsgInfo( ::cCom+", Error de escritura " , "ERROR "+::cCom )
      lResult:=.f.
      RetardoSecs(nRetardo)
     else
      // Windows requires to have a Window at least to perform comunications !!!
      // Let's use the MessageBox() Window as default
      // MsgInfo( Str( nBytes ) + " bytes sent" )  //Probado a mi no me ha hecho falta
      lResult:=.t.
      exit
    endif
   next ii
*/
return lResult

//----------------------------------------------------------------------------//

METHOD COM_LimpiaBuffer() class TComClass
  local lResult:=.t.
  OutBufClr( ::nComm )
return lResult

//----------------------------------------------------------------------------//

//Recoge del puerto COM todos los caracteres presentes en el buffer de entrada.
METHOD COM_LeePuerto() class TComClass
  local cBuffer:=space(1)
  local nBytes :=0 

   // Get a chunk from the COM port. El tamaño de la loncha (chunk) es el del buffer.

   nBytes=InBufSize(::nComm)
   cBuffer:=space(nBytes)
   
   if nBytes != InChr( ::nComm, @cBuffer, nBytes )
     // MsgStop( 'Some kind of read failure on COM Port.'+::cComm )
   endif
return (cBuffer)

//----------------------------------------------------------------------------//

//Recoge del puerto COM nChars caracteres
METHOD COM_LeePuerto_nChars(nChars) class TComClass
  local cBuffer:=space(1)
  local nBytes := 0

  cBuffer:=space(nChars)
  nBytes:=nChars
   
   if nBytes != InChr( ::nComm, @cBuffer, nChars )
      //MsgStop( 'Some kind of read failure on COM Port.'+::cComm )
   endif
return (cBuffer)

//----------------------------------------------------------------------------//

/*Recoge del puerto COM todos los caracteres hasta encontrar el cChar y el siguiente 
   que contiene el código BCC ó LRC del mensaje.
  Se implementa para poder leer el puerto hasta encontrar  una marca, 
    normalmente ETX, y no recoger caracteres siguientes que pueden formar 
   parte de otro msg.
  Si el primer caracter es EOT sale sin leer más.
*/
METHOD COM_LeePuertoHastaChrmasBcc(cChar) class TComClass
  local cBuffer:=space(::nBufferEntrada)  //Necesario para reservar memoria para la cadena cBuffer
  local cCarAct:=space(1)
  local nBytes := 0, nChars:=1, nLeidos:=0
  
  default cChar:=ETX
  
  cBuffer:=alltrim(cBuffer)  //Necesario para dejar la cadena cBuffer vacía.

  while ( cCarAct<>cChar .and. nLeidos <= ::nBufferEntrada )
       // Lee el caracter siguiente
       if nBytes != InChr( ::nComm, @cCarAct, nChars )
          //MsgStop( 'Some kind of read failure on COM Port.'+::cComm )
         else
          nLeidos:=nLeidos+1
       endif
       if cCarAct == EOT
          return (cCarAct)
         else
          cBuffer:=cBuffer + cCarAct
       endif
  enddo
  // Recoge ahora el caracter siguiente a cChar que es el correspondiente al BCC
  if nBytes != InChr( ::nComm, @cCarAct, nChars )
     //MsgStop( 'Some kind of read failure on COM Port.'+::cComm )
  endif
  cBuffer:=cBuffer + cCarAct
  //msginfo("Leido del puerto:"+cBuffer,"COM_LeePuertoHastaChrmasBcc(cChar)")
return (cBuffer)

//----------------------------------------------------------------------------//

// Devuelve los caracteres presentes en el Buffer de Entrada
METHOD COM_nCharsEnInBuffer() class TComClass
return ( InBufSize( ::nComm ) )

//----------------------------------------------------------------------------//

// Devuelve los caracteres presentes en el Buffer de Salida 
METHOD COM_nCharsEnOutBuffer() class TComClass 
return ( OutBufSize( ::nComm ) ) 

//----------------------------------------------------------------------------//
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 09:09 PM

Verhoven,

En el método New() tienes que devolver Self en vez de nil

Con eso debe funcionar bien :-)

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 563
Joined: Sun Oct 09, 2005 07:23 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 09:12 PM

Ok. Con esto compila. ?Se puede devolver también .F. En vez de self?

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sun Dec 07, 2014 10:03 PM

Un método constructor en Harbour tiene que devolver Self obligatoriamente.

En Clipper no era necesario, pero en Harbour si :-)

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 817
Joined: Sun Jun 15, 2008 07:47 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Mon Dec 08, 2014 01:26 PM
Create una data que sea

DATA isOpen

Por ejemplo, y cambia tu código del metodo new:

Code (fw): Select all Collapse
//----------------------------------------------------------------------------//

METHOD New(nPuerto,nBaudios,nParidad,nBits,nStops,nBufferSalida,nBufferEntrada) class TComClass
   default nPuerto := 1
   default nBaudios:= 9600
   default nParidad:= 0  // 0,1,2,3 -> none, odd, mark, even
   default nBits   := 8
   default nStops  := 1  // 0,1,2   -> 1,  1.5  , 2
   default nBufferSalida :=1024
   default nBufferEntrada:=1024
   
   ::cCom    :='COM'+alltrim(str(nPuerto)) //AS STRING
   ::nBufferEntrada := nBufferEntrada
   ::nBufferSalida  := nBufferSalida
   ::nError  :=0
 
   //msgwait("Abriendo puerto: "+::cCom,::cCom,1)
   ::nComm := init_Port( ::cCom, nBaudios,nBits,nParidad,nStops, nBufferEntrada )
   ::isOpen := ( ::nComm > 0 ) .and. ISWORKING(::nComm) <------------------------------------------------------------------------------------------------------------esto 
   if ::isOpen <-----------------------------------------------------------------------------------------------------------------------------esto 
     ::COM_LimpiaBuffer()
   endif
return self  <----------------------------------------------------------------------------------------------------------------------------------esto


Luego en tu código del PRG puedes poner:

Code (fw): Select all Collapse
oCon := TComClass():New(nPuerto,nBaudios,nParidad,nBits,nStops,nBufferSalida,nBufferEntrada)

if !oCon:isOpen // Si no funciona
       msgstop( "Puerto "+oCon:cCom+' desconectado',                        ;
                 "Error intentando abrir puerto serie" )
        return
endif

 // Aqui lo que quieras si funciona


Mi consejo es que no metas salidas a pantalla si es una clase de proceso.
El día de mañana puede que cambies a patrones de diseño y uses el Modelo Vista Controlador MVC, por ejemplo.

Otra cosilla, deberías cambiar la data ::nComm por ::hComm ya que seguramente lo que devuelve la función init_Port sea un manejador, o sea un puntero a una estructura y en C no es lo mismo :-)
______________________________________________________________________________

Sevilla - Andalucía
Posts: 563
Joined: Sun Oct 09, 2005 07:23 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Mon Dec 08, 2014 09:49 PM

Ok. He realizado unas modificaciones para adaptarlo a esa recomendación que permite mantener todo dentro de la clase.
Las modificaciones básicamente consiste en añadir llamadas al método end() para el caso en que IsOpen=.F. y así evitar tener un objeto creado sin uso.
Antes esto lo comprobaba con empty(oBjetoCom).
Gracias por el aporte.

Posts: 518
Joined: Fri Jun 29, 2012 12:49 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Tue Dec 09, 2014 07:16 AM

Hola,

Por favor, ¿podrías compartir un ejemplo completo de uso?.

He probado esto, pero el msginfo me lo devuelve vacío.

oCon:COM_EscribePuerto( "hola mundo " )
msginfo( oCon:COM_LeePuerto() )

Posts: 563
Joined: Sun Oct 09, 2005 07:23 PM
Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3
Posted: Sat Dec 13, 2014 08:00 PM
Cuando me pides un ejemplo "completo" no entiendo muy bien a qué te refieres, no obstante, copio una función completa de lectura de un puerto com para un equipo con protocolo ccTalk (Billeteros y Monederos):

Code (fw): Select all Collapse
/*Lee la respuesta de un equipo ccTalk y la devuelve en el formato matriz:
  {nDest,nNumDatos,nSlave,nHeader,cDatos}
*/
function ccTalk_ReadComPort(lHacerRetardo, nRetardo_ms)
  local cRespuesta:=space(255), cRespSinEco:=space(255)
  local i:=0, cMsg:=dtos(Date())+" "+time()+' ccTalk_ReadComPort: '
  local aResp:={}  //Respuesta por defecto, si no hay respuesta del equipo
  
  local nLenRespuesta:=0, nLenLastSent:=len(cLastSentccTalkCommand)
  
  local nTries := 3, niter:=0
  
  default lHacerRetardo:=.T.
  default nRetardo_ms:=20
  
  while nTries > 0
     nTries = nTries - 1
     niter = niter+1
     if lHacerRetardo
        Retardo_ms(nRetardo_ms)
     endif
     cRespuesta:=oComccTalk:COM_LeePuerto()
  
     nLenRespuesta:=len(cRespuesta)
     
     // No hay respuesta
     if nLenRespuesta==0
       if nTries = 0
           EscribeEnFichTxt(space(18)+'ERROR: THERE IS NO ANSWER FROM the Slave in '+str(niter*nRetardo_ms,5)+' ms','LOGCCTALK.TXT',.t.,2)
           return {-1,-1,-1,-1,""}
          else
           loop
       endif
     endif
  
     //Si no hay respuesta devuelve una cadena indicando error
     if nLenRespuesta == nLenLastSent //Esto se puede hacer solo porque la respuesta incluye el comando enviado previamente.
       if nTries = 0
           EscribeEnFichTxt(space(18)+'ERROR: THERE IS NO ANSWER FROM the Slave in '+str(niter*nRetardo_ms,5)+' ms','LOGCCTALK.TXT',.t.,2)
           return {-1,-1,-1,-1,""}
          else
           loop
       endif
     endif
  
     //oComccTalk:COM_LimpiaBuffer()  NO HACE FALTA. LO LIMPIA AL LEER LA INSTRUCCION COM_LeePuerto()
     //Verifica que lo leido no empiece por la última instrucción enviada por el puerto,es decir,
     // quita el eco de la instrucción que viene al principio de la respuesta del equipo.
     if nLenRespuesta > nLenLastSent
        if LEFT(cRespuesta,nLenLastSent)=cLastSentccTalkCommand
           cRespSinEco:=substr(cRespuesta,nLenLastSent+1,nLenRespuesta-nLenLastSent)
        endif
        exit
     endif
  enddo
  
  //Formatea mensaje para pintado en log
  for i=1 TO (nLenRespuesta - nLenLastSent)
      cMsg := cMsg + '['+str(asc(substr(cRespSinEco,i,1)),3)+']'
  next i
  
  // No pinta el polling del credito para no saturar el log
  //  229 es el polling de credito para monedas y 159 para los billetes
  if asc(substr(cRespuesta,4,1)) == 229 .or. asc(substr(cRespuesta,4,1)) == 159
     if asc(substr(cRespSinEco,5,1)) <> aCoinsLastStatus[1] .or. asc(substr(cRespSinEco,5,1)) <> aBillsLastStatus[1]
        EscribeEnFichTxt(CRLF+cMsg+' = '+cRespSinEco+' ['+str(niter*nRetardo_ms,5)+' ms ]','LOGCCTALK.TXT',.t.,2)
     endif
    else
        EscribeEnFichTxt(cMsg+' = '+cRespSinEco+' ['+str(niter*nRetardo_ms,5)+' ms ]','LOGCCTALK.TXT',.t.,2)
  endif
  // Extrae los datos a formato matriz de datos y cadenas a ASCII
  aResp:=ccTalk_ExtraeRespuesta(cRespSinEco)
return aResp


Espero que te aclare la duda sobre el tratamiento de la salida del método correspondiente a la lectura del puerto que me solicitas.
Saludos.

Continue the discussion