Classe ZDBFTABLE – Parte 01 – Introdução

Introdução

No post anterior (Lendo DBF em AdvPL — Sem DRIVER ou RDD), foi apresentado um exemplo de uso da classe ZDBFTABLE, criada em AdvPL para ler arquivos DBF usando diretamente as funções de baixo nível de arquivo do AdvPL. Com isso, eu consigo ler um DBF em qualquer Build, 32 ou 64 Bits, Windows ou Linux, sem nenhum Driver ou RDD.

Agora, vamos ver por dentro como a classe lê o arquivo do disco, e como ela emula o comportamento ISAM de leitura de registros e navegação de dados através de todos os métodos da classe.

Comportamento atual

Neste post, a classe está na sua primeira revisão, ela acabou de ganhar um método para permitir setar um filtro. O comportamento atual de leitura de dados possui as seguintes características e restrições:

  • Não suporta nenhum índice em nenhum formato.
  • Não respeita o filtro SET DELETED da RDD padrão. Todos os registros são lidos, inclusive deletados.
  • Suporta campos memo em formato DBT e FPT
  • Passa a suportar filtro com qualquer expressão válida em AdvPL, desde que os campos estejam escritos em letras maiúsculas.
  • Suporta navegação para frente — ::DbSkip(n)  — ou para trás — ::DbSkip(-n)
  • Suporta posicionamento direto pelo RECNO — DBGoto(nRecno)

Declaração da Classe

A orientação a objetos do AdvPL utilizada ainda não é o novo TL++, a classe foi escrita usando a orientação básica a Objetos do AdvPL, para ser possível utilizá-la inclusive em builds bem mais antigas do Protheus Server. Qualquer build de Protheus Server lançada nos últimos 15 anos roda esse código. Para separar o que deve e o que não deve ser feito, foram aditadas as seguintes regras:

  1. Não acesse propriedades da classe. Elas são de uso interno e podem ser alteradas. Use sempre os métodos publicados.
  2. Assuma que qualquer método com o nome iniciado com “_” não deve ser chamado pelos fontes que consomem a classe. Eles são de uso interno da classe apenas.

Logo, vamos para a declaração ou prototipagem da classe ZDBFTALBE:

CLASS ZDBFTABLE FROM LONGNAMECLASS

  DATA cDataFile		// Nome do arquivo de dados
  DATA cMemoFile		// Nome do arquivo memo (DBT/FPT) 

  DATA cDBFType			// Identificador hexadecimal do tipo do DBF 
  DATA dLastUpd			// Data registrada dentro do arquivo como ultimo UPDATE 
  DATA nLastRec			// Ultimo registro do arquivo - Total de registros
  DATA nRecLength		// Tamanho de cada registro 
  DATA nDataPos 		// Offset de inicio dos dados 
  DATA lHasMemo			// Tabela possui campo MEMO ?
  DATA cMemoType		// Identificador (extensao) do tipo do campo MEMO
  DATA nFileSize 		// Tamanho total do arquivo em bytes 
  DATA nFldCount		// Quantidade de campos do arquivo 
  DATA aRecord			// Array com todas as colunas do registro atual 
  DATA lDeleted			// Indicador de registro corrente deletado (marcado para deleção ) 
  DATA nRecno			// Número do registro (RECNO) atualmnete posicionado 
  DATA bFilter                  // Codeblock de filtro 

  DATA lBOF			// Flag de inicio de arquivo 
  DATA lEOF			// Flag de final de arquivo 
  
  DATA nHData			// Handler do arquivo de dados
  DATA nHMemo			// Handler do arquivo de MEMO
  DATA aStruct		   	// Array com a estrutura do DBF 
    	
  DATA nLastError		// Ultimo erro ocorrido 
  DATA cLastError		// Descrição do último erro 

  // ========================= Metodos de uso público da classe

  METHOD NEW(cFile)		// Construtor 
  METHOD OPEN()			// Abertura da tabela 
  METHOD CLOSE()		// Fecha a tabela 

  METHOD GetDBType()		// REtorna identificador hexadecimal do tipo da tabela 
  METHOD GetDBTypeStr() 	// Retorna string identificando o tipo da tabela 

  METHOD Lastrec()		// Retorna o total de registros / numero do ultimo registro da tabela 
  METHOD RecCount()		// Retorna o total de registros / numero do ultimo registro da tabela 
  METHOD DbStruct()		// Retorna CLONE da estrutura de dados da tabela 
  METHOD DBGoTo(nRec)		// Posiciona em um registro informado. 
  METHOD DBGoTop()		// Posiciona no RECNO 1 da tabela 
  METHOD DBGoBottom()   	// Posiciona em LASTREC da tabela 
  METHOD DbSkip( nQtd )     // Navega para frente ou para tráz uma quantidade de registros 
  METHOD FieldGet( nPos )   // Recupera o conteudo da coluna informada do registro atual 
  METHOD FieldName( nPos )  // Recupera o nome da coluna informada 
  METHOD FieldPos( cField ) // Retorna a posicao de um campo na estrutura da tabela ( ID da Coluna )
  METHOD BOF()	            // Retorna .T. caso tenha se tentado navegar antes do primeiro registro 
  METHOD EOF()		    // Retorna .T, caso o final de arquivo tenha sido atingido 
  METHOD Recno()            // Retorna o numero do registro (RECNO) posicionado 
  METHOD Deleted()	    // REtorna .T. caso o registro atual esteja DELETADO ( Marcado para deleção ) 
  METHOD SetFilter()        // Permite setar um filtro para os dados 
  METHOD ClearFilter()      // Limpa o filtro 

  METHOD Header() 	    // Retorna tamanho em Bytes do Header da Tabela
  METHOD RecSize()	    // Retorna o tamanho de um registro da tabela 
  METHOD LUpdate()	    // Retorna a data interna do arquivo que registra o ultimo update 
 
  METHOD GetError() 	    // Retorna o Codigo e Descricao por referencia do ultimo erro 
  METHOD GetErrorCode()     // Retorna apenas oCodigo do ultimo erro ocorrido
  METHOD GetErrorStr()	    // Retorna apenas a descrição do último erro ocorrido

  // ========================= Metodos de uso interno da classe

  METHOD _ResetError()	    // Limpa a ultima ocorrencia de erro 
  METHOD _SetError()        // Seta uma nova ocorrencia de erro 
  METHOD _ResetVars() 	    // Inicializa propriedades do Objeto, no construtor e no CLOSE
  METHOD _ReadHeader()	    // Lê o Header do arquivo  de dados
  METHOD _ReadStruct()	    // Lê a estrutura do arquivo de dados 
  METHOD _ReadRecord()	    // Le um registro do arquivo de dados
  METHOD _ClearRecord()	    // Limpa o registro da memoria (EOF por exemplo) 
  METHOD _ReadMemo()        // Recupera um conteudo de campo memo por OFFSET
  METHOD _CheckFilter()     // Verifica se o registro atual está contemplado no filtro 
  METHOD _SkipNext()	    // Le o proximo registro da tabela considerando filtro
  METHOD _SkipPrev()        // Le o registro anterior da tabela considerando filtro 

ENDCLASS

Por dentro do DBF

Eu já sabia por dentro como era um DBF — conceitualmente, header, estrutura e dados — mas não tinha entrado no “detalhe” do byte-a-byte dentro do arquivo. Em linhas gerais, os primeiros 32 bytes são o header do arquivo, com detalhes sobre o tipo ou versão de DBF, tamanho  do registro, início dos dados. Logo depois do header vêm a estrutura de campos, cada campo ocupa 32 bytes com nome, tipo, tamanho, decimais e outras propriedades para outras versões.

Terminada a estrutura, vêm a área de dados. Cada registro no DBF ocupa um valor fixo de bytes, equivalente a soma dos tamanhos dos campos na estrutura, mais um byte — o primeiro byte de dados do campo é usado para a marca de registro deletado, que pode ser reaproveitado pela aplicação ou eliminado fisicamente em uma operação de PACK.

Inclusive, uma curiosidade. Um campo MEMO possui um tamanho de 10 bytes na estrutura, Estes 10 bytes são usados para gravar no DBF uma referência ao bloco inicial para a leitura do campo memo em um arquivo auxiliar (DBT ou FPT). No DBF somente são gravados campos de tamanho fixo. Uma String de 40 bytes, mesmo que você preencha ela apenas com 10, é gravada com espaços em branco a direita.

Para maiores detalhes do DBF por dentro, consulte os links de referência deste post, foram o meu “guia” para abrir e ler as tabelas.

Por dentro da classe ZDBFTABLE

Vamos destrinchar a classe método a método, com os porquês de cada propriedade. O princípio de armazenamento e leitura de um driver xBASE ou ISAM é: Em um arquivo aberto, você sempre está posicionado em um registro, ou está em EOF (End Of File — Final de Arquivo). Você pode reposicionar o registro para um registro anterior ou próximo. Sem usar um índice, você navega pela ordem física de inserção dos registros. Cada registro ou Recno() é a posição ordinal deste registro no arquivo, iniciada em 1.

As propriedades da classe são usadas para manter o contexto de abertura e posicionamento do arquivo, para ser possível a navegação e reposicionamento dos registros. Cada registro posicionado é lido no posicionamento. Quando ao campo MEMO, apenas é lida a referência para localizar o dado no arquivo de dados de memo (DBF ou FPT), a leitura real do conteúdo somente será feita caso a aplicação AdvPL fizer um Fieldget() do campo MEMO.

Método NEW() — Construtor

METHOD NEW(cFile) CLASS ZDBFTABLE
::_ResetVars() 
::cDataFile := lower(cFile)
Return self

Recebe como parâmetro o path mais nome do arquivo a ser utilizado. Não é realizada a abertura da tabela no construtor. O método interno _ResetVars() inicializa as propriedades do contexto do arquivo na classe, e foi feita separadamente para ser chamada também no fechamento da tabela pelo método Close().

Método OPEN

Responsável pela abertura efetiva da tabela, identificação de Header e Estrutura, e em caso de sucesso, já posiciona e lê o primeiro registro da tabela — caso a mesma tenha dados. As etapas intermediárias de abertura são feitas por classes internas, para não deixar o corpo da classe de abertura muito grande, e procurar seguir as premissas de boas práticas de programação — Cada classe tem uma finalidade, cada método também têm uma finalidade.

METHOD OPEN() CLASS ZDBFTABLE 

::_ResetError()

If ::nHData <> -1
	::_SetError(-1,"File Already Open")
	Return .F.
Endif

// Abre o arquivo de dados
::nHData := Fopen(::cDataFile)

If ::nHData == -1
	::_SetError(-2,"Open Error - File ["+::cDataFile+"] - FERROR "+cValToChar(Ferror()))
	Return .F.
Endif

// Pega o tamanho do arquivo 
::nFileSize := fSeek(::nHData,0,2)

// Lê o Header do arquivo 
If !::_ReadHEader()
	FClose(::nHData)
	::nHData := -1
	Return .F. 
Endif

If ::lHasMemo

	// Se o header informa que a tabela possui campo MEMO 
	// Determina o nome do arquivo MEMO 

	::cMemoFile := substr(::cDataFile,1,at(".dbf",::cDataFile)-1)
	::cMemoFile += ::cMemoType
	
	If !file(::cMemoFile)
		::_SetError(-3,"Memo file ["+::cMemoFile+"] NOT FOUND.")
		::Close()
		Return .F. 
	Endif

	// Abre o arquivo MEMO 
	::nHMemo := FOpen(::cMemoFile)
    
	If ::nHMemo == -1
		::_SetError(-4,"Open Error - File ["+::cMemoFile+"] - FERROR "+cValToChar(Ferror()))
		::Close()
		Return .F. 
	Endif
	
Endif

If !::_ReadStruct()
	// Em caso de falha na leitura da estrutura 
	FClose(::nHData)
	::nHData := -1
	IF ::nHMemo <> -1
		FClose(::nHMemo)
		::nHMemo := -1
	Endif
	Return .F.
Endif

// Cria o array de campos do registro atual 
::aRecord := Array(::nFldCount)

// Vai para o topo do arquivo 
// e Lê o primeiro registro físico 
::DBGoTop()

Return .T.

Cada registro lido é armazenado na memória na propriedade aRecord. O método Fieldget() busca dos dados de cada coluna do registro atual pelo numero da coluna usando como índice do array. Caso a tabela não tenha dados, um método interno chamado _ClearRecord() preenche o array com os campos vazios, baseado nos tipos de dados da estrutura. Um campo caractere será preenchido com espaços em branco, um campo data terá uma data vazia, um tipo numérico será zero, um booleano será .F. (Falso) e um campo memo será uma string vazia. Em caso de erro de abertura, o método OPEN retorna .F., e você pode recuperar informações adicionais sobre o erro usando o método GetErrorStr() por exemplo.

Leitura e reposicionamento de registros

Podemos usar o método DbSkip() para avançar para registros para trás ( em direção ao início do arquivo) ou para frente (em direção ao final do arquivo). Caso cada um destes limites seja atingido (BOF ou EOF), os métodos de mesmo nome vão retornar .T. . Em caso de BOF(), é mantido o posicionamento no primeiro registro da tabela. Caso você atingiu EOF(), o registro atual é limpo da memória. Você também pode ir para o primeiro ou para o último registro da tabela, usando os métodos DbGoTop() e DBGoBottom() — respecitvamente — e ainda pode posicionar diretamente eu um registro a partir do RECNO — Caso um registro quer não exista fisicamente tente ser endereçado, você vai para EOF().

Desempenho

A operação mais executada na navegação entre registros é a leitura do conteúdo do registro atual e a recuperação dos dados. Por isso escolhi usar uma propriedade do tipo Array para armazenar na memória apenas a linha atual posicionada na tabela (::aRecord), e a recuperação dos dados é feita pelo número da posição da coluna na estrutura da tabela. Logo, se você tem um processamento em LOOP e vai precisar recuperar várias vezes a mesma coluna em cada loop, é muito mais performático primeiro descobrir a posição dos campos desejados e armazenar em memória, para então acessar cada campo pelo número da coluna.

Uma leitura de registro, feita pelo método interno _ReadRecord() calcula o offset de arquivo do RECNO atual, reposiciona o ponteiro para aquele offset, e lê em uma string o conteúdo de uma linha ou um registro na memória. Então, em um loop posterior, partindo do array com a estrutura da tabela, realizamos as conversões necessárias para alimentar o array de campos (::aRecord).

Método interno _ReadRecord

METHOD _ReadRecord() CLASS ZDBFTABLE 
Local cTipo , nTam , cValue
Local nBuffPos := 2 , nI
Local cRecord := '' , nOffset

// ----------------------------------------
// Calcula o offset do registro atual baseado no RECNO

nOffset := ::nDataPos 
nOffset += (::nRecno * ::nRecLength)
nOffset -= ::nRecLength

// Posiciona o arquivo de dados no offset do registro 
FSeek(::nHData , nOffset )

// Lê o registro do offset atual 
FRead(::nHData , @cRecord , ::nRecLength )

// Primeiro byte = Flag de deletato
// Pode ser " " (espaço)    registro ativo 
//          "*" (asterisco) registro deletado 
   
::lDeleted := ( left(cRecord,1) = '*' )

// Agora lê os demais campos e coloca no ::aRecord

For nI := 1 to ::nFldCount

	cTipo := ::aStruct[nI][2]
	nTam  := ::aStruct[nI][3]
	cValue := substr(cRecord,nBuffPos,nTam)

	If cTipo == 'C'
		::aRecord[nI] := cValue
		nBuffPos += nTam
	ElseIf cTipo == 'N'
		::aRecord[nI] := val(cValue)
		nBuffPos += nTam
	ElseIf cTipo == 'D'
		// Por hora le como caractere
		::aRecord[nI] := cValue
		nBuffPos += nTam
	ElseIf cTipo == 'L'
		::aRecord[nI] := ( cValue=='T' )
		nBuffPos += nTam
	ElseIf cTipo == 'M'
		// Recupera o Offset do campo no DBT/FPT
		::aRecord[nI] := val(cValue)
		nBuffPos += nTam
	Endif
  
Next

// Reseta flags de BOF e EOF 
::lBOF := .F. 
::lEOF := .F. 

Return .T.

Como podemos ver no fonte, o tratamento das partes que compõe o buffer do registro atual em memória — armazenada em cRecord — é tratada extraindo o dado em sequência, desmontando o buffer baseado no tipo e tamanho de cada campo. O Flag de registro deletado é o primeiro byte do campo, e seu resultado é armazenado na propriedade ::lDeleted, que pode ser consultada pela aplicação através do método Deleted()

O que são estes montes de “::” ?

Para quem ainda não está familiarizado com orientação a objetos em AdvPL, a sequência de “::” é um #translate, ou “açúcar sintático” — ela é traduzida de “::” para “self:”, e apenas facilita a leitura do código. Dentro de um método da classe, você faz referência a uma variável que é propriedade da classe usando “self:variavel”. Para o código ficar mais limpo, você usa “::variavel”. Para quem programa em C++, o “self” do AdvPL é o equivalente ao “this“.

Conclusão

Por hora, conclusão mesmo vai ser quando essa classe conseguir usar um índice. Aí sim ela será promovida a classe Chuck Norris Certified !!! Por hora, estou preparando para o próximo post um suporte a filtro de registros — De forma similar a um DbSetFilter(), usando uma expressão AdvPL para determinar a visibilidade lógica na navegação de registros.

Agradeço a todos pela audiência, e lhes desejo TERABYTES DE SUCESSO !!!

Referências

 

 

Lendo DBF em AdvPL — Sem DRIVER ou RDD

Introdução

Com o passar do tempo, a utilização de tabelas em formato ISAM, como o DBF e o c-TREE, estão sendo substituídas por uso de tabelas temporárias no Banco de Dados principal das aplicações AdvPL. E, por questões tecnológicas, a utilização do Protheus Server 64 Bits não suporta mais abertura e manutenção de arquivos no formato DBF. Neste post eu compartilho uma forma de ler conteúdo de arquivos DBF em AdvPL, independente do sistema operacional (Windows / Linux ) e independente da Build do Application Server (32 ou 64 bits).

Driver DBF em AdvPL

Em breve será lançada uma build nova do Protheus 12 (ERP Microsiga) chamada “Lobo Guará”, para Windows 64 Bits e Linux 64, não devendo mais haver Build 32 bits, e o novo servidor não terá mais suporte ao uso de arquivos DBF.

Muitas vezes, existem integrações com sistemas legados que ainda usam arquivos no formato DBF. Eu mesmo tenho várias modelagens de sistemas antigos — coisas de mais de 20 anos — todas criadas com o driver DBFNTX do Clipper 5.x, campos memo em formato DBT. Após estudar um pouco a estrutura interna do arquivo DBF, e dos campos MEMO (DBT e FPT), resolvi construir uma classe em AdvPL que seja capaz de abrir e ler os dados de um arquivo DBF usando as funções de baixo nível  de arquivo do AdvPL (FOpen, FRead,FSeek).

Classe ZDBFTABLE

A implementação da classe foi feita partindo da premissa que todas as operações de leitura da tabela sejam feitas através de métodos, que foram publicados com o mesmo nome das funções de baixo nível de arquivo de dados do AdvPL — por exemplo DbSkip(), DBGotop(), FieldGet().

A primeira versão operacional suporta leitura de arquivos nos formatos dBASE III/Foxpro/Foxbase+/Clipper, com campos MEMO em arquivo DBT e/ou FPT. Não há suporte a índices ou filtros, todos os registros são lidos — inclusive os registros deletados logicamente (ou seja, marcados para deleção). Os dados são lidos na ordem física de registro — ou Recno(). Os métodos de navegação ISAM e de leitura implementados são:

  • DBGoBottom() — Posiciona no último registro da tabela.
  • DBGoTop() — Posiciona no primeiro registro da tabela
  • DBSkip(nRecs)  — Avança ou volta um ou mas registros
  • DBGoto(nRecno) — Posiciona direto em um registro pelo número.

O construtor do objeto — método New() — recebe como parâmetro o nome do arquivo DBF no disco a ser aberto. O arquivo é aberto apenas para leitura usando o método Open(), que já posiciona a tabela no primeiro registro, e fechado usando o método Close().  A recuperação de dados e de status dos registros é feita usando os métodos:

  • FieldGet(nCol) — Recupera o valor de uma coluna da linha atual
  • FieldPos(cCol) — Recupera o número da coluna a partir do nome
  • EOF() — Retorna .T. caso o ponteiro de registros não esteja apontando para um registro. Isto normalmente acontece quando a tabela está vazia ou quando houve a movimentação de ponteiro para próximo registro, e você já estava no último registro da tabela.
  • BOF() — Retorna .T. caso a ultima movimentação de registro tentou ler antes do primeiro registro da tabela
  • Recno() — Retorna o número do registro atual posicionado da tabela. Caso ela esteja em EOF(), é retornado o número do último registro mais um.
  • Deleted() — Retorna .T. caso o registro atualmente posicionado esteja deletado.

Outros métodos para recuperar propriedades da tabela são:

  • LastRec() e/ou RecCount() — Retornam o número do último registro da tabela. Como os registros no DBF são numerados a partir do número 1, ele também representa o número total de registros da tabela.
  • DBStruct() — Retorna um Array contendo a estrutura do arquivo DBF, com 4 colunas: Nome do campo, Tipo do Campo, Tamanho do campo e quantidade de decimais.
  • Header() — Retorna o tamanho em bytes do cabeçalho do arquivo DBF
  • RecSize() — Retorna o tamanho em bytes ocupado no arquivo DBF para uma linha de dados — Equivale a soma do tamanho de todas as colunas mais um (Coluna interna reservada para marcar um registro como deletado.)
  • LUpdate() — Retorna a data da última atualização de registro do arquivo DBF, gravada no Cabeçalho do arquivo.

Fonte de Exemplo

Sem maiores delongas, segue abaixo o fonte “LeUmDbf.prw”, com o exemplo de uso da classe ZDBFTable(). Ela abre uma tabela DBF, mostra informações sobre a tabela e a estrutura de dados, e o primeiro registro ( RECNO 1 ) da tabela no log de console.

#include  'protheus.ch'

User Function LeUmDBF()
Local oDBF
Local nCampos, aStru
Local cFile := '\system\sx3990.dbf'
Local nI

// Cria o objeto para leitura do arquivo 
oDBF := ZDBFTABLE():New(cFile)

// Faz a abertura. Em caso de falha, 
// aborta a aplicação obtendo os detalhes do erro
// usando o método GetErrorStr()
If !oDBF:Open()
	UserException( oDBF:GetErrorStr() )
Endif

// Mostra no log de console alguns dados sobre o arquivo aberto 
conout(replicate('=' ,79))
conout("Database File Name : " + cFile )
conout("Database File Type : " + oDBF:GetDBType()+ " => "+ oDBF:GetDBTypeStr())
conout("Last Update .......: " + dtoc(oDBF:LUpdate()) )
conout("Record Count ......: " + cValToChar(oDBF:RecCount()))
conout("Header Size .......: " + cValToChar(oDBF:Header()))
conout("Record Length......: " + cValToChar(oDBF:RecSize()))

// Recupera a estrutura da tabela 
aStru := oDBF:DbStruct()

// Verifica quantos campos tem a tabela 
nCampos := len(aStru)

// Mostra a estrutura de campos no console
conout("")
conout("--- Table Structure --- ")

For nI := 1 to nCampos
	
	conout("Field ["+aStru[nI][1]+"]"+;          // Nome
          " Type ["+aStru[nI][2]+ "]"+;          // Tipo (CNDLM)
          " Size ["+Str(aStru[nI][3],3)+"]"+;    // Tamanho
          " Dec ["+Str(aStru[nI][4],2)+"]")     // Decimais
	
Next

// Mostra o primeiro registro da tabela, e detalhes do registro
// Caso a tabela esteja vazia, BOF() e EOF() retornam .T. 

conout(replicate('-' ,79))
conout("RECNO() ...... "+cValToChar(oDBF:Recno()) )
conout("BOF() ........ "+cValToChar(oDBF:Bof()) )
conout("EOF() ........ "+cValToChar(oDBF:Eof()) )
conout("DELETED() .... "+cValToChar(oDBF:Deleted()) )
conout("")
For nI := 1 to len(aStru)
	conout(oDBF:FieldName(nI)+" => " +;
               "["+cValToChar(oDBF:Fieldget(nI))+"]" )
Next
conout("")

// Fecha a tabela 
oDBF:Close()

// Limpa / Libera o Objeto 
FreeObj(oDBF)

Return

Desempenho

Não é uma API em C/C++, mas o desempenho não ficou nada mal. Usando uma tabela de 44 colunas e 1660 bytes por registro (ou linha), o programa leu sequencialmente em média 5 mil linhas por segundo. Este desempenho pode ficar ate dez vezes mais lento, quando o arquivo a ser lido estiver sendo acessado por um compartilhamento de rede por exemplo — não têm almoço grátis.

Quer usar esta classe ?

Para usar esta classe, pegue o código fonte atualizado no GITHUB — arquivo “zDBFTable.prw” – – e fique a vontade. Apenas mantenha o cabeçalho do fonte com as informações da autoria do código. Simples assim 😀

Como ela acessa o arquivo diretamente no disco, ela funciona para qualquer Build do Protheus, 32 e 64 Bits, Windows e/ou Linux, e como as funções utilizadas são arrox-com-feijão, esse fonte pode ser usado em versões anteriores do Protheus Server, lançadas a pelo menos nos últimos 10 anos.

Conclusão

Para uma primeira versão de leitura, eu acho que já dá pra fazer bastante coisa. Ao usar a classe, use sempre o acesso pelos métodos, e não diretamente as propriedades, e também não use os métodos de uso interno — prefixados com “_”. Desta forma é possível eu continuar evoluindo esta classe sem impacto nos fontes que a utilizam. O fonte da classe já está no GITHUB, no próximo post nós vamos entrar dentro dos detalhes da implementação do DBF Driver em AdvPL!

Desejo a todos um ótimo uso da classe ZDBFTABLE, e TERABYTES DE SUCESSO !!!

Referências

 

Glossário do AdvPL – Parte 01

Intodução

“Um glossário é uma lista alfabética de termos de um determinado domínio de conhecimento com a definição destes termos. Tradicionalmente, um glossário aparece no final de um livro e inclui termos citados que o livro introduz ao leitor ou são incomuns.” — Wikipédia

O que mais temos no mundo da informática são terminologias, siglas, acrônimos, etc. Alguns deles são comuns em desenvolvimento de software — independente da plataforma — e outros são específicos ou relacionados ao contexto de um sistema específico.  A ideia do glossário AdvPL é dar uma breve explicação sobre cada palavra ou terminologia, onde existem termos genéricos, e dentre eles alguns aplicam-se ao Protheus, ao ERP Microsiga e a linguagem AdvPL. E, este Glossário está em ordem “Analfabética” 😀

Glossário

AdvPL — Linguagem de programação com sintaxe xBase – como o CA Clipper — desenvolvida e implementada pela TOTVS em um ambiente de desenvolvimento Client-Server — chamado por Protheus — usada principalmente pela linha de produtos do ERP Microsiga.

Variável local — (ou variável de escopo local) É uma variável declarada dentro do corpo de uma função ou método através do statement “Local“, que indica que esta variável somente pode ser endereçada (ou referenciada) dentro do corpo da função ou método.

Variável privada — (ou variável de escopo privado) É uma variável declarada com o statement “Private“, ou inicializada através de atribuição de conteúdo sem declaração, dentro de uma função ou método. Ela torna-se visível para a função ou método atuais em tempo de execução a partir do momento que é declarada ou inicializada, e também é visível para funções ou métodos chamados no AdvPL a partir da função atual que a declarou.

Variável estática — (ou variável de escopo estático) É uma variável declarada explicitamente usando o statement “Static” dentro de um fonte AdvPL, fora do corpo de qualquer função ou método. Esta variável é visível e pode ser endereçada apenas por funções ou métodos declarados no mesmo arquivo-fonte, e criada em tempo de execução uma vez que qualquer função ou método daquele fonte seja chamado pela primeira vez durante a execução da aplicação.

Variável pública — (ou variável de escopo público) É uma variável explicitamente declarada com o statement “Public“, e uma vez declarada, ela torna-se visível a partir daquele momento para qualquer função ou método executado a parir de então, em qualquer ponto da pilha de chamadas (ou Stack).

Função estática — Função declarada explicitamente em um fonte AdvPL, usando o statement “STATIC Function“,somente é visível e pode ser chamada por funções ou métodos de classe declarados no mesmo arquivo-fonte AdvPL.

Função pública — Qualquer função não estática declarada no AdvPL — Inclusive uma USER Function — é uma função pública, sendo visível e passível de ser chamada qualquer outro programa AdvPL compilado no mesmo RPO — Repositório de Objetos.

Bloco de Código (Codeblock) — É o nome dado a um tipo de dado no AdvPL, usado para armazenar uma função anônima em uma variável, que pode ser passada e executada em outros pontos do código, fazendo referências inclusive a variáveis de escopo local.

RPO — Acrônimo de Repository of Protheus Objects, (também conhecido simplesmente por “Repositório”), é o termo usado no Protheus para identificar o arquivo ou container gerado pela compilação dos códigos AdvPL, usado para guardar o código -fonte AdvPL compilado.

IDE — Acrônimo de Integrated Development Environment, foi a primeira versão da ferramenta do ambiente tecnológico do Protheus que permite ao desenvolvedor de software programar, compilar e fazer debug do código AdvPL.

TDS — Acrônimo de Totvs Developer Studio, é a versão nova — evolução do IDE, baseada no Eclipse — do ambiente de desenvolvimento AdvPL. Possui também funcionalidades de monitoramento, gerenciamento de ambiente, Profiler, Replay e outras novidades.

LOG – Trata-se de um registro de atividades relevantes de um programa. Normalmente o servidor de aplicação AdvPL ( ou Application Server, ou Protheus Server), o DBAccess e o próprio ERP possuem registro de LOG. Estes registros podem ser gravados em arquivos com nome pré-determinado pela aplicação em formato TEXTO no sistema de arquivos, ou em algum SGDB (Banco de Dados).

ERRO – A palavra em si pode ter um significado realmente amplo. Não fazer algo que deveria ser feito, ou fazer pela metade, ou não fazer direito, conceitualmente caracteriza um “erro”. Porém, em AdvPL, convencionamos que “ERRO é uma ocorrência sistêmica não tratada, que interrompe e impede um processamento, gerando um LOG”. 

ACCESS VIOLATION — Forma reduzida da expressão “Memory Access Violation“, normalmente registrada pelo sistema operacional logo após a finalização anormal (ou QUEDA) de um serviço ou programa, devido a uma tentativa de acesso de um endereço inválido de memória.

SEGMENT FAULT —  Exatamente a mesma coisa que um “Access Violation”, porém esta ocorrência é emitida no Linux.

SGBD – Acrônimo de “Sistema Gerenciador de Banco de Dados“, é o nome dado a uma aplicação que gerencia, armazena e consulta informações — popularmente conhecido como apenas Banco de Dados.

Passar por Referência — Chamar uma função ou método usando o operador “@” antes da variável, para sinalizar ao Runtime do AdvPL que o parâmetro recebido pela função não é uma cópia — ou clone — da variável, mas sim uma referência da variável do escopo da chamada. Isto permite que uma função possa atribuir um novo valor na variável declarada na função como parâmetro, e esta atribuição na verdade seja feita na variável informada na chamada da função.

Referência implícita — Comportamento da passagem de parâmetros Advpl do tipo “B” (Codeblock), “O” (Objeto) e “A” (Array), onde naturalmente a variável é passada como argumento para a função por referência, sem o uso do operador “@“, por causa do conteúdo da variável.

Variável Global — Nome dado a um identificador, para permitir múltiplos programas em execução no mesmo Application Server a ler e gravar valores em uma mesma área de memória compartilhada. Em AdvPL, não existe um tipo global nativo declarado, as variáveis globais nomeadas são acessadas através de funções criadas para este fim.

Cache — Nome dado ao procedimento de usar uma área de memória (variável local, global, ou mesmo um serviço dedicado acessado por API) para armazenar identificadores e conteúdos previamente lidos — normalmente de um Banco de Dados — para que, o acesso a este valor seja feito a partir do último valor armazenado na memória — ao invés de realizar mais um acesso ao SGDB, que fatalmente terá mais custo de tempo e recurso do que pegar o valor da memória.

Leitura suja — Nome dado a um comportamento, normalmente do Banco de Dados, de permitir que uma consulta qualquer leia dados vindos de uma transação que ainda não fez commit.

Algoritmo recursivo — Lógica de programação de uma rotina ou sub-rotina, que durante o processamento, realiza chamadas para a própria rotina. Por exemplo, fazer uma busca ou varredura em todos os elementos de uma árvore. A função recebe o nó principal da árvore como parâmetro, e faz a varredura dos nós filhos, fazendo um loop e chamando novamente a função de busca, passando em cada chamada nó em questão. Uma função recursiva normalmente possui uma forma não recursiva de ser escrita.

JOB — Nome dado ao início de um processo sem dependência ou amarração de nenhuma interface.

RPC — Acrônimo de Remote Procedure Call, um recurso disponibilizado entre sistemas operacionais para permitir uma chamada de execução de aplicação em outra máquina. Em AdvPL, existe uma interface nativa para permitir um processo a estabelecer uma conexão sem interface com um outro Protheus Server, e executar chamadas de funções no ambiente alvo da conexão TCP, que pode estar no mesmo equipamento servidor ou em outra máquina.

LOOP – Ação de executar um trecho do código por repetidas vezes — chamadas iterações — até que uma condição pré-estabelecida se apresente, fazendo a aplicação sair do LOOP e continuar sua execução.

LOOP INFINITO — Devido a um erro de lógica na construção da rotina, ou um comportamento inesperado ou anormal de alguma instrução ou componente dentro do trecho de código em LOOP, a condição se saída nunca acontece, fazendo o programa executar indefinidamente aquele trecho de código, até que a aplicação seja derrubada ou finalizada externamente.

Application Server Nome pelo qual é conhecido o servidor de aplicação AdvPL — responsável por atender a conexões da aplicação SmartClient e executar código AdvPL. Também chamado de “Protheus Server”.

SmartClient — Aplicação Cliente do AdvPL, responsável por executar programas AdvPL com interface gráfica nativa. Também conhecida de versões anteriores como “Protheus Remote”.

PAU — Amigo(a), um sistema não têm “pau”. Ele pode ter uma não conformidade, um comportamento não esperado, uma falha na lógica de implementação. Pau é um pedaço de madeira 😉

Lentidão — Normalmente diz-se que uma função ou oma tarefa apresenta lentidão (ou mais elegantemente, perda de desempenho ou desempenho inadequado) quando a tarefa não é executada em um tempo aceitável perante a complexidade da tarefa e o volume de dados. Uma medida de desempenho baseada em tempo é sempre relativa, isto é, quando comparamos a tarefa X sendo executada na condição X e no ambiente Y, a mesma tarefa X em outro ambiente, ou com outra condição, ou com após uma atualização de um dos componentes do ambiente Y ( RPO de Produto, RPO de LIb/Framework, Binário, etc) apresenta desempenho pior.

Binário — O significado é amplo, mas aplicado ao AdvPL, nomeamos como “Binário” qualquer componente executável ou de link dinâmico (EXE e/ou DLL/SO). O Protheus Server, o Smartclient, o DBAccess, são componentes binários da tecnologia Protheus. Como existem vários tipos de atualização — RPO, Patches de RPO , Binários e configurações, quando alguém diz que “atualizou o ambiente”, é necessário saber e informar quais componentes foram atualizados e quais eram as versões e builds anterior e atual para facilitar os processos na identificação por não conformidades na aplicação.

Update sem Where — Trata-se da aplicação direta em um banco de dados relacional (SGBD) de uma instrução que atualiza uma ou mais colunas de uma tabela, onde faltou especificar uma condição para que apenas alguns — e não TODOS —  registros fossem atualizados. É considerado um erro primário, que quando feito em um banco de dados oficial (ou em produção), pode causar sérios transtornos — pois você vai ter que restaurar uma cópia recente desta tabela de um backup para obter de volta os dados antes da atualização.

Conclusão

O Glossário é um tópico sem fim, conforme aparecerem mais terminologias a serem enumeradas, novos posts vão saindo.

Desejo a todos novamente TERABYTES DE SUCESSO !!! 

Referências

Manipulação de arquivos em AdvPL – Parte 02

Introdução

No post anterior, Manipulação de arquivos em AdvPL – Parte 01, vimos em detalhes os comportamentos das funções de baixo nível de arquivo, acompanhados de um exemplo de manipulação simples de um arquivo texto. Neste post, vamos rever alguns detalhes, e ver como as funções de baixo nível de arquivo do AdvPL foram adaptadas para o Linux e MAC OS.

Vale a pena lembrar de novo

  • As funções FCreate() e FOpen() retornam um identificador numérico, chamado de Handler do arquivo. Ele é usado como parâmetro para as demais funções. Se o número retornado for -1 (menos um), isto indica uma falha na criação e/ou abertura da tabela, respectivamente.
  • Caso o nome do arquivo informado inicie com uma unidade de disco, o Application Server assume que o arquivo está na estação onde o SmartClient está sendo executado. Se você criou um JOB — processo sem interface — este JOB somente consegue abrir arquivos a partir do RootPath do ambiente no Protheus Server, onde o path deve começar com uma “\” barra inversa.

Novas informações

  • Um Handler de arquivo no AdvPL não pode ser compartilhado entre processos. Isto é, o handler retornado na criação ou abertura da tabela é válido apenas dentro do processo (ou Thread) que obteve o Handler.
  • Um Handler de arquivo válido obtido pelas funções FCreate() e/ou FOpen() mantém o arquivo aberto até que ele seja explicitamente fechado pela função FClose(). Após fechar o arquivo, este Handler deve ser descartado, pois ene não é mais válido após o fechamento da tabela.
  • Um Handler de arquivo não fechado explicitamente pelo programa AdvPL somente será fechado automaticamente pelo Application Server no término do processo / Thread — Seja ela finalizada com sucesso ou com erro de execução.

Comportamentos em ambientes Multi-Plataforma

Atualmente o Application Server é homologado para Windows e Linux, e o SmartClient é homologado para Windows, Linux e Mac OS. Porém, o sistema de arquivos destes sistemas operacionais difere em alguns pontos em relação ao Windows.

  • Não existe o conceito ou a implementação de letras para designar unidades de disco.
  • A barra separadoras de diretórios utilizada é a barra para a direita “/”, enquanto no Windows é usada a barra para a esquerda “\”.
  • O nome dos arquivos é case-sensitive. Isto é, chamou o arquivo de “Jose.txt”, e tentou abrir como “jose.TXT”, o sistema operacional retorna erro de “Arquivo não encontrado.”
  • O sistema de arquivos no Linux não prevê o tratamento de acesso exclusivo a um determinado arquivo por um determinado processo.
  • O compartilhamento de arquivos via rede é feito utilizando NFS, diferente do compartilhamento de arquivos do Windows, Você não tem nenhum prefixo ou identificador informando que seu arquivo está sendo acessado pela rede. O caminho do arquivo é escrito da mesma forma, porém a partir da pasta que deve ser acessada pela rede,deve ser feito no Linux e MAC OS um “ponto de montagem”, que indica ao sistema operacional que a partir daquela pasta, os arquivos estão fisicamente em outra máquina.

Devido a estas diferenças, foram adotadas algumas convenções para o sistema de arquivos do AdvPL no Linux.

  • Todos os arquivos do ambiente, a partir do RootPath, devem estar em capitulação baixa — isto é, em letras minúsculas — tanto no nome como na extensão, inclusive todas as pastas e/ou diretórios do ambiente devem estar com letras minúsculas. Mesmo que o seu fonte AdvPL passe como parâmetro para as funções de arquivo um nome com letras maiúsculas, internamente ele será convertido para minúsculas caso a plataforma seja Unix-Like (Linux).
  • No caso de utilização de serviços de Application Server em máquinas diferentes, para balanceamento de carga, por exemplo, o nome do RootPath do ambiente deve ser o mesmo para todas as instâncias, em todas as máquinas. Por exemplo, na máquina onde são gravados os arquivos  dentro o RootPath do ambiente, o RootPath é “/totvs/protheus12/envtop”. Este deve ser também o RootPath do ambiente em outras máquinas. A diferença para uma máquina “Slave” é que a pasta “envtop” vai existir como um ponto de montagem Client de compartilhamento de rede NFS (Network File System), que aponta para a pasta “/totvs/protheus12/envtop” da máquina principal.
  • No caso da utilização de mais de um serviço do Protheus compartilhando o mesmo RootPath de ambiente, mesmo que estes serviços estejam na mesma máquina, ‘é necessário haver a configuração de um “LockServer Protheus” — Configurar um serviço dedicado a obtenção de bloqueios exclusivos (Locks() para criação e abertura de arquivos, onde todos os Serviços de Protheus que acessam este RootPath devem apontar para um único LockServer.
  • A necessidade de uso de um Application Server Protheus configurado como LockServer no Linux existe para justamente emular o controle de acesso exclusivo a arquivos, que o Linux não possui nativamente, mas vários programas em AdvPL contam com este comportamento.
  • Mesmo que em Linux eu não tenha o conceito de “unidade de disco”, a forma de eu indicar que um arquivo deve ser aberto pelo SmartClient continua a mesma — informar uma unidade de disco no path do arquivo a ser acessado. Caso o SmartClient em uso seja Linux ou Mac OS, a unidade de disco informada será ignorada, e o arquivo será localizado pelo path e nome informados.
  • Mesmo que você tenha escrito o seu programa AdvPL usando as barras separadoras de arquivo para a esquerda “\”, as funções básicas da linguagem que realizam acesso a disco foram preparadas para converter automaticamente para “/”  caso a plataforma em uso seja Unix-like (como Linux e MAC OS).
  • Caso exista alguma diferença não tratada, ou seja necessário um tratamento diferenciado durante a execução de uma aplicação AdvPL, onde exista mudança ou indisponibilidade do recurso em algum sistema operacional ou plataforma, existem funções de diagnóstico no AdvPL que retornam por exemplo se o Application Server é Linux ou Windows, e qual é o SmartClient que está sendo usado — Linux, Windows ou MAX OS. Veja informações adicionais das referências, no final do post.
  • A lista de códigos de erro da função FError() possui uma numeração própria, diferente dos códigos de erro de manipulação de arquivos do Sistema Operacional em questão. Essa lista está documentada na própria função FError() na TDN.

Fonte de Testes

No post anterior, para cada função testada foi feita uma parte de um código, realizando as operações em sequência. Veja abaixo o fonte de testes e demonstração das funções de baixo nível de arquivo unificado — fonte tstfile.prw

#include 'protheus.ch'
#include 'fileio.ch'

USER Function TSTFILE()
Local nHnd 
Local cFile := '\temp\teste.txt'
Local cLine , nTamFile
Local cNewLine , nRead, nWrote

// Cria o arquivo 
// Automaticamente o arquivo é aberto em modo exclusivo para gravação
nHnd := fCreate(cFile)

If nHnd == -1
  MsgStop("Falha ao criar arquivo ["+cFile+"]","FERROR "+cValToChar(fError()))
  Return
Endif

// Cria uma linha em memória
cLine := "Olá sistema de arquivos" + CRLF

// Grava três linhas iguais, com 25 bytes cada 
fWrite(nHnd,cLine)
fWrite(nHnd,cLine)
fWrite(nHnd,cLine)

// Fecha o arquivo 
fClose(nHnd)

// Abre novamente para leitura e ecrita em modo exclusivo 
nHnd := fOpen(cFile,FO_READWRITE + FO_EXCLUSIVE )
If nHnd == -1
  MsgStop("Falha ao abrir ["+cFile+"]","Ferror " + cValToChar(fError()) )
  Return
Endif

// Identifica o tamanho do arquivo 
nTamFile := fSeek(nHnd,0,FS_END)
fSeek(nHnd,0,FS_SET)

conout("Tamanho do Arquivo = "+cValToChaR(nTamFile)+" byte(s)." )

// Lê a primeira linha do arquivo 
cBuffer := ""
nRead := FRead( nHnd, @cBuffer, 25 )
conout("Byte(s) lido(s) : "+cValToChar(nRead))

If nRead < 25
  MsgStop("Falha na leitura da primeira linha.","Ferror "+cValToChar(ferror()))
  Return
Endif

// Agora vamos trocar a segunda linha 
cNewLine := replicate('-',23)

nWrote := fWrite(nHnd , cNewLine )
conout("Byte(s) gravado(s) : "+cValToChar(nWrote))

If nWrote < 23
  MsgStop("Falha na gravação da segunda linha.","Ferror "+cValToChar(ferror()))
  Return
Endif

// fecha o arquivo 
fClose(nHnd)

Return

Este fonte está disponível para download no GITHUB.

Conclusão

Com este post, passamos a abranger praticamente todos os detalhes da implementação de baixo nível de arquivos do AdvPL. Ainda não testei este fonte no Linux, porém dada a natureza da implementação, o comportamento esperado é o mesmo.

Novamente agradeço a audiência, e desejo a todos TERABYTES DE SUCESSO 😀

Referências

 

 

 

 

 

 

Protheus – do AP5 ao P11

Introdução

Desde o surgimento do Advanced Protheus 5.07 em 1999, até o Protheus 11, o produto ganhou novos módulos, funcionalidades e recursos. Desde implementações e extensões da Linguagem AdvPL, até o FrameWork AdvPL com MVC. Porém, até o momento não ouvi falar de nenhuma análise técnica entre as versões. Então, após ressuscitar alguns CDs de instalação antigos, e fazer algumas medições, eu cheguei a alguns números aproximados muito, muito interessantes.

Do Protheus 5 ao Protheus 11

Informações por Versão Ap5 Ap6 Ap7 Mp8 P10 P11
Tabelas (SX2) 991 1394 1681 1677 3700 6368
Índices (SIX) 2190 3124 4107 4249 8770 14868
Campos (SX3) 16306 22042 27087 27545 59420 103951
Campos Virtuais 1281 1746 2261 2104 5284 8890
Maior numero de campos 168 169 176 163 200 267
Maior tamanho de Registro 1794 3761 3761 3761 4022 4909
Tabela com mais índices 14 15 18 26 20 28
Índice com mais Campos 12 12 13 14 14 14
Índice com maior chave 152 203 212 214 232 505
Tamanho do RPO (MB) 28 44 44 36 60 174
Funções no RPO 15104 21665 24601 26177 46026 79811
Classes no RPO 57 57 61 287 603 2451
Resources RPO 2445 3604 4107 4455 7975 15928
Entrada no ERP – SIGAFAT
Memória Consumida (MB) 10,63 10,47 12,48 12,06 15,74 49,22
Chamadas de funções básicas do AdvPl 34690 35682 44170 36615 63844 377300
Chamadas de funções do Repositório 8423 8598 9128 3944 6444 53066
Chamadas de Operadores 514403 576713 614246 363848 589606 4287337
Memoria para Carga de Fontes (MB) 1,8 2,3 2,41 2,91 4,8 14,9
Fontes Carregados 38 44 47 52 72 215

Analisando por cima os dados encontrados, podemos ver que do Protheus 10 para  Protheus 11 houve um salto bem maior em relação às versões anteriores. Houveram muitas implementações no ERP, módulos novos, MVC, ferramentas, integrações e controles adicionais.

 

Ao migrar do P10 para o P11, muitos clientes precisaram refazer o “sizing” de máquina. Na verdade, sempre ao migrar de versão um “sizing” de máquina deve ser avaliado, pois dependendo do tamanho do ambiente, um aumento de consumo de memoria ou CPU de 10 % pode representar um número grande em valores absolutos. No caso do P10 para o P11, o salto foi muito grande, mesmo quem usava o P10 com uma folga razoável de hardware precisou mexer no parque de máquinas para escalar o ambiente.

Os dados obtidos com esta pesquisa são meramente informativos, e foram obtidos em sua maioria com as versões de repositório original do CD, e dicionários sem customização. Foi um trabalho muito gostoso rever as telas de todas as versões de produto que eu trabalhei direta ou indiretamente nos últimos 17 anos, funcionando em um Windows 10 😀

Evolução da Interface

A interface da aplicação ERP foi evoluindo e ganhando novos layouts e funcionalidades ao longo das versões do ERP Microsiga. No TDN, têm um post muito interessante, com vários prints de interface, de todas as versões do ERP Microsiga, desde as primeiras versões Windows (antes do Protheus), até o P12 !! Vale a pena conferir, você pode acessar pelo link http://tdn.totvs.com/display/framework/Microsiga+Protheus

Conclusão

Com o crescimento da plataforma de negócios e de todo o parque de soluções rodando em AdvPl em cima da plataforma TotvsTec, o desafio constante de aprimorar a plataforma e implementar recursos para tornar viável a implementação e operacionalização de soluções cada vez maiores faz parte da rotina diária de todos os colaboradores da TOTVS 😀

Desejo a todos TERABYTES de sucesso !!!

Abraços e Saudações 😉 

 

 

Primeiro aniversário do “Tudo em AdvPL”

Em 21 de Novembro de 2015, o Blog “Tudo em AdvPL” completou um ano de existência, contados do primeiro “post”. Eu sou apaixonado por tecnologia e programação desde que eu entendi o conceito da palavra (e do “Byte”), e eu gostaria de compartilhar isso com o mundo, onde existem mais pessoas que, em diferentes graus e intensidades, também têm afinidade com este mundo mágico da Tecnologia da Informação. Depois de acompanhar de perto a evolução da Internet nos últimos 20 anos, decidi que estava mais que na hora de arregaçar as mangas e fazer algo a respeito.

Hoje só tenho a agradecer a vocês, leitores e visitantes do “Tudo em AdvPL”, que buscam por mais informações e conhecimentos, que comentam um post com elogios, dúvidas ou sugestões de abordagem de outros assuntos, e que compartilham com seus amigos nas redes sociais o conteúdo aqui disponível 😉

Mais uma vez, obrigado pela audiência, e desejo a todos TERABYTES de SUCESSO 😀

“Toda a grande caminhada começa com um pequeno passo” — BUDA

filme-06-020

“Viagem ao Oiapoque (2006), BR-156” – foto por Júlio Wittwer.

20151910 Dia do Profissional de TI

Dia do Profissional de TI

Quase que a data passou em branco … estava avaliando uma ocorrência de não conformidade e comportamento inesperado, outra de perda de desempenho isolada, e projetando as etapas para a realização de um projeto. Fui lembrado pelos colegas que compartilharam imagens (algumas muito engraçadas) no Facebook.

Na verdade, a data comemorativa não é (ainda) oficial, e seu nome original é “Dia do Profissional de Informática“. Mas vem sendo também usada sob o título de “Dia do Profissional de TI“.

Como cada post do blog envolve uma etapa de pesquisa, localizei uma homenagem feita pelo site PTI — Profissionais TI –, postado em nome da equipe de redação do site (acesso na íntegra pelo link http://www.profissionaisti.com.br/2013/10/19-de-outubro-dia-do-profissional-de-ti-parabens/ ), onde eu acredito que a razão da existência da data e a mensagem dedicada aos profissionais da área não poderia ser mais perfeita.

“Estas datas comemorativas, seja qual for, não foram criadas necessariamente para se comemorar algo, mas sim, para nos lembrarmos da importância que aquele determinado profissional exerce no mundo como um todo. A secretária, o pedreiro, o médico, o professor, o engenheiro ou até mesmo “o(a) menino(a) do computador” merecem ser lembrados.

Hoje não entrarei no mérito “reconhecimento da profissão”, até mesmo por que a discussão é longa e dividida entre os que a apoiam veementemente, os que desaprovam totalmente e os que estão em cima do muro. Hoje só quero fazer lembrar de todos que amam atuar na área de tecnologia, independente do nicho de atuação. Quero dar os parabéns aos que, mesmo (ainda) não tendo todo o reconhecimento desejado, literalmente Respiram Informação!”

Faço deles as minhas palavras, desejando a todos que seus caminhos na tecnologia da informação sejam épicos, com muitos desafios, muitas conquistas, muito reconhecimento, muito crescimento, muitos Bytes e Ciclos de Cpu 😀

Aproveito para agradecer a todos os seguidores e leitores eventuais do blog “Tudo em AdvPL” pela sua audiência e carinho, por buscarem conhecimento e nunca estarem satisfeitos, por questionarem sempre se há um melhor caminho a seguir, e por nunca desistirem de achar a resposta.

Um grande abraço a todos, lhes desejo TERABYTES de sucesso 😀