Resta1 em AdvPL

Introdução

Seguindo a linha de jogos em AdvPL, hoje o post traz para vocês a contribuição do desenvolvedor e nobre colega Flávio Luiz Vicco, que fez um porte do jogo “Resta 1” para o AdvPL, usando orientação a objeto em AdvPL.

O Jogo

Resta 1 é um quebra-cabeças, cujo objetivo é retirar todas as peças do tabuleiro, até que reste somente uma. No início do jogo, há 32 peças no tabuleiro, deixando vazia a posição central. Um movimento consiste em pegar uma peça e fazê-la “saltar” sobre outra peça, sempre na horizontal ou na vertical, terminando em um espaço vazio. A peça que foi “saltada” é retirada do tabuleiro. O jogo termina quando não mais é possível fazer nenhum outro movimento. Nesta ocasião, o jogador ganha se restar apenas uma peça no tabuleiro.

resta1

Com apenas 250 linhas a classe com o “core” do jogo foi implementada, e usando menos de 50 linhas, foi implementada a função executora do Jogo. Internamente, o jogo usa a classe tPaintPanel, e a classe executora monta o jogo usando uma caixa de diálogo e um menu superior. Os objetos correspondentes às peças do quebra-cabeça são criados nas respectivas posições, disparando um bloco de código a cada clique do mouse em cada posição. Ao clicar em uma posição ocupada por uma pedra, a respectiva posição é selecionada. Ao clicar em uma nova pedra, esta passa ser a pedra selecionada. Uma vez havendo a seleção de uma pedra, caso você clique em uma posição livre localizada a 2 pedras de distância da pedra selecionada, e entre elas existe uma pedra, a pedra selecionada é movida para a nova posição selecionada, e a pedra entre elas é removida.

Compilando e Executando

Para compilar e executar o Resta Um, basta baixar os fontes e imagens do GitHub, no endereço “https://github.com/siga0984/Resta1-OO“, criar um projeto no IDE/TDS, acrescentar os dois fontes PRW no projeto, acrescentar todas as imagens no projeto como recursos do Projeto, compilar tudo, e chamar a função “U_RESTA1” através do SmartClient.

O Código

Um jogo de tabuleiro, sem adversário ou contagem de tempo, deve ter uma representação em memória do estado do tabuleiro, uma interface que represente graficamente esta representação, e permita ao jogador imputar um movimento, e ser capaz de criticar um movimento inválido, e aplicar um movimento válido na representação do tabuleiro em memória.

Para desempenhar estas tarefas, o jogo foi implementado como uma classe em AdvPL, chamada “TResta1”, onde o construtor “New()” recebe como parâmetro o “container” de interface do Jogo — no nosso caso o objeto da janela de diálogo — e inicia a execução do jogo através do método Activete() da classe “TResta1”.

Na classe TRESTA1, a propriedade “aShape” contem todas as peças do quebra-cabeças. Como o tabuleiro é basicamente a união de um array 3×7 e outro 7×3, compartilhando a área central ( 3×3 ), o array é criado baseado em um loop de 7×7 elementos, onde as posições onde não entraria nenhuma pedra nao são ocupadas não criam nenhum novo elemento. São 16 posicoes ignoradas, 4 de cada quadrante. Sabendo que uma matriz 7×7 tem 49 elementos, e 16 serão ignorados, serão criadas 33 posições no tabuleiro, onde para cada uma será atribuído um status ( vazio / ocupado / selecionado ) e uma imagem correspondente ao estado.

Partindo de um loop 7×7, numerando cada elemento sequencialmente, eu teria as seguintes posições numeradas:

01 02 03 04 05 06 07
08 09 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 32 33 34 35
36 37 38 39 40 41 42
43 44 45 46 47 48 49

Somente serão criados elementos para as posições onde podem haver pedras no quebra-cabeça. Logo, as 4 pedras de cada extremidade deste quadrado não serão usadas — repare que as posições a serem usadas estão em negrito. Cada pedra é criada como um “Shape” dentro do tPaintPanel, e cada uma delas recebe um identificador único, sendo criadas 33 pedras ( ou shapes ), no loop abaixo — vide método Activate() da classe tResta1.

For nX := 1 To 7
 For nY := 1 To 7
  nZ ++
  If !StrZero(nZ,2) $ "01|02|06|07|08|09|13|14|36|37|41|42|43|44|48|49"
   ::Create( ::oTPanel, nX, nY, "P"+StrZero(nZ,2), IIf(nZ==25,0,1) )
  EndIf
 Next nY
Next nX

O evento interessante tratado no jogo é o Click() … A classe tPaintPanel() tem uma propriedade que permite informar um bloco de código, que será disparado quando o usuário da aplicação clicar com o mouse dentro da área do tPaintPanel. Ao ver o fonte, reparamos que o evento em si não recebe nenhum parâmetro, mas isso não impede do método ‘descobrir’ se o evento de clique do mouse foi feito em cima de um determinado Shape. Ao ser criado, cada shape possui um “ID”, informado pela aplicação como um valor numérico. Quando um evento de clique do mouse é realizado sobre um shape, a propriedade ShapeAtu da instância do tPaintPanel é atualizada. Logo, o método somente precisa procurar no array de Shapes qual o shape que foi clicado. Se não foi clicado em nenhum shape, o evento é ignorado.

//-- Identifica obj. shape clicado.
nDestino := aScan(::aShape,{ |x|(x[1] == ::oTPanel:ShapeAtu)})

Ainda dentro do método Click(), existe todo o tratamento para avaliar como o clique deve ser tratado. Para realizar um “pulo”, você deve selecionar uma pedra, criando sobre ela, e depois clicando em um espaço em branco, indicando que você quer movê-la para aquela posição. Seu movimento somente será realizado se, a posição escolhida tiver a distancia de 2 casas, na horizontal ou vertical — diagonal não pode — , e entre a posição selecionada e a posição escolhida, exista uma pedra — que será removida.

E, da forma que o jogo foi idealizado, existe um programa “base”, ou “executor”, que é responsável por prover a interface externa do jogo — A janela de diálogo onde o jogo será montado e o menu de opções — Iniciar Novo Jogo, Ajuda, Sair, barra de status inferior, etc. Reparem que apenas uma instância do jogo é criada e armazenada na variável oResta1, e a partir deste ponto todas as ações externas que podem ser realizadas com o Jogo ( iniciar, reiniciar, mostrar ajuda) são feitas a partir dos métodos publicados.

#INCLUDE "PROTHEUS.CH"

/* -----------------------------------------------------
Fonte RESTA1.PRW
Programa Resta 1 - executor
Autor Flavio Luiz Vicco
Data 11/2016
----------------------------------------------------- */

User Function Resta1()
 Local oDlg
 Local oResta1
 DEFINE DIALOG oDlg TITLE "Resta1" From 180,180 TO 550,700 PIXEL COLOR CLR_BLACK,CLR_WHITE
 oDlg:lEscClose := .F.
 //-- Cria Resta1
 oResta1:= TResta1():New(oDlg)
 //-- Cria Menu superior
 CreateMenuBar(oDlg,oResta1)
 //-- Cria Barra de Status inferior
 CreateMsgBar(oDlg)
 // Na ativação do dialogo, ativa o jogo 
 ACTIVATE DIALOG oDlg CENTERED ON INIT oResta1:Activate()
Return Nil

//-- Cria Menu superior
Static Function CreateMenuBar(oDlg,oResta1)
 oTMenuBar:= TMenuBar():New(oDlg)
 oTMenuBar:SetCss("QMenuBar{background-color:#eeeddd;}")
 oTMenuBar:Align := CONTROL_ALIGN_TOP
 oTMenuBar:nClrPane := RGB(238,237,221)
 oTMenuBar:bRClicked := {||}
 oFile:= TMenu():New(0,0,0,0,.T.,,oDlg)
 oHelp:= TMenu():New(0,0,0,0,.T.,,oDlg)
 oTMenuBar:AddItem("&Arquivo",oFile,.T.)
 oTMenuBar:AddItem("Aj&uda" ,oHelp,.T.)
 oFile:Add(TMenuItem():New(oDlg,"&Novo Jogo",,,,{|| oResta1:NewGame()},,"",,,,,,,.T.))
 oFile:Add(TMenuItem():New(oDlg,"Sai&r",,,,{|| If(MsgYesNo("Deseja realmente sair do jogo?"),oDlg:End(),)},,"FINAL",,,,,,,.T.))
 oHelp:Add(TMenuItem():New(oDlg,"&Sobre... F1",,,,{|| oResta1:Help() },,"RPMPERG",,,,,,,.T.))
Return

//-- Cria Barra de Status inferior
Static Function CreateMsgBar(oDlg)
 oTMsgBar := TMsgBar():New(oDlg, "Resta1",.F.,.F.,.F.,.F., RGB(116,116,116),,,.F.) 
 oTMsgItem1 := TMsgItem():New( oTMsgBar,"2014", 100,,,,.T., {||} ) 
 oTMsgItem2 := TMsgItem():New( oTMsgBar,"V.1.00", 100,,,,.T., {||} )
Return


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

E, segue abaixo o fonte da classe do jogo .

#INCLUDE "PROTHEUS.CH"

/* -----------------------------------------------------
Fonte TRESTA1.PRW
Programa Resta 1 - objetos
Autor Flavio Luiz Vicco
Data 11/2016
----------------------------------------------------- */

Class TResta1

Data nId AS INTEGER
 Data nOrigem AS INTEGER // Numero da posicao selecionada
 Data aShape AS ARRAY INIT {}
 DATA oTPanel AS OBJECT // tPaintPanel

Method New(oDlg) CONSTRUCTOR
 Method Activate()
 Method NewGame()
 Method Create()
 Method Click( x, y, oTPanel )
 Method Change( oTPanel, nItem, nStatus )
 Method SetId()
 Method ExportImage()
 Method Help()
EndClass

Method New(oDlg) Class TResta1
 ::nId := 0 
 ::nOrigem := 0 
 ::aShape := {} 
 ::oTPanel:= TPaintPanel():new(0,0,0,0,oDlg,.f.)
 ::oTPanel:Align := CONTROL_ALIGN_ALLCLIENT
 ::oTPanel:bLClicked := {|x,y| ::Click()}
 ::ExportImage()
Return Self

Method Activate() Class TResta1
 Local nX := 0
 Local nY := 0
 Local nZ := 0
 Local cImg := "backg.png"
 Local cId := ""
 //-- Tamanho da tabuleiro
 cTabLarg := cValToChar(400)
 cTabAlt := cValToChar(450)
 //-- Ajusta tela conforme tabuleiro
 ::oTPanel:oWnd:nHeight := Val(cTabAlt)
 ::oTPanel:oWnd:nWidth := Val(cTabLarg)
 //-- Altura largura do tabuleiro
 cAltura := '0'
 cLargura := '0'
 //-- Cria Container
 ::oTPanel:addShape( "id="+::SetId()+";type=1;left=0;top=0;width="+cValToChar(Val(cTabLarg))+;
   ";height="+cValToChar(Val(cTabAlt))+";"+;
   "gradient=1,0,0,0,0,0.0,#FFFFFF;pen-width=0;pen-color=#FFFFFF"+;
   ";can-move=0;can-mark=0;is-container=1;")
 //-- Cria shape com imagem do tabuleiro
 cId := ::SetId()
 ::oTPanel:addShape( "id="+cId+";type=8;left="+cLargura+";top="+cAltura+";width="+cTabLarg+;
   ";height="+cTabAlt+";image-file="+::GetTempPath()+cImg+";tooltip=Resta1"+;
   ";can-move=0;can-deform=0;can-mark=0;is-container=1")
 For nX := 1 To 7
  For nY := 1 To 7
   nZ ++
   If !StrZero(nZ,2) $ "01|02|06|07|08|09|13|14|36|37|41|42|43|44|48|49"
    ::Create( ::oTPanel, nX, nY, "P"+StrZero(nZ,2), IIf(nZ==25,0,1) )
   EndIf
  Next nY
 Next nX
Return

Method NewGame() Class TResta1
 Local nX := 0
 Local nY := 0
 Local nZ := 0
 For nZ := 1 To Len(::aShape)
  nX := ::aShape[nZ,3]
  nY := ::aShape[nZ,4]
  If ::aShape[nZ,5] <> IIf(nX==4.And.nY==4,0,1)
   ::Change( ::oTPanel, nZ, IIf(nX==4.And.nY==4,0,1 ) )
  EndIf
 Next nZ
Return

Method Create( oPanel, nImgX, nImgY, cCodigo, nStatus, nShape, cImgId ) Class TResta1
 Local cWidth := "30"
 Local cHeight := "30"
 Local cImg := ""
 Local cToolTip := AllTrim(cCodigo)+" X= "+AllTrim(Str(nImgX))+" Y= "+AllTrim(Str(nImgY))
 Default nShape := 0
 Default cImgId := ::SetId()

 //-- Define imagem para cada status
 // 0 = Nao há pedra - vazio
 // 1 - Espaço ocupado com uma pedra
 // 2 - Pedra atualmente selecionada
 Do Case
 Case nStatus == 0
   cImg := "empty.png"
 Case nStatus == 1
   cImg := "full.png"
 Case nStatus == 2
   cImg := "select.png"
 EndCase
 //-- criacao do obj
 If nShape == 0
  aAdd(::aShape,Array(5))
  nShape := Len(::aShape)
 EndIf
 //-- config. do obj
 ::aShape[nShape,1] := Val(cImgId) //CODIGO DO SHAPE
 ::aShape[nShape,2] := cCodigo //CODIGO
 ::aShape[nShape,3] := nImgX //POSICAO X
 ::aShape[nShape,4] := nImgY //POSICAO Y
 ::aShape[nShape,5] := nStatus //STATUS

oPanel:addShape("id="+cImgId+";type=8;left="+Str(nImgY*45)+;
 ";top="+Str(nImgX*45)+";width="+cWidth+";height="+cHeight+;
 ";image-file="+::GetTempPath()+cImg+";tooltip="+cToolTip+;
 ";can-move=0;can-deform=1;can-mark=0;is-container=0")
Return

Method Click() Class TResta1
 Local nDestino := 0
 Local nSalto := 0
 Local nIdImg := 0
 Local nX := 0
 Local nY := 0
 Local nIdClk := 0
 Local nStatus := 0
 Local lOk := .F.

//-- Identifica obj. shape clicado.
 nDestino := aScan(::aShape,{ |x|(x[1] == ::oTPanel:ShapeAtu)})
 If nDestino > 0
  nStatus := ::aShape[nDestino,5]
  Do Case
  Case nStatus == 0
  If ::nOrigem > 0
  nX0 := ::aShape[::nOrigem ,3]
  nY0 := ::aShape[::nOrigem ,4]
  nX1 := ::aShape[ nDestino,3]
  nY1 := ::aShape[ nDestino,4]
  //-- Verifica se movimento horizontal valido...
  If (nX0 == nX1 .And. Abs(nDif := nY0 - nY1) == 2)
   If nDif == 2
    nDif := -1
   Else
    nDif := 1
   EndIf
   lOk := (nSalto:=aScan(::aShape,{|x| x[3]==nX0 .And. x[4]==nY0+nDif .And. x[5]==1})) > 0
  EndIf
  //-- Verifica se movimento vertical valido...
  If (nY0 == nY1 .And. Abs(nDif := nX0 - nX1) == 2)
   If nDif == 2
    nDif := -1
   Else
    nDif := 1
   EndIf
   lOk := (nSalto:=aScan(::aShape,{|x| x[3]==nX0+nDif .And. x[4]==nY0 .And. x[5]==1})) > 0
  EndIf
  If lOk
   nStatus := 1
   //-- Retira da posicao saltada
   ::Change( ::oTPanel, nSalto, 0 )
   //-- Retira da posicao anterior
   ::Change( ::oTPanel, ::nOrigem, 0 )
   ::nOrigem := 0
  EndIf
 EndIf
 Case nStatus == 1
 If ::nOrigem > 0
  //-- Retira da posicao anterior
  ::Change( ::oTPanel, ::nOrigem, 1 )
 EndIf
 nStatus := 2
 ::nOrigem:= nDestino
 lOk := .T.
 Case nStatus == 2
  nStatus := 1
  ::nOrigem:= 0
  lOk := .T.
 EndCase
 //-- Troca figura da posicao atual
 If lOk
  ::Change( ::oTPanel, nDestino, nStatus )
 EndIf
 EndIf
Return

//-- Realiza uma mudança de status de um elemento ( pedra ) do jogo 
Method Change( oTPanel, nItem, nStatus ) Class TResta1
 Local nIdImg := 0
 Local cCodigo := ""
 Local nX := 0
 Local nY := 0
 nIdImg := ::aShape[nItem,1]
 cCodigo := ::aShape[nItem,2]
 nX := ::aShape[nItem,3]
 nY := ::aShape[nItem,4]
 //-- Excluir shape com status anterior
 ::oTPanel:DeleteItem(nIdImg)
 //-- Recriar shape com status atual
 ::Create( ::oTPanel, nX, nY, cCodigo, nStatus, nItem, Str(nIdImg) )
Return

//-- CRia identificador sequencial para objetos
Method SetId() Class TResta1
Return cValToChar(++::nId)

//-- Exporta as imagens do RPO para o temporario %TEMP%
Method ExportImage() Class TResta1
 Local aImage := { "backg.png" , "empty.png" , "full.png" , "select.png" }
 Local nImage, cImageTo
 For nImage := 1 To Len(aImage)
  cImageTo := ::GetTempPath()+aImage[nImage]
  If !Resource2File(aImage[nImage],cImageTo)
   MsgStop("Image not found: " + aImage[nImage])
   QUIT
  EndIf
 Next nImage
Return

Method Help() Class TResta1
 MsgInfo( "Resta1 em ADVPL.","Bem Vindo!")
Return

Conclusão

Agradeço novamente a colaboração do Fávio Vicco, neste momento em que eu estou enfrentando um breve “bloqueio criativo” ..risos.. e desejo a todos TERABYTES DE SUCESSO !!!

Referências

https://github.com/siga0984/Resta1-OO
http://tdn.totvs.com/display/tec/TPaintPanel
https://pt.wikipedia.org/wiki/Resta_um

Anúncios

Aplicações Externas no AdvPL – Parte 01

Introdução

A linguagem AdvPL permite a execução de aplicações externas. E este recurso é muito interessante quando precisamos fazer automação de processos e integrações com sistemas legados ou aplicações externas ao TOTVS Application Server. Nesta série de tópicos, vamos abordar o que o AdvPL nos oferece para realizar estas tarefas, e para abrir esse assunto, este tópico têm um exemplo funcional de DLL para ser usada no SmartClient, que permite capturar a tela da máquina que está executando o SmartClient.

Visão Geral de Aplicações Externas

Podemos definir como “aplicações externas”, qualquer programa executável, com ou sem interface, criado para uma determinada finalidade, que não pertence ao conjunto de ferramentas do Protheus.

Existem inúmeras aplicações externas que o Protheus realiza algum tipo de integração. Por exemplo, um servidor de e-mail ou de FTP. É uma aplicação externa, que pode ser acessada através de uma conexão TCP, onde já existe uma função ou classe em AdvPL para encapsular as funcionalidades desta aplicação, como as classes de FTP e e-MAIL.

Normalmente muitas aplicações externas são sistemas complexos, com múltiplas funcionalidades. Algumas delas oferecem APIS de acesso via rede TCP/IP, mediante troca de mensagens em formato específico ou proprietário, ou usando XML-SOAP sobre HTTP, REST sobre HTTP, etc.

Aplicações Específicas ou Especialistas

Existem alguns tipos de aplicações, normalmente menores e criadas para tarefas especializadas, que não oferecem uma camada de acesso remoto, ou mesmo alguma API, mas sim apenas uma DLL ou um executável sem interface, onde devemos realizar a chamada da operação desejada através do endereço de uma função publicada na DLL, ou mesmo através da linha de comando do sistema operacional. Nesta abordagem de aplicações externas, vamos focar inicialmente nestes casos.

Recursos do AdvPL

Usando determinadas funções do AdvPL, podemos realizar a carga de uma DLL, e a chamada de uma função publicada nesta DLL, desde que a mesma atenda a especificação de parâmetros estabelecida, e podemos chamar programas externos (executáveis ou mesmo comandos do sistema operacional).

Porém, devido a uma (importante) questão de segurança e isolamento, ñenhum destes recursos está disponivel para carga de dlls ou execução de programas na instância do TOTVS | Application Server (serve-side). Elas foram feitas para trabalhar em conjunto com o TOTVS SmartClient.

Desse modo, a sua aplicação AdvPL precisa ser iniciada a partir de um SmarrtClient, para a partir dele carregar uma DLL ou chamar um programa executável disponivel na máquina onde o Smartclient está sendo executado. Deste modo, nenhum processo executado em JOB dentro do Protheus Server consegue carregar uma DLL no servidor, ou iniciar um aplicativo executável externo.

SmartClient – Carga de DLL

Existem 5 (cinco) funções no AdvPL que lidam com a carga e execução de funções em DLL, a seguir :

ExecInDLLOpen() – Carrega a DLL na área de memória do SmartClient
ExecInDLLRun() – Executa uma função pré-definida na DLL carregada
ExeDLLRun2() – Executa uma função pré-definida na DLL carregada
ExeDLLRun3() – Executa uma função pré-definida e diferenciada na DLL carregada
ExecInDLLClose()- Descarrega a DLL da memória do SmartClient

A DLL criada deve conter uma (e apenas uma) das prototipagens abaixo, para ser chamada pelo AdvPL:

extern “C” __declspec(dllexport) void ExecInClientDLL(int , char * , char * , int )
extern “C” __declspec(dllexport) int ExecInClientDLL(int , char * , int , char * , int )

Usamos a primeira prototipagem para realizar chamadas pelas funções AdvPL ExecInDLLRun() e ExeDLLRun2(), e a segunda pode ser chamada apenas pela ExeDLLRun3(). Devemos escolher apenas uma prototipagem a ser usada. Não é possível usar as duas na mesma DLL. Você pode criar uma DLL usando C ou C++, ou Delphi, ou outra linguagem que permita a declaração da função na prototipagem adequada. Quando precisamos encapsular um ou mais funcionalidades dentro de um projeto que dependem de DLLs de terceiros, podemos encapsular a DLL de terceiros, construindo um projeto de uma DLL que faça link dinâmico com a DLL de terceiros a ser encapsulada, fazendo a camada de chamadas dentro da função prototipada.

Uma aplicação externa em uma DLL pode abrir uma interface própria, sobre a interface do SmartClient, ou pode ser usada simplesmente para acessar um dispositivo externo ou realizar qualquer outra tarefa sem interface. Existem limitações sobre os parâmetros e retorno da chamada, estes limites variam de acordo com a função AdvPL utilizada para realizar a chamada.

Parâmetros e Retornos

Basicamente, todas as prototipagens permitem a passagem simultânea de um parâmetro numérico inteiro e uma string, e um retorno de uma string. A diferença entre elas está no tamanho dos parâmetros, e a última prototipagem permite também o retorno simultâneo para o AdvPL de um valor numérico e uma string. A documentação da TDN aborda em detalhes o funcionamento esperado de cada uma, vamos focar aqui em um exemplo prático.

DLL de Captura de Tela

Para mostrar como a carga e execução de uma DLL no TOTVS SmartClient funciona, vamos começar com um projeto em C++, que pode ser compilado a partir do Visual Studio 2005 e versões superiores. Basta criar um “Empty Project” em C++ no Visual Studio, definir que o projeto será uma DLL, e criar o fonte de cabeçalho “GetScreen.hpp” e “GetScreen.cpp”, com os conteúdos abaixo:

Arquivo [getscreen.hpp]

void DoCapture( char * file , char * result );
void ScreenCapture(int x, int y, int width, int height, char *filename, char * result );

Arquivo [getscreen.cpp]

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <windows.h>
#include <gdiplus.h>
#include <memory>

#include "GetScreen.hpp"

/* ------------------------------------------------------------
Programa GetScreen
Autor Júlio Wittwer
Data 07/09/2016
Descrição DLL Win32 para uso com o TOTVS SmartClient
 Permite a captura da tela do computador onde o SmartClient está sendo executado
 gerada como uma imagem .JPEG salva em disco, usando o nome do arquivo fornecido 
 como parâmetro.

Utilização 

 Utilizar a função ExecInClientDLL(), informando os seguintes parâmetros: 

 int ID => Número da operação desejada 
 0 = Obter a versão da API 
 1 = Capturar um ScreenShot em formato JPEG

 char * BufIn => array de char contendo um parâmetro para a opção.
 SE ID = 0, o parâmetro é ignorado e pode ser NULL
 SE ID = 1, deve ser informado o nome do arquivo JPEG no qual a 
 captura de tela deve ser gravada. 

 char * BufOut => Array de char contendo o resultado da chamada, no formato 
 NNN XXXXXXXX, onde NNN é um Código de Status , e XXXXX contém uma informação 
 adicional sobre o status retornado. Em caso de sucesso, o número retornado é "000"
 Qualquer outro número indica uma condição de falha. 

 "001 Encode size failed"
 "002 Memory allocation failed"
 "003 Image Codec not found"
 "004 Image Save Error (%d)"
 "005 Unexpected Error %d
 "010 Unknow Command"

------------------------------------------------------------ */


extern "C" __declspec(dllexport) void ExecInClientDLL( int ID, char * BufIn, char * BufOut, int nSizeOut )
{

 if( ID == 0 )
 {
 // Retorna a versão da DLL de captura
 strcpy(BufOut,"000 GetScreen V 0.160911");
 }
 else if (ID == 1)
 {
 // REaliza a captura da tela
 // Recebe em BuffIn o nome do arquivo a ser salvo 
 // Retona em buffOut o status da operação 
 // Em caso de sucesso, retorno "000 Ok"
 // Em caso de erro, retorno "NNN <error message>"
 DoCapture(BufIn,BufOut);
 }
 else
 {
 // ID não conhecido/inválido 
 strcpy(BufOut,"010 Unknow Command");
 }
}

using namespace Gdiplus;
using namespace std;

// Inicializa GDI para a captura de vídeo
// faz a captura, salva em disco, e finaliza GDI

void DoCapture( char * file , char * result ) 
{

 // Initialize GDI+.
 GdiplusStartupInput gdiplusStartupInput;
 ULONG_PTR gdiplusToken;

 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

 int x1 = 0;
 int y1 = 0;
 int x2 = GetSystemMetrics(SM_CXSCREEN);
 int y2 = GetSystemMetrics(SM_CYSCREEN);

 // Realiza a captura da tela e salva em arquivo 
 ScreenCapture(x1, y1, x2 - x1, y2 - y1, file , result );

 // Shutdown GDI+
 GdiplusShutdown(gdiplusToken);

}


// Retorna o ponteiro do encoder adequado para fazer
// a conversão do BITMAP em memória para o formato desejado


int GetEncoderClsid(const WCHAR* format, CLSID* pClsid )
{
 UINT num = 0; // number of image encoders
 UINT size = 0; // size of the image encoder array in bytes

 ImageCodecInfo* pImageCodecInfo = NULL;

 GetImageEncodersSize(&num, &size);

 if(size == 0)
 {
 // Encode Size Failure
 return -1; 
 }

 pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
 if(pImageCodecInfo == NULL)
 {
 // Memory allocation failure
 return -2; 
 }

 GetImageEncoders(num, size, pImageCodecInfo);

 for(UINT j = 0; j < num; ++j)
 {
 if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
 {
 *pClsid = pImageCodecInfo[j].Clsid;
 free(pImageCodecInfo);
 // Success
 return j; 
 } 
 }

 free(pImageCodecInfo);

 // Image Codec not found
 return -3; 
}

// Encapsula a gravação do BITMAP capturado da tela
// para o formato JPEG em disco 

void BitmapToJpg(HBITMAP hbmpImage, char *filename , char * result )
{

 Status eRC = Ok;

 Bitmap * p_bmp = Bitmap::FromHBITMAP(hbmpImage, NULL);

 CLSID pngClsid;

 int RC = GetEncoderClsid(L"image/jpeg", &pngClsid);

 if( RC >= 0 )
 {
 const size_t cSize = strlen(filename)+1;
 std::wstring wc( cSize, L'#' );
 mbstowcs( &wc[0], filename, cSize );
 eRC = p_bmp->Save(&wc[0], &pngClsid, NULL);
 if ( eRC != Ok)
 RC = -4;
 }

 delete p_bmp;

 if ( RC == -1 )
 sprintf_s(result,255,"001 Encode size failed");
 else if ( RC == -2 )
 sprintf_s(result,255,"002 Memory allocation failed");
 else if ( RC == -3 )
 sprintf_s(result,255,"003 Image Codec not found");
 else if ( RC == -4 )
 sprintf_s(result,255,"004 Image Save Error (%d)",eRC);
 else if ( RC < 0 )
 sprintf_s(result,255,"005 Unexpected Error %d",RC);
 else
 sprintf_s(result,255,"000 Ok");

}


// Funão de captura / snapshot de tela
// Requer o ambiente DGI previamente inicializado

void ScreenCapture(int x, int y, int width, int height, char *filename, char * result )
{
 HDC hDcMemory = CreateCompatibleDC(0);
 HDC hDcScreen = GetDC(0);
 HBITMAP hBmp = CreateCompatibleBitmap(hDcScreen, width, height);

 SelectObject(hDcMemory, hBmp);
 BitBlt(hDcMemory, 0, 0, width, height, hDcScreen, x, y, SRCCOPY);

 // Converte a tela capturada em JPEG e salva em disco 
 BitmapToJpg(hBmp, filename , result );

 // Cleanup
 DeleteObject(hBmp);
 DeleteObject(hDcMemory);
 ReleaseDC(NULL,hDcScreen);
}

Feito isso, basta fazer a “build do Projeto, gerando uma DLL 32 bits, e copiá-la para a pasta do TOTVS SmartClient.

Agora, crie um fonte AdvPL, chamado por exemplo de “GetScreen.PRW”, adicione-o a um projeto no IDE/TDS, com o seguinte conteúdo:

Arquivo [GetScreen.PRW]

/* ------------------------------------------------------------
Funcao U_GetSCR()
Autor Júlio Wittwer
Data 09/2016
Descrição Tira um ScreenShot da tela da máquina onde está 
 sendo executado o TOTVS | SmartClient, e copia 
 o JPEG com a imagem gerada para uma pasta 
 chamada "\images\" a partir do RootPath do 
 ambiente no Servidor
Observação A Pasta \images\ a partir do RootPath deve ser 
 criada antes de executar este programa

------------------------------------------------------------ */

User function GetSCR()

Local hHdl := 0
Local cRet
Local nCode
Local cRmtFile
Local cSrvFile 
Local cImgFile

// Abre a DlL
hHdl := ExecInDLLOpen( "GetScreen.dll" )

IF hHdl == -1 
 UserException("Failed to load [GetScreen.dll]")
Endif

// Pega a versao do GetScreen
cRet := ExecInDllRun( hHdl, 0, "" )
conout(cRet)

// identifica a pasta temporaria da maqina 
// que está rodando o smartclient 
// e usa ela pra criar a imagem do screenshot

cImgFile := 'img_'+cValTochar(Threadid())+'.jpeg'
cRmtFile := GETTEMPPATH() + cImgFile;

Msgrun( "Aguarde",;
 "Executando captura de tela",;
 {|| cRet := ExecInDllRun( hHdl, 1, cRmtFile )} )


If empty(cRet)
 UserException("Unexpected Empty result from [GetScreen.dll]")
Endif

// Verifica o codigo retornado
// 000 = Sucesso 
// <> 0 = Falha

nCode := val(left(cRet,3))
If nCode > 0 
 UserException(cRet)
Endif

// copia o arquivo do terminal para o servidor
CPYT2S(cRmtFile,"\images\")

// Apaga o arquivo na pasta temporária da estação 
Ferase(cRmtFile)

// Informa a operação realizada
MsgInfo("Imagem salva no servidor em \images\"+cImgFile)

// ----------------------------------------------------------------
// Fecha a DLL
ExecInDllClose( hHdl )

Return 

Execução

Após gerar a DLL 32 bits no Visual Studio, copiá-la para a pasta do SmartClient, e compilar o fonte AdvPL “GetScreen.PRW”, e criada uma pasta chamada “\images\” a partir do RootPath do seu ambiente do Protheus, basta iniciar um Totvs SmartClient, e executar o programa “U_GetSCR”. Ele mostrará rapidamente uma caixa de diálogo da função MsgRun(), a partir de onde será feita a captura da tela na estação onde está sendo executado o TOTVS SmartClient, e logo depois uma caixa de diálogo, informando o nome do arquivo que contém a imagem capturada na pasta \images\ no ambiente do Protheus Server.

O Fonte em AdvPL determina qual é o nome da pasta temporária da máquina onde o SmartClient está sendo executado, cria um nome para salvar a imagem, chama a DLL pra fazer a captura da imagem, informando o nome do arquivo onde a imagem deve ser salva, e depois apenas copia o arquivo gerado no disco da estação onde o SmartClient está sendo executado para a pasta “\images\” do Protheus Server, e apaga o arquivo temporário da estação.

Estendendo as funcionalidades

O modelo proposto permite que você implemente literalmente milhares de instruções dentro de uma DLL, mesmo tendo apenas uma função prototipada, pois podemos criar uma funcionalidade com tratamento diferenciado de parâmetros e retorno para cada nID utilizado. Usando o ID 0 (zero) para controlar a versão da API, quando você implementar uma funcionalidade nova nesta DLL, basta atualizar o número da versão, e no seu programa em Advpl, basta verificar qual é a versão em uso, para saber se ela já possui a funcionalidade desejada. Por exemplo, baseado neste programa, você pode implementar o ID=2 para pegar a quantidade de monitores que existe na máquina Client, ou a resolução atual de tela, ou qualquer outra coisa que lhe seja útil.

Fontes e DLL

Para você que quer compilar, testar e usar este recurso, ou estendê-lo, basta pegar os fontes no https://github.com/siga0984/Blog — todos os arquivos que começam com “GetScreen.*”. Inclusive, também disponibilizei o arquivo getscreen.dll, compilado para Windows 32 Bits. Basta copiar ele para a pasta do SmartClient, compilar o programa AdvPL GetScreen.PRW no repositório do ambiente Protheus e utilizá-lo. Segue abaixo links do GitHub para cada um deles.

https://github.com/siga0984/Blog/blob/master/GetScreen.hpp
https://github.com/siga0984/Blog/blob/master/GetScreen.cpp
https://github.com/siga0984/Blog/blob/master/GetScreen.prw
https://github.com/siga0984/Blog/blob/master/GetScreen.dll

Conclusão

Esse é um exemplo bem arroz-com-feijão, apenas pra dar um gostinho do que podemos fazer com esta integração, e ainda nem chegamos na camada de chamar aplicações externas — que também é uma “mão na roda”. No próximo post, vou entrar um pouco mais em alguns aspectos e propriedades desta integração com DLL, que abrem um amplo leque de possibilidades !!!

Agradeço a todos(as) pela audiência, e lhes desejo TERABYTES de SUCESSO 😀

Informações Adicionais

RELEASE 2016/09/14 23:32 – Meus agradecimentos ao meu chapa Pedro Scarapicchia, por localizar e me apontar 3 pontos de leak na função ScreenCapture() 😀 Post e GitHub atualizados 😉 Pedro, obrigado por me lembrar das boas práticas — a pressa de colocar o post no ar foi maior que a atenção no código ..rs…

Referências

http://tdn.totvs.com/display/tec/ExecInDLLOpen
http://tdn.totvs.com/display/tec/ExecInDLLRun
http://tdn.totvs.com/display/tec/ExeDLLRun2
http://tdn.totvs.com/display/tec/ExeDLLRun3
http://tdn.totvs.com/display/tec/ExecInDLLClose

 

Damas em AdvPL

Introdução

Em 2006, eu fiz um jogo de Damas em AdvPL, para jogar contra o Computador. Não cheguei a reforçar muito o algoritmo de decisão, mas ficou bom o suficiente pra dar um pouco de trabalho. O fonte ainda está cm alguns remendos, ainda não está pronto para um post de fins didáticos, MAS, atendendo a pedidos no FaceBook, estou disponibilizando um Patch da P11 (RPO TOP / Português) do Jogo, para degustação 😀

O Jogo

Após aplicar o patch, basta iniciar o SmartClient com a função “U_APGAMES”, e será mostrada a interface abaixo, para você entrar com o NickName do Jogador.

Checkers003

Após inserir o NickName e clicar em “Iniciar”, inicia-se o jogo de Damas. Você joga com as pedras amarelas, e o computador com as azuis. Você inicia a partida. Para jogar, primeiro você clica em cima de uma pedra sua, depois clica no lugar onde a sua pedra deve ser movimentada, vide sequência abaixo:

Checkers006a

Checkers006b

Checkers006c

Assim que você jogar, o computador jogará uma pedra dele. E já é a sua vez de novo.

Regras

  • As pedras normais somente movimentam-se para a frente.Em nenhuma hipótese uma pedra normal movimenta-se para trás.
  • Não existe “assopro”. Se você oferecer uma ou mais pedras ao computador, ele é obrigado a comer a sua pedra, e vice-versa.
  • Se mais de uma pedra for oferecida, o adversário escolhe como e qual pedra ele vai atacar.
  • Ao chegar do outro lado do tabuleiro com uma pedra normal, ela vira uma “Dama”.
  • A Dama pode mover-se para a frente e para trás, mas apenas UMA CASA por vez.
  • O mecanismo de navegação do Computador é “reativo”, então para você ganhar o jogo, você tem que encurralar o computador e fazer ele entregar as suas peças.
  • É mais fácil vencê-lo em jogo aberto, havendo troca de peças, e você abrindo caminho primeiro para fazer uma Dama. Mas cuidado com as “arapucas”, ele pode oferecer “inocentemente” uma pedra, e limpar duas ou três suas. 😉

Patch

O Patch do jogo está disponível para download no link “https://github.com/siga0984/Blog/blob/master/tttp110_APGames.zip” . Basta abrir a página, e clicar em “View RAW”, no final da página, para o Browse fazer o Dowload do ZIP contendo o Patch.

Conclusão

Existem diversas melhorias previstas no design do jogo, e inclusive a separação completa do core do jogo e da interface, além da utilização de orientação a objetos. Uma vez passado a limpo, o jogo será disponibilizado na íntegra, co os fontes 😀

Espero que vocês gostem do desafio, joguem um pouco contra o algoritmo, postem seus resultados no FaceBook 😉 E se você gostou, faça como eu: Compartilhe 😀

Desejo a todos TERABYTES de SUCESSO 😀 Abraços 😉 

 

RunTime do AdvPL

Introdução

No primeiro post do Blog, em apenas um parágrafo foi dada uma definição bem sintética do que é o AdvPL: Trata-se de uma uma linguagem de programação estruturada com suporte a orientação de objetos, que roda em uma máquina virtual com arquitetura client-server e multi-plataforma.

Hoje vamos entrar um pouco mais fundo em algumas características da execução de um código AdvPL, desde a pré-compilação até a execução do Código. Mas antes, vamos com algumas definições.

Código AdvPL

A sintaxe da linguagem AdvPL é uma extensão da sintaxe xBase, mais conhecida pelo seu uso na linguagem Clipper. Um código fonte em AdvPL passa por uma etapa de pré-compilação, onde vários comandos são transformados em chamadas de funções, e “açúcares sintáticos” podem ser implementados, através de #defines, #translates, #command(s), #ifdef(s) e afins. Os comandos da linguagem AdvPL que realmente são “comandos” são as instruções de decisão e iteração, como WHILE, FOR, IF, ELSEIF , ELSE, CASE, END … praticamente todo o resto dos comandos são implementações feitas por #command, #xcommand ou #translate, que permitem você usar por exemplo um “comando” de abertura de tabela, como o comando “USE <cTabela> ALIAS <cAlias> [ SHARED | EXCLUSIVE ] [ VIA <cRdd> ] [READONLY] [NEW]”, que na etapa de pré-compilação vai ser transformando internamente na chamada da função DbUseArea().

Pré-compilação

Tanto o IDE como o TDS (versão anterior e atual do Ambiente de Desenvolvimento de aplicações AdvPL, respectivamente) trabalham com um pré-compilador (appre.exe). A partir do fonte AdvPL e dos respectivos #include(s) utilizados no código, a pré-compilação gera um novo arquivo, chamado de PPO (Pre Processed Output), e este sim é o código enviado ao AppServer para ser compilado.

Compilação

A compilação deste código gera um “ByteCode” em formato proprietário, interpretável somente pela máquina virtual do AppServer. O ByteCode gerado é criptografado e armazenado em um arquivo de repositório de objetos, configurado no AppServer para o ambiente onde os fontes estão sendo compilados. Damos o nome de “ambiente” a um espaço virtual de execução, que engloba um repositório de objetos e um ponto de acesso a uma pasta no disco (local ou remota via rede) para acesso a arquivos sob este identificador de ambiente, entre outras configurações de acesso a dados relacionais e comportamentos desejados para a instância do ambiente.

Repositório de Objetos

Armazena as funções e classes compiladas a partir dos fontes AdvPL, é fornecido pela TOTVS contendo as compilações dos módulos do ERP Microsiga do produto padrão. Contém também as funções do Framework AdvPL, usadas pelos fontes dos demais módulos, e que também são usados em customizações do produto, onde o próprio cliente pode criar um código em AdvPL, usando-se das funções básicas e de Framework AdvPL para customizar o produto padrão através de pontos de entrada (compilação de funções de usuário — USER FUNCTION(s) — com nomes especificos, onde existem pontos de chamada realizados pelo produto padrão do ERP), ou mesmo criando novas funcionalidades.

O repositório possui mecanismos de verificação de integridade e proteção de código, para evitar a engenharia reversa dos fontes compilados. A reversão de código é possível, e às vezes necessária quando um cliente por exemplo “perde” os fontes das customizações da versão em uso do seu ERP, e precisa delas no momento de uma migração de versão. O procedimento de “descompilação” neste caso é solicitado para a TOTVS mediante abertura de chamado, onde cada caso é avaliado.

O repositório de objetos também guarda arquivos de imagem (BMP,PNG,JPG), que podem ser inseridos em um Projeto AdvPL através do IDE/TDS e recuperados posteriormente em tempo de execução e usados diretamente por algumas funções, classes e comandos da linguagem que montam a interface da aplicação.

O Protheus 12 utiliza uma nova engine da máquina virtual do AppServer, com um novo formato de ByteCode, com mais instruções de baixo nível. Isto tornou o ByteCode ligeiramente maior, porém com ganhos de desempenho em alguns processos e funcionalidades.

Execução do AdvPL

Partindo de uma execução de um programa AdvPL, iniciado a partir do SmartClient, o SmartClient faz um HandShake com o AppServer via conexão TCP (ou SSL caso configurado), e solicita a execução de uma função. Ela pode ser uma “Main Function” — Função de entrada de módulo ou ferramenta do ERP — ou uma “User Function” — aplicações ou customizações criadas em AdvPL pelo próprio cliente.

Como o AdvPL é essencialmente dinâmico, os fontes do repositório são carregados na memória sob demanda, conforme as funções vão sendo chamadas. Quando o Menu do ERP é apresentado, todos os programas já carregados e executados até então, essencialmente fontes do Framework do AdvPL são mantidos na memória. Ao chamar uma opção de menu, o fonte e suas dependências são carregados sob demanda e executados, e quando o usuário encerra a opção e volta ao Menu do ERP, todos os fontes carregados desde o início daquela opção são descarregados da memória.

Quando a excecução é a chamada de menu da interface MDI (SIGAMDI), cada opção de menu cria um contexto e uma conexão separada com o AppServer, permitindo a execução de múltiplos programas sob a mesma interface, onde cada execução possui seu contexto e processo independentes. Cada opção de menu finalizada encerra o seu próprio conexto e descarrega todos os prograas da memória. Ao usar o mecanismo de Balanceamento de Carga do AdvPL, cada nova opção de Menu do MDI pode ser direcionada para o AppServer com menos processos em execução.

Para jobs, não existe balanceamento de carga nativo. Cada Job é executado na instância do serviço onde o AppServer está sendo executado. Hoje podemos emular um balanceamento realizando um RPC para um determinado serviço e subindo o job via RPC ( Nativo do AdvPL).

Atualmente o contexto de uma execução em AdvPL está implicitamente amarrado com uma Thread do Sistema Operacional. A arquitetura multi-thread do AppServer não estabelece afinidade entre as CPUS físicas do equipamento, o Scheduler de tarefas do sistema operacional e encarregado de distribuir as Threads e realocá-las por CPU a seu critério. Devido ao cenário extremamente variável de peso de execução das Threads, tentar estabelecer uma afinidade tende a ser pior do que deixar o sistema operacional escolher a CPU em uso.

AdvPL com interface – SmartClient

A execução de processos de interface AdvPL com o SmartClient é síncrona, a aplicação SmartClient é a resposável por renderizar a interface (Janela ou diálogo) construída pela aplicação AdvPL, e uma vez que a interface seja “ativada”, o AppServer passa a esperar por uma interação do usuário no SmartClient, como pressionar um botão ou preencher um campo. Entre ambos existe um mecanismo de verificação de conexão, chamado “pulso”. Quando o AppServer está aguardando por uma interação do usuário com a interface, mesmo que o usuário não dispare nenhum evento, a cada 60 segundos o SmartClient dispara um evento interno de “pulso” para o AppServer , para indicar que o SmartClient ainda está aberto. Caso o AppServer nao receba nenhum evento ou nenhum pulso do SmartClient em 3 minutos, ele assume que o SmartClient não está mais conseguindo se comunicar com o AppServer , e derruba/finaliza o processo em execução no AppServer , liberando os recursos utilizados ( arquivos e registros bloqueados, licenças, conexão com DBAccess,etc ).

Depuração de Código AdvPL

Através do IDE e/ou TDS, podemos depurar (ou “debugar”) um código AdvPL, colocando pontos de parada, inspecionando variáveis de memória, e determinando as etapas de execução (step into, step over, step out, run…). Deve-se tomar uma atenção especial no uso dos “Watches” de execução, pois a chamada de funções dentro de um “Watch” podem mudar o comportamento da aplicação, como por exemplo reposicionar o ponteiro de registro atual de uma tabela de dados, mudando o comportamento da aplicação enquanto ela está sendo depurada. Normalmente usamos os Watches para variáveis de memória. Através da Janela de Comandos do IDE/TDS, também podemos executar expressões, e até mudar conteúdos de variáveis durante a depuração através de instruções de atribuição. Isto pode ser útil em muitas situações.

Uso de memória

Normalmente a memória alocada por um programa é limpa quando o programa é finalizado. Durante a execução de código, existem algumas limpezas realizadas nos destrutores de componentes de interface, e no retorno ao stack (pilha) anterior após a execução de funções do Repositório de Objetos. Normalmente objetos são limpos quando não são mais referenciados, porém caso seja feita uma referência circular entre objetos, a limpeza somente ocorre quando a aplicação termina, ou quando for realizada explicitamente por uma função chamada “FreeObj()”.

Existem diversos recursos de monitoramento de memória do AppServer, um dos mais utilizados é a configuração DebugThreadUsedMemory, que pode ser habilitada no appserver.ini, na seção [GENERAL]. Esta configuração faz com que o AppServer mostre no Monitor de Processos do AppServer a quantidade de memória aproximada em uso por cada processo em execução.

Conclusão

Boa parte dos recursos mencionados neste tópico estão documentados no site oficial de documentação da TOTVS, o TDN (Totvs Development Network), que pode ser acessado no link http://tdn.totvs.com. O Post de hoje é só pra “molhar” o bico, os próximos posts em momento oportuno vão entrar em mais detalhes de cada uma destas operações 😀

Agradeço a audiência de todos, e desejo a vocês TERABYTES de sucesso !!! 

Abraços 😉

Introdução a Informática – Parte 03

Introdução

No post anterior, vimos as etapas que acontecem quando ligamos o equipamento, e de bandeja alguma coisa bem ‘por cima’ sobre dados e programas. Agora vamos descer um pouco mais dentro dos Bytes, abordar o conceito de Dados e Programas, e aproveitar o gancho para ver ‘por cima’ o que é um sistema de arquivos, mais especificamente o sistema de arquivos em árvore, usado pelo MS-DOS e pelo Windows.

Lembrando do Byte

“Note que um byte nada tem de especial, é apenas um número binário de oito algarismos. Sua importância na informática deriva apenas do fato do código ASCII haver adotado números de oito bits, além de razões meramente construtivas ou operacionais. Por exemplo: os códigos enviados a impressoras para controlar a impressão têm oito bits, os valores trocados via Modem entre computadores também, assim como diversas outras operações elementares de intercâmbio de informações. Além disso, memórias costumam ser organizadas de tal forma que as operações de leitura e escrita são feitas com quantidades de um byte ou de um múltiplo de bytes (oito, dezesseis, trinta e dois, sessenta e quatro ou cento e vinte e oito bits – o que corresponde a um, dois, quatro, oito e dezesseis bytes, respectivamente).” (Wikipedia, BYTE)

Logo, o que está gravado em qualquer arquivo armazenado no disco de um computador ? R: Bytes. E na memória RAM ? R: Bytes. E num pen-drive ? Mais Bytes. Legal, tudo são Bytes, ou sequências numéricas de octetos. Mas, no computador eu tenho arquivos de texto, imagens, áudio, vídeo … tudo são bytes ? R: SIM.

Existem editores de texto e ferramentas de disco que permitem abrir um arquivo qualquer, em qualquer formato, e ler todos os bytes do arquivo, e mostrar na tela o conteúdo do arquivo representados em bytes. Normalmente estes programas mostram o valor de cada byte em uma base numérica conhecida por HEXADECIMAL. No sistema decimal, usamos 10 algarismos diferentes para representar um número, de “0” a “9”. No sistema binário, usamos apenas 2: “0” e “1”. No sistema Hexadecimal, usamos 16 ( HEXA + DECIMAL = 6 + 10 ), usando os algarismos de “0” a “9” e as letras de “A” a “F”.

Partindo da base binária, precisamos de uma sequência de 4 BITS para representar 16 valores diferentes, certo ? Usando Hexadecimal, com apenas um símbolo, de “0” a “F”, conseguimos representar 16 valores diferentes. Vejamos abaixo como podemos representar os 256 valores diferentes de um Byte usando cada um dos sistemas ( DEC = Decimal, HEX = Hexadecimal, BIN = Binário ):

DEC HEX BIN DEC HEX BIN DEC HEX BIN DEC HEX BIN
------------------------------------------------------------------------------
000 00 00000000 064 40 01000000 128 80 00000010 192 C0 11000000
001 01 00000001 065 41 01000001 129 81 00000011 193 C1 11000001
002 02 00000010 066 42 01000010 130 82 00000100 194 C2 11000010
003 03 00000011 067 43 01000011 131 83 00000101 195 C3 11000011
004 04 00000100 068 44 01000100 132 84 00000110 196 C4 11000100
005 05 00000101 069 45 01000101 133 85 00000111 197 C5 11000101
006 06 00000110 070 46 01000110 134 86 00001000 198 C6 11000110
007 07 00000111 071 47 01000111 135 87 00001001 199 C7 11000111
008 08 00001000 072 48 01001000 136 88 00001010 200 C8 11001000
009 09 00001001 073 49 01001001 137 89 00001011 201 C9 11001001
010 0A 00001010 074 4A 01001010 138 8A 00001100 202 CA 11001010
011 0B 00001011 075 4B 01001011 139 8B 00001101 203 CB 11001011
012 0C 00001100 076 4C 01001100 140 8C 00001110 204 CC 11001100
013 0D 00001101 077 4D 01001101 141 8D 00001111 205 CD 11001101
014 0E 00001110 078 4E 01001110 142 8E 00010000 206 CE 11001110
015 0F 00001111 079 4F 01001111 143 8F 00010001 207 CF 11001111
016 10 00010000 080 50 01010000 144 90 00010010 208 D0 11010000
017 11 00010001 081 51 01010001 145 91 00010011 209 D1 11010001
018 12 00010010 082 52 01010010 146 92 00010100 210 D2 11010010
019 13 00010011 083 53 01010011 147 93 00010101 211 D3 11010011
020 14 00010100 084 54 01010100 148 94 00010110 212 D4 11010100
021 15 00010101 085 55 01010101 149 95 00010111 213 D5 11010101
022 16 00010110 086 56 01010110 150 96 00011000 214 D6 11010110
023 17 00010111 087 57 01010111 151 97 00011001 215 D7 11010111
024 18 00011000 088 58 01011000 152 98 00011010 216 D8 11011000
025 19 00011001 089 59 01011001 153 99 00011011 217 D9 11011001
026 1A 00011010 090 5A 01011010 154 9A 00011100 218 DA 11011010
027 1B 00011011 091 5B 01011011 155 9B 00011101 219 DB 11011011
028 1C 00011100 092 5C 01011100 156 9C 00011110 220 DC 11011100
029 1D 00011101 093 5D 01011101 157 9D 00011111 221 DD 11011101
030 1E 00011110 094 5E 01011110 158 9E 00100000 222 DE 11011110
031 1F 00011111 095 5F 01011111 159 9F 00100001 223 DF 11011111
032 20 00100000 096 60 01100000 160 A0 00100010 224 E0 11100000
033 21 00100001 097 61 01100001 161 A1 00100011 225 E1 11100001
034 22 00100010 098 62 01100010 162 A2 00100100 226 E2 11100010
035 23 00100011 099 63 01100011 163 A3 00100101 227 E3 11100011
036 24 00100100 100 64 01100100 164 A4 00100110 228 E4 11100100
037 25 00100101 101 65 01100101 165 A5 00100111 229 E5 11100101
038 26 00100110 102 66 01100110 166 A6 00101000 230 E6 11100110
039 27 00100111 103 67 01100111 167 A7 00101001 231 E7 11100111
040 28 00101000 104 68 01101000 168 A8 00101010 232 E8 11101000
041 29 00101001 105 69 01101001 169 A9 00101011 233 E9 11101001
042 2A 00101010 106 6A 01101010 170 AA 00101100 234 EA 11101010
043 2B 00101011 107 6B 01101011 171 AB 00101101 235 EB 11101011
044 2C 00101100 108 6C 01101100 172 AC 00101110 236 EC 11101100
045 2D 00101101 109 6D 01101101 173 AD 00101111 237 ED 11101101
046 2E 00101110 110 6E 01101110 174 AE 00110000 238 EE 11101110
047 2F 00101111 111 6F 01101111 175 AF 00110001 239 EF 11101111
048 30 00110000 112 70 01110000 176 B0 00110010 240 F0 11110000
049 31 00110001 113 71 01110001 177 B1 00110011 241 F1 11110001
050 32 00110010 114 72 01110010 178 B2 00110100 242 F2 11110010
051 33 00110011 115 73 01110011 179 B3 00110101 243 F3 11110011
052 34 00110100 116 74 01110100 180 B4 00110110 244 F4 11110100
053 35 00110101 117 75 01110101 181 B5 00110111 245 F5 11110101
054 36 00110110 118 76 01110110 182 B6 00111000 246 F6 11110110
055 37 00110111 119 77 01110111 183 B7 00111001 247 F7 11110111
056 38 00111000 120 78 01111000 184 B8 00111010 248 F8 11111000
057 39 00111001 121 79 01111001 185 B9 00111011 249 F9 11111001
058 3A 00111010 122 7A 01111010 186 BA 00111100 250 FA 11111010
059 3B 00111011 123 7B 01111011 187 BB 00111101 251 FB 11111011
060 3C 00111100 124 7C 01111100 188 BC 00111110 252 FC 11111100
061 3D 00111101 125 7D 01111101 189 BD 00111111 253 FD 11111101
062 3E 00111110 126 7E 01111110 190 BE 01000000 254 FE 11111110
063 3F 00111111 127 7F 01111111 191 BF 01000001 255 FF 11111111
------------------------------------------------------------------------------

TABELA ASCII

Mas, se dentro de um arquivo qualquer, eu tenho apenas Bytes, como é que eu vou trocar informações ? No final, tudo são números, certo ? R: Sim. Na prática sim. O que nós podemos fazer é dar significado aos números, e definir uma forma de interpretação diferente para os números. O padrão mais conhecido na informática para representação e trocas de informações baseadas em TEXTO para computadores é a famosa TABELA ASCII.

“ASCII (do inglês American Standard Code for Information Interchange; “Código Padrão Americano para o Intercâmbio de Informação“) é um código binário (cadeias de bits: 0s e 1s) que codifica um conjunto de 128 sinais: 95 sinais gráficos (letras do alfabeto latino, sinais de pontuação e sinais matemáticos) e 33 sinais de controle. […] A codificação ASCII é usada para representar textos em computadores, equipamentos de comunicação, entre outros dispositivos que trabalham com texto. Desenvolvida a partir de 1960, grande parte das codificações de caracteres modernas a herdaram como base.” ( Wikipedia, ASCII)

Não vou replicar a tabela ASCII dentro deste post, dêem uma olhada nela no link da Wikipedia “https://pt.wikipedia.org/wiki/ASCII“. Quando abrimos um arquivo que contém apenas TEXTO (normalmente identificados pela extensão “.txt”), quando encontramos um byte com valor decimal “32”, ele significa um espaço em branco, “65” representa a letra “A” (“a” maiúscula do alfabeto), e uma sequência de bytes “13” e “10” representa uma quebra de linha.

Arquivos e Extensões

Um arquivo nada mais é do que uma sequência finita/limitada de Bytes. Qualquer programa é capaz de abrir um arquivo e ler seus bytes, mas para dar significado a esta informação, é necessário saber o que estes bytes representam. Quando o sistema de arquivos do MS-DOS foi concebido, foi definido que um “arquivo” possui um nome, que o diferencia dos demais, e uma extensão, normalmente separada do nome por um “.” ponto decimal, onde a extensão indica o tipo do conteúdo do arquivo.

Vejam abaixo algumas extensões e seus usos mais comuns, para os ambientes MS-DOS e Windows:

  • .INI Arquivo texto de configuração ou inicialização de algum aplicativo ou sistema.
  • .BAT Arquivo texto que contém uma sequencia de comandos do sistema operacional.
  • .EXE Arquivo binário que contém os códigos de um programa que pode ser executado pelo sistema operacional.
  • .JPG Arquivo de imagem no formato JPEG (Joint Photographic Experts Group).
  • .BMP Arquivo de imagem no fotmato BITMAP.
  • .PNG Arquivo de imagem no formato Portable Network Grafics.
  • .TXT Arquivo texto de uso geral.
  • .DLL Arquivo binário de link dinâmico, contém funções que podem ser carregadas e usadas por um ou mais programas.
  • .DOC Documento do MS-Word ou outro editor de textos.
  • .XLS Arquivo de Planilha Eletrônica do MS-Excel.
  • .PPT Arquivo de apresentação do MS-PowerPoint.
  • .ZIP Arquivo compactado no padrão ZIP, armazena um ou mais arquivos.
  • .RAR Arquivo compactado no padrão proprietário RAR, criado pelo russo Eugene Roshal (RAR = Roshal ARquive).
  • .XML Arquivo texto que contém dados estruturados usando XML (eXtended Markup Language).
  • .XSD Arquivo texto que contém uma regra de validação ou modelo de troca de dados em XML.
  • .DBF Arquivo de dados, normalmente nos formatos DBASE III e posteriores.
  • .CDX Arquivo de índice de dados, normalmente relacionado a dados em arquivo DBF.
  • .DTC Arquivo de dados no formato c-Tree, usados no AdvPL

Para mais extensões e seus usos mais comuns, consulte “https://pt.wikipedia.org/wiki/Extens%C3%B5es_de_ficheiros“.

Sistema de Arquivos e Diretórios (ou Pastas) no Windows

O sistema de arquivos do Windows herdou a estrutura organizacional e princípios do sistema de arquivos do MS-DOS. Cada unidade de disco instalada na máquina recebe uma letra maiúscula do alfabeto como identificador de unidade, seguido do símbolo “:” (dois pontos). As duas primeiras unidades de disco do computador, antes de existir disco rígido, eram as unidades “A” e “B”. Por isso que, até hoje, quando você abre o Prompt de Comando do sistema operacional no Windows, ou o Windows Explorer, a sua unidade de disco rígido é mostrada como “C:”, e as demais partições de disco e outras unidades (CD-ROM, Pen-Drive,etc) seguem em sequência.

A estrutura de arquivos do Windows é baseada em pastas — ou diretórios, como uma árvore. O primeiro diretório da árvore, ou “raiz”, e representado pela barra inversa “\”. A partir de qualquer diretório, você pode ter um ou mais arquivos, e um ou mais diretórios. Vamos entender que um diretório é um espaço nomeado dentro da estrutura que permite você guardar mais arquivos e outros diretórios. Dentro de qualquer pasta, você não pode ter mais de um arquivo ou pasta com o mesmo nome e extensão. Porém você pode ter arquivos com mesmo nome e extensão em pastas diferentes.

O Windows criou uma padronização, que apresentou pequenas mudanças ao longo das últimas 7 ou 8 versões, mas a base é a mesma. A partir da pasta Raiz da unidade de disco rígido (C:\) existe uma pasta chamada “Arquivos de Programas”, onde dentro dela existem pastas para as aplicações e programas instalados no equipamento, uma pasta “Usuários”, para registrar as informações de perfil e dados de aplicativos de cada usuário do equipamento, e a pasta “Windows”, que armazena um enorme grupo de outras pastas, que contém todos os programas, extensões, drivers e demais arquivos do Sistema Operacional.

Cada arquivo de cada pasta do computador pode ser endereçada ou identificada por um identificador de caminho completo (Full Path), composto pela unidade de disco, estrutura de pastas, e o nome do arquivo. Por exemplo, o arquivo nomeado “mstsc.exe” (Remote Desktop Connection Client), que fica na pasta “System32”, que fica na pasta “Windows” a partir do Drive “C:”, pode ser endereçado como “c:\Windows\System32\mstsc.exe”.

O sistema de arquivos de forma geral permite bem mais recursos e operações, e outras formas de endereçamento determinadas por cada sistema operacional. No Linux, por exemplo, o sistema de arquivos foi herdado dos sistema Unix, onde qualquer dispositivo pode ser endereçado como um arquivo. Não há “letras” de unidade de disco, existem “devices” nomeados, utilizando prefixos específicos para acessar cada tipo de dispositivo, e outros recursos como links dinâmicos para arquivos e pastas, que recentemente também foram implementados no Windows.

Dentro do arquivo

Mesmo que todos os arquivos do disco contenham “Bytes” dentro, arquivos que contenham por exemplo uma imagem ou fotografia, possuem uma regra de armazenamento e interpretação destes bytes. Por exemplo, vamos pegar um formato de imagem mais “antigo”, como o BITMAP (Ou Mapa de Bits), usado no Windows e no (extinto) OS\2.

Na Wikipedia, acesse o link “BMP file format” (https://en.wikipedia.org/wiki/BMP_file_format), veja a seção “File Structure“. Nela é descrito como é o formato de armazenamento interno de uma imagem em um arquivo BMP. O arquivo começa a ser lido do primeiro byte, e existem sequências de bytes de tamanho fixo que indicam informações de como os demais dados estão dispostos, e a partir de um determinado ponto, os blocos de bytes possuem tamanhos variáveis, de acordo com as definições anteriormente especificadas no arquivo. Um arquivo de dados em formato DBF possui uma estrutura similar, onde no início do arquivo os primeiros blocos de bytes identificam quais as informações que estão armazenadas no arquivo.

Nível de complexidade de operações com arquivo

Agora, imagine o quão complexo seria se você tivesse que saber como as informações estão dispostas dentro do arquivo para poder acessá-las? Sim, daria um valente trabalho…. cada arquivo possui uma estrutura interna, e um erro ao ler ou gravar um dado pode corromper a informação no arquivo.

PAra tornar este processo mais fácil, as linguagens de programação que trabalham nativamente acesso a dados oferecem mecanismos de mais “alto nivel”, onde através de funções ou classes, você aciona operações como “ler um registro”, “posicionar em um registro”, “inserir um novo registro”, “alterar os dados de um registro”. Outra abordagem muito utilizada é usar um segundo software, um SGBD (Sistema Gerenciador de Banco de Dados), usando SQL (Structured Query Language), através das quais você monta sentenças praticamente “literais” e solicita ao SGDB a operação desejada, como “SELECT * FROM SA1010 WHERE A1_ATIVO = ‘S’ ORDER BY A1_NREDUZ” (Selecione todos os registros da tabela SA1010 onde o campo (coluna) A1_ATIVO seja igual a letra ‘S’ maiúscula, com o resultado ordenado pelo campo A1_NREDUZ).

Se você precisa trabalhar com um tipo de arquivo que contém imagens, e quer aplicar na imagem um efeito, como mudança de brilho ou cor, você pode escrever uma API sua, abrir o arquivo em modo ‘binário’ (lendo diretamente seu conteúdo em bytes para a memória), entender como aquele mapa de bytes representa a imagem e suas cores, e aplicar o efeito desejado recalculando as definições de cores. Ou, você pode usar um componente pronto, uma biblioteca de funções fornecida com a linguagem ou uma biblioteca de terceiros (existem bibliotecas pagas e gratuitas, com código aberto (Open Source) e código fechado, para diversas linguagens de programação. Naturalmente, vai ser bem mais fácil e rápido usar uma biblioteca pronta. Basta você pesquisar se a escolha em foco atende todas as suas necessidades e não tem alguma restrição que inviabilize seu uso no cenário onde a mesma será aplicada.

Conclusão

A introdução está ficando “longa”, mas uma vez assimilada esta base comum de conhecimento, ela serve para tudo o que foi construído sobre ela, apenas existindo algumas características e comportamentos diferenciados para algumas plataformas ou sistemas, o resto é “farinha do mesmo saco”. Quem gosta de TI, Análise de Sistemas, Engenharia de Software, Programação e ciências correlatas, precisa ter sede e vontade de aprender, e de se manter atualizado, sempre. Mesmo após tornar-se um especialista, é preciso estar de olhos e ouvidos abertos para a evolução constante em torno da área como um todo.

Novamente, agradeço a todos pela audiência, e desejo a vocês TERABYTES de sucesso 😉

Até o próximo post, pessoal 😀

Referências

BYTE. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Byte&oldid=43771720>. Acesso em: 2 nov. 2015.

ASCII. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=ASCII&oldid=43771464>. Acesso em: 2 nov. 2015.

Extended ASCII. (2015, April 1). In Wikipedia, The Free Encyclopedia. Retrieved 03:19, November 2, 2015, from https://en.wikipedia.org/w/index.php?title=Extended_ASCII&oldid=654535698

RAR (file format). (2015, October 30). In Wikipedia, The Free Encyclopedia. Retrieved 19:19, November 14, 2015, from https://en.wikipedia.org/w/index.php?title=RAR_(file_format)&oldid=688294797

General overview of the Linux file system. In The Linux Documentation Project. Retrieved 17:27, November 14, 2015, from http://www.tldp.org/LDP/intro-linux/html/sect_03_01.html

Introdução a Informática – Parte 01

Introdução

“Informática é um termo usado para descrever o conjunto das ciências relacionadas ao armazenamento, transmissão e processamento de informações em meios digitais, estando incluídas neste grupo: a ciência da computação, a teoria da informação, o processo de cálculo, a análise numérica e os métodos teóricos da representação dos conhecimentos e da modelagem dos problemas. Mas também a informática pode ser entendida como ciência que estuda o conjunto de informações e conhecimentos por meios digitais.” (1)

Em dois artigos anteriores, eu postei algo sobre Programação e sobre Análise de Sistemas. Seguindo a mesma linha, de “desmistificar” a aparente complexidade deste universo, eu reparei que alguns conhecimentos que eu considero como “base” para o entendimento dos sistemas computacionais são pouco abordados, ou são vistos com uma superficialidade muito grande.

E boa parte do que temos hoje em tecnologia de sistemas e processamento de dados é uma evolução natural desta base. Logo, eu considero fundamental saber um pouco mais da origem das coisas, e da evolução histórica da computação no último século, para entender um pouco mais no que ela é baseada, e por quê (e como) ela evoluiu a saltos gigantescos desde então.

Um pouco de História

Fiz meu primeiro curso de programação com 13 anos de idade, e meu primeiro curso de nível profissional de informática com 15 anos, em uma escola de informática em Botucatu, fundada por um ex-técnico de Software da IBM e sua esposa. Antes de sentarmos na frente de um IBM-PC e ligá-lo pela primeira vez, tivemos uma semana de aula sobre “Introdução a Informática”, para que todos tenham uma noção de o que é um micro-computador, o que têm dentro, e pelo menos uma noção de como as partes internas funcionam.

Particularmente eu achei aquilo extremamente interessante, pois na semana seguinte, quando fomos para a frente dos computadores, mesmo sem ter visto um, nós sabíamos o que precisava ser feito para ligá-lo, e o que acontecia desde o momento que você apertou o botão “ON” até aparecer o “Prompt” de comando do sistema operacional ( na época, “A:\>” ).

Seguindo esta linha, estou dando inicio a uma série de Posts, com a TAG “Introdução a Informática”, onde eu vou tentar trazer esse conhecimento para vocês, com a maior riqueza de detalhes que a minha memória permite, estendendo esta abordagem até os micro-computadores dos dias de hoje. Recomendo essa literatura a todos que estão iniciando seus estudos em computação, e estendo o convite a todos que tenham alguma curiosidade sobre isso.

A necessidade é a mãe da invenção

A necessidade de mensurar coisas é tão antiga quanto a história da civilização. Uma vez que o homem aprendeu a cultivar a terra, por volta do período neolítico (8 mil A.C.). Este período é considerado um importante avanço social, econômico e político, onde o homem descobre que é muito mais vantajoso agir em grupo do que individualmente.

Com a subsistência mais fácil, não tendo que sair para caçar todos os dias, os neolíticos tinham mais tempo para interagir entre si, então desenvolveram as primeiras atividades de lazer, descobrindo a arte da cerâmica e depois como comercializá-la. O surgimento da escrita e a criação do Estado pelas primeiras da Antiguidade marcou o final da era neolítica.

Desde então, sempre existiu a necessidade de mensurar coisas. Quantidade de sementes, frutos produzidos, criação de unidades monetárias (dinheiro) para facilitar relações comerciais e afins. Embora a matemática conhecida hoje — usando algarismos indo-arábicos (1234567890), com ponto decimal e frações date de 1000 D.C., a utilização dessa representação em algarismos decimais é datada de 2 A.C.

O Ábaco, um instrumento de cálculo que data de 5500 A.C., ele é considerado como uma extensão do ato natural de contagem com os dedos. E também usa o sistema decimal (onde usamos 10 algarismos para representar um número). O Ábaco ainda é usado hoje para ensinar a crianças somar e subtrair.

Invenções mecânicas

Buscando dar mais precisão, segurança e velocidade aos processos de operações numéricas, várias invenções mecânicas foram surgindo ao longo do tempo, como a máquina de operações de Pascal (Século XVII), o Tear de cartões perfurados de Jacquard (Século XVIII), o projeto da máquina analítica de Babbage, até a máquina eletromecânica de Herman Hollerith, um equipamento que usava cartões perfurados usado para calcular o censo populacional dos EUA de 1890 em APENAS um ano — o censo da década anterior demorou 7 anos … Através de um painel de fios em seu tabulador 1906 Type I, foi possível executar diferentes trabalhos sem a necessidade de ser reconstruído. Estes foram considerados os primeiros passos em direção à programação. Em 1911, quatro corporações, incluindo a firma de Hollerith, se fundiram para formar a Computing Tabulating Recording Corporation. Sob a presidência de Thomas J. Watson, ela foi renomeada para IBM (“International Business Machines”).

O papel da eletricidade

Mesmo sendo conhecidos desde a Grécia antiga, os fenômenos magnéticos e os fenômenos elétricos somente começaram a ser estudados e explicados cientificamente a partir do século XVII. No início do século XIX, Hans Christian Ørsted obteve evidência empírica da relação entre os fenômenos magnéticos e elétricos. A partir daí, os trabalhos de físicos como André-Marie Ampère, William Sturgeon, Joseph Henry, Georg Simon Ohm, Michael Faraday foram unificados por James Clerk Maxwell em 1861 por meio de equações que descreviam ambos os fenômenos como um só: o fenômeno eletromagnético. Esta unificação foi uma das grandes descobertas da física no século XIX. Com uma teoria única e consistente, que descrevia os dois fenômenos anteriormente julgados distintos, os físicos puderam realizar vários experimentos prodigiosos e inventos úteis, como a lâmpada elétrica (Thomas Alva Edison) ou o gerador de corrente alternada (Nikola Tesla)

Entre 1930 e 1940, George Stibitz, um pesquisador da Bell Labs percebeu que os relés eletromecânicos usados em centrais telefônicas poderiam ser usados para realizar operações sequenciais, e desenvolveu os primeiros circuitos de lógica digital usando relés. Os projetos de computadores usando relês da Bell foram usados pelos EUA para fins militares, usando 1400 relês e com memória de 10 números, eles eram usados para processamento de cálculos de direcionamento de artilharia anti-aérea. Os trabalhos de George Stibitz foram baseados nos princípios de álgebra booleana, concebidos por George Boole quase um século antes, mas que até o momento não tinham aplicação prática.

Com o surgimento da válvula termo-iônica, como uma forma de modulação direta de energia, bem mais rápida que um relê, todo o conhecimento adquirido até então foi aproveitado para a evolução desta tecnologia. Porém, as válvulas queimavam com muita facilidade, e consumiam muita energia para funcionar. Mas a velocidade de chaveamento de energia era tão mais rápida que um relê, que logo surgiram os primeiros computadores usando esta tecnologia. A universidade de Iowa em 1942 montou um dos primeiros computadores valvulados, não programável, mas funcional para resolver equações de primeiro grau.

O primeiro computador totalmente eletrônico e programável foi o ENIAC (Electronic Numerical Integrator And Computer). Construído durante a segunda guerra mundial na universidade da Pensilvânia, ficou pronto em 1946. Usava mais de 17 mil válvulas, consumia 150 kW, e pesava 27 toneladas …. mas era milhares de vezes mais rápido do que qualquer computador a relê. Por exemplo, o computador Harward Mark II (1947), que usava relês de alta velocidade, operava a uma frequência de 8 Hz (ciclos por segundo). O ENIAC operava a uma frequência vertiginosa de 5 mil Hz (5 KHz), o que permitia ele calcular aproximadamente 357 multiplicações de números de 10 dígitos por segundo. Hoje, um processador de um SmartPhone ultrapassa facilmente 1 GHZ ( Um BILHÃO de ciclos por segundo).

Onde estão os “zeros” e “uns” ?

O sistema de numeração binário (base 2), usa apenas dois algarismos para representar um valor: ‘0’ ou ‘1’. Em informática, a menor unidade de armazenamento de um dado é o “bit”, que coincidentemente também armazena os valores ‘0’ ou ‘1’. Quando agrupamos 8 bits, temos um Byte ( também chamado de “octeto” de bits). Utilizando um BIT, armazenamos 2 valores diferentes (0 ou 1). Usando 2 bits, armazenamos quatro sequências de bits diferentes ( 00, 01, 10 e 11). Usando 8 Bits (um Byte) podemos armazenar 2 ^ 8 sequencias diferentes, ou seja, 256 combinações diferentes de 0 e 1 (de 00000000 a 11111111). Convertendo isso para numeração decimal (ou base 10), temos 256 valores diferentes (entre 0 e 255).

E por que eu preciso saber disso ?

Bem, meu chapa … toda a base de programação e processamento de dados são sequencias de zeros e uns. Isto somente vai ser quebrado com a computação quântica, que surgiu como teoria a alguns anos, e atualmente os estudos na área estão tornando possível a criação do Hardware para permitir este tipo de processamento. A computação quântica deve quebrar paradigmas em campos de aplicação onde as máquinas tradicionais (utilizando lógica booleana) chegaram em um limite tecnológico “físico” de desempenho. A menor unidade de informação em um computador é um Byte, que nada mais é do que um agrupamento de 8 bits. Logo, todo o armazenamento de dados e fluxo de informações entre os componentes de um computador e seus componentes auxiliares (ou periféricos) serão sequências ou blocos de zeros e uns 😉

E onde entra a tal da “programação” ?

Programar significa fornecer uma lista de instruções para realizar uma determinada tarefa. Quando você compra um produto desmontado, que vêm com um manual de instruções de montagem, ele descreve em linguagem natural a lista de passos para você montar o produto. Isto pode ser considerado um programa, que ao ser executado na ordem certa e com os materiais corretos, resulta no produto montado.

Um programa de computador é exatamente isso. Qualquer programa de computador é composto de uma sequência de instruções e dados, regidos por uma gramática e um conjunto de operações especificas, cujo objetivo final é enviar ao processador do equipamento uma sequência de códigos binários que representem a sequência de processamento a ser executada. Nesta sequência de instruções podemos interagir com os periféricos do equipamento, como a interface de vídeo, o teclado, uma impressora, uma unidade de armazenamento de dados, etc.

Conclusão

Este tópico é só para “abrir o apetite”. No próximo vamos dar uma boa olhada no que tem dentro de um computador, o que é um “Sistema Operacional”, por quê precisamos de um, e depois o que acontece quando a gente liga um equipamento.

Desejo a todos uma boa leitura, e TERABYTES de sucesso 😀

Até o próximo post, pessoal 😉

Referências

INFORMÁTICA. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Inform%C3%A1tica&oldid=43615873>. Acesso em: 12 out. 2015.

ÁBACO. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=%C3%81baco&oldid=43500340>. Acesso em: 12 out. 2015.

ALGARISMOS INDO-ARÁBICOS. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Algarismos_indo-ar%C3%A1bicos&oldid=43058223>. Acesso em: 12 out. 2015.

ELETROMAGNETISMO. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Eletromagnetismo&oldid=43478266>. Acesso em: 12 out. 2015.

HERMAN HOLLERITH. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Herman_Hollerith&oldid=42754476>. Acesso em: 12 out. 2015.

George Stibitz. (2015, August 15). In Wikipedia, The Free Encyclopedia. Retrieved 07:19, October 12, 2015, from https://en.wikipedia.org/w/index.php?title=George_Stibitz&oldid=676218293

Harvard Mark II. (2014, September 19). In Wikipedia, The Free Encyclopedia. Retrieved 07:37, October 12, 2015, from https://en.wikipedia.org/w/index.php?title=Harvard_Mark_II&oldid=626186726

ENIAC. (2015, October 12). In Wikipedia, The Free Encyclopedia. Retrieved 07:44, October 12, 2015, from https://en.wikipedia.org/w/index.php?title=ENIAC&oldid=685327153

SISTEMA DE NUMERAÇÃO BINÁRIO. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: <https://pt.wikipedia.org/w/index.php?title=Sistema_de_numera%C3%A7%C3%A3o_bin%C3%A1rio&oldid=43459004>. Acesso em: 12 out. 2015.

Outras referências

http://www.infoescola.com/pre-historia/periodo-neolitico/

http://www.newtoncbraga.com.br/index.php/curiosidades/4856-cur008

http://www.techtudo.com.br/artigos/noticia/2012/09/computadores-a-valvulas.html

Escalabilidade e Performance – Stored Procedures

Introdução

Em um tópico anterior sobre “Escalabilidade e performance – Técnicas”, um dos tópicos falava sobre Stored Procedures, inclusive sugerindo que seu uso deveria ser minimizado. Vamos entrar neste tema com um pouco mais de profundidade neste tópico. Vamos começar com o clone do tópico abordado, e esmiuçar ele dentro do contexto do AdvPL e Protheus.

Minimize o uso de Stored Procedures

Este é um ponto aberto a discussão, depende muito de cada caso. Não é uma regra geral, existem pontos em um sistema onde uma stored procedure realmente faz diferença, mas seu uso excessivo ou como regra geral para tudo impõe outros custos e consequências. O Princípio 1 diria: “use apenas stored procedures”. No entanto, esta decisão pode causar grandes problemas para o Princípio 2 devido à escalabilidade. As Stored procedures têm a vantagem de ser pré-compiladas, e não há nada mais perto de um dado no Banco de Dados.

Porém Bancos de Dados transacionais são especializados em quatro funções: Sort, Merge, gerência de Locks e Log. A gerência de lock é uma das tarefas mais críticas para a implementação de algoritmos distribuídos, e este é o real motivo de existirem poucos Bancos de Dados que possam implementar a escalabilidade horizontal. Se as máquinas de Banco de Dados têm dificuldade de escalar horizontalmente, ela é um recurso escasso e precioso. Temos então que otimizar seu uso para não consumir excessivamente seus recursos a ponto de onerar os demais processos do ambiente. Isto acaba adiando a necessidade de escalar horizontalmente o SGBD.

Abordando a questão de desempenho

Se o algoritmo para processamento de um grande grupo de informações pode ser escrito dentro de uma Stored Procedure no próprio Banco de Dados, esta alternativa tende fortemente a ser a mais performática. Num cenário onde o algoritmo é escrito usando um programa sendo executado dentro do servidor de aplicação da linguagem, cada processamento que dependa da leitura de grupos de dados e tenha como resultado a geração de novos dados vai ser onerado pelo tempo de rede de tráfego destes dados, na ida e na volta. Logo, com uma base de dados modelada adequadamente, e uma stored procedure bem construída, ela naturalmente será mais rápida do que um processamento que precisa trafegar os dados pra fora do SGDB e depois receba novos dados de fora.

Porém, este recurso não deve ser usado como solução mágica para tudo. Afinal, o SGDB vai processar uma Stored Procedure mais rápido, pois ele não vai esperar um processamento ser realizado “fora dele”, porém o SGDB vai arcar com o custo de ler, processar e gravar a nova informação gerada. Se isto for feito sem critério, você pode mais facilmente esgotar os recursos computacionais do Banco de Dados, ao ponto da execução concorrente de Stored Procedures afetar o desempenho das demais requisições da aplicação.

Outras técnicas pra não esgotar o SGDB

Existem alternativas de adiar um upgrade no SGDB, inclusive em alguns casos as alternativas são a solução para você não precisar comprar um computador da “Nasa” …risos… Normalmente estas alternativas envolvem algum tipo de alteração na aplicação que consome o SGDB.

Réplicas de leitura

Alguns SGDBs permitem criar nativamente réplicas da base de dados acessadas apenas para consulta, onde as cópias de leitura são sincronizadas em requisições assíncronas. Existem muitas partes da aplicação que podem fazer uma leitura “suja”. Neste caso, a aplicação pode ler os dados de uma base sincronizada para leitura, e os processos que precisam de leitura limpa são executados apenas na instância principal. Para isso a aplicação precisaria saber qual e o banco “quente” e qual é o espelho, para fazer as coisas nos lugares certos.

Caches

Outra alternativa é a utilização de caches especialistas, implementados na própria aplicação. Utilizando por exemplo uma instância de um “MemCacheDB” em cada servidor, cada aplicação que pode reaproveitar a leitura de um dado com baixo índice de volatilidade (dados pouco atualizados ou atualizados em momentos específicos), poderiam primeiro consultar o cache, e somente se o cache não têm a informação desejada, a aplicação acessa o banco e popula o cache, definindo uma data de validade. Neste caso, o mais legal a fazer é definir um tempo de validade do cache (Expiration Time). E, paralelo a isso, para informações de baixa volatilidade, a rotina que fizer update desta informação pode eliminar ela do cache no momento que um update for realizado, ou melhor ainda, pode ver se ela se encontra no cache, e em caso afirmativo, ela mesma poderia atualizar o cache 😉

Sequenciamento de operações

Operações de inserção ou atualização de dados que não precisam ser refletidas em real-time no SGDB podem ser enfileiradas em pilhas de requisições, e processadas por um processo dedicado. O enfileiramento de requisições não essenciais em tempo real limita o consumo de recursos para uma determinada atividade. Caso a pilha se torne muito grande, ou um determinado processo dependa do esvaziamento total da pilha, podem ser colocados mais processos para ajudar a desempilhar, consumindo mais recursos apenas quando estritamente necessário.

Escalabilidade Vertical

Devido a esta questão de dificuldade de escalabilidade de bancos relacionais horizontalmente, normalmente recorremos a escalabilidade vertical. Escalamos horizontalmente as máquinas de processamento, colocando mais máquinas menores no cluster e balanceando carga e conexões, e quando a máquina de banco começa a “sentar”, coloca-se uma máquina maior só para o SGDB, com vários processadores, discos, memória e placas de rede. Mas tudo tem um limite, e quando ele for atingido, a sua máquina de Banco de Dados pode ficar mais cara que o seu parque de servidores de processamento.

Dificuldade de Implementação

Usar caches e réplicas e pilhas não é uma tarefa simples, fatores como a própria modelagem da base de dados podem interferir negativamente em algumas destas abordagens. Não se pode colocar tudo em cache, senão não vai ter memória que aguente. O cache é aconselhável para blocos de informações repetidas constantemente requisitadas, e de baixa volatilidade. Também não é necessário criar pilhas para tudo que é requisição, apenas aquelas que não são essenciais em tempo real, e que podem ter um delay em sua efetivação.

Stored Procedures no AdvPL

O ERP Microsiga disponibiliza um pacote de Stored Proecures, aplicadas no SGDB em uso por um programa do módulo “Configurador” (SIGACFG). As procedures foram desenvolvidas para funcionalidades específicas dentro de cada módulo, normalmente aquelas que lidam com grandes volumes de dados, e foi possível criar um algoritmo que realize o processamento dentro do SGDB, trafegando menos dados “pra fora” do Banco de Dados. Normalmente um pacote de procedures é “casado” com a versão dos fontes do Repositório, pois uma alteração na aplicação pode envolver uma alteração na procedure. Os ganhos de performance são nítidos em determinados processamentos, justamente por eliminar uma boa parte do tráfego de informações para fora do SGDB durante os processos.

Conclusão

Dado o SGDB como um recurso “caro e precioso”, como mencionado anteriormente, a utilização de recursos adicionais como réplicas e caches, ajuda a dar mais “fôlego” pro SGDB, você consegue aumentar o seu parque de máquinas e volume de dados processados sem ter que investir proporcionalmente na escalabilidade do SGDB. E em tempos de “cloudificação” , SaaS e IaaS, quando mais conseguimos aproveitar o poder computacional que temos em mãos, melhor !

Desejo novamente a todos TERABYTES de Sucesso 😀

Até o próximo post, pessoal 😉

Referências

“Escalabilidade e performance – Técnicas”