Metodo para construir clases FW14.11 y XHarbour 1.2.3

Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Verhoven » 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?.
Verhoven
 
Posts: 505
Joined: Sun Oct 09, 2005 7:23 pm

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby lucasdebeltran » 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.
User avatar
lucasdebeltran
 
Posts: 1303
Joined: Tue Jul 21, 2009 8:12 am

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby cnavarro » 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
User avatar
cnavarro
 
Posts: 6500
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Verhoven » 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
Verhoven
 
Posts: 505
Joined: Sun Oct 09, 2005 7:23 pm

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Antonio Linares » Sun Dec 07, 2014 4:06 pm

Verhoven,

Puedes copiar aqui el código de tu clase ?
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 41314
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Verhoven » Sun Dec 07, 2014 8: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: Select all  Expand view
/*
  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 ) )

//----------------------------------------------------------------------------//
Verhoven
 
Posts: 505
Joined: Sun Oct 09, 2005 7:23 pm

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Antonio Linares » Sun Dec 07, 2014 9: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
User avatar
Antonio Linares
Site Admin
 
Posts: 41314
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Verhoven » Sun Dec 07, 2014 9:12 pm

Ok. Con esto compila. ?Se puede devolver también .F. En vez de self?
Verhoven
 
Posts: 505
Joined: Sun Oct 09, 2005 7:23 pm

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Antonio Linares » 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
User avatar
Antonio Linares
Site Admin
 
Posts: 41314
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby xmanuel » Mon Dec 08, 2014 1:26 pm

Create una data que sea

DATA isOpen

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

Code: Select all  Expand view

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

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: Select all  Expand view

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 :oops:
______________________________________________________________________________
Sevilla - Andalucía
xmanuel
 
Posts: 756
Joined: Sun Jun 15, 2008 7:47 pm
Location: Sevilla

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Verhoven » Mon Dec 08, 2014 9: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.
Verhoven
 
Posts: 505
Joined: Sun Oct 09, 2005 7:23 pm

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby elvira » Tue Dec 09, 2014 7: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() )
elvira
 
Posts: 515
Joined: Fri Jun 29, 2012 12:49 pm

Re: Metodo para construir clases FW14.11 y XHarbour 1.2.3

Postby Verhoven » Sat Dec 13, 2014 8: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: Select all  Expand view
/*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.
Verhoven
 
Posts: 505
Joined: Sun Oct 09, 2005 7:23 pm


Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 90 guests