Google+ Followers

segunda-feira, 23 de abril de 2012

Protheus :: "Script Linux CentOs/RedHat start/stop/restart TotvsdbAccess/TopConnect"

Para aqueles que usam o SGBD da Oracle e o TotvsdbAccess/TopConnect em Linux, um pequeno "script" para "start,stop and restart" do "daemon" do TotvsdbAccess/TopConnect. Adapte-o às suas necessidades e, atenção para a seguinte linha:

NLS_LANG='Brazilian Portuguese_Brazil.WE8ISO8859P1'; export NLS_LANG

Altere-a conforme a configuração de NLS_LANG configurada no SGBD

#script file: dbacces
#!/bin/bash
# Avoid using root's TMPDIR

unset TMPDIR

RETVAL=0

TMP=/tmp; export TMP
TMPDIR=$TMP; export TMPDIR
ORACLE_BASE=/u01/app/oracle; export ORACLE_BASE
ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1; export ORACLE_HOME
PATH=/usr/sbin:$PATH; export PATH
PATH=$ORACLE_HOME/bin:$PATH; export PATH
LD_LIBRARY_PATH=$ORACLE_HOME/lib32:/lib:/usr/lib; export LD_LIBRARY_PATH
NLS_LANG='Brazilian Portuguese_Brazil.WE8ISO8859P1'; export NLS_LANG

start() {

ulimit -n 65526
ulimit -s 1024
ulimit -c 0
ulimit -f unlimited
ulimit -v unlimited
ulimit -u unlimited
ulimit -t unlimited
ulimit -m unlimited
ulimit -l unlimited
ulimit -d unlimited

KIND="TotvsdbAccess"
echo -n $"Iniciando $KIND: "
cd /totvs/dba/dbaccess/multi
/totvs/dba/dbaccess/multi/totvsdbaccess &
RETVAL=$?
echo
}

stop() {
KIND="TotvsdbAccess"
echo -n $"Parando $KIND: "
killall /totvs/dba/dbaccess/multi/totvsdbaccess
RETVAL=$?
echo
echo ""
return $RETVAL
}

restart() {
stop
start
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 2
esac

exit $?

quarta-feira, 21 de janeiro de 2009

Protheus :: Programação Básica em Advpl “Combo XL”

Classificando um Array Unidimensional e Bidimensional:

Da mesma forma que podemos passar um bloco para aScan(), também podemos passar um bloco para aSort(). A função aSort() classifica o Array passada por parâmetro, na ordem ascendente, mas, em substituição, podemos passar para ela um bloco de codificação para fazer a comparação. Quando fizermos isso, em vez de aSort() comparar os elementos internamente, ela passará para o Bloco de Codificação os dois elementos necessários para a comparação. A avaliação do Bloco, então, retornará verdadeiro se os dois elementos estiverem classificados em ordem; caso contrário, retornará falso. Quando o Bloco retornar falso, aSort() trocará os dois elementos.

É importante observar que aSort() passa o elemento com o número mais baixo do Array como primeiro parâmetro do bloco; desta forma, saberemos quem é quem! É igualmente importante notar que não teremos conhecimento dos dois elementos que estaremos comparando; só saberemos que o primeiro parâmetro é um elemento com número menor do que o segundo. aSort() assegura a classificação do Array, determina os elementos a comparar e sabe quando a classificação termina.

Para classificar um Array Unidimensional em ordem ascendente a passagem do bloco é opcional. Mas, para ordená-lo em ordem descendente, teremos passar o bloco informado a regra para a classificação:

Exemplos de classificação de Array unidimensional

Local aNomes := {;
“Vinicius”,;
“Aline”,;
“Marinaldo”,;
“Michele”,;
“Camara”,;
“Andre”,;
“Valeria”,;
“João”,;
“Adriana”;
}
Local aSvNomes := aClone( aNomes ) //Efetua a cópia do array
Local bSort

//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes nao ordenados” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

//Ordenando o Array Unidimensional em ordem ascendente sem
//utilizar o Bloco de Codificação:
aSort( aNomes )

//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes ordenados sem Bloco” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

aNomes := aClone( aSvNomes ) //Restaurando aNomes

//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes nao ordenados” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

//Monta Bloco para Classificação em Ordem Ascendente
bSort := { |x,y| x < y }
aSort( aNomes , NIL , NIL , bSort )

//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes ordenados ( asc ) utilizando Bloco” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

//Monta Bloco para Classificação em Ordem Descendente
bSort := { |x,y| x > y }
aSort( aNomes , NIL , NIL , bSort )

//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes ordenados ( desc ) utilizando Bloco” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

aNomes := aClone( aSvNomes ) //Restaurando a Nomes

//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes nao ordenados” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

//Ordenando o Array em ordem Ascendente considerando apenas //alguns elementos
aSort( aNomes , 1 , 3 , { |x,y| x < y } )
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes Ordenados. Elem 1 a 3” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )

Exemplos de classificação de Array Bidimensional:

Local aStru := {;
{ "SNome" , "C" , 10 , 0 },;
{ "PNome" , "C" , 10 , 0 },;
{ "Ender1" , "C" , 30 , 0 },;
{ "Ender2" , "C" , 40 , 0 } ;
}
Local aSvStru := aClone( aStru ) //Efetua a cópia do array
Local bSort

/Mostrando as informações do Array no Console
ConOut( "Lista nao ordenada" )
ConOut( “” )
aEval( aStru , { |aElem| ConOut( aElem[1] ) } )
ConOut( “’ )

//Definindo bloco para ordenacao ascendente
bSort := { |x,y| x[1] < y[1] }

//Ordenando Array
aSort( aStru , NIL , NIL , bSort )

//Mostrando as informações do Array no Console
ConOut( "Lista ordenada ascendente" )
ConOut( "" )
aEval( aStru , { |aElem| ConOut( aElem[1] ) } )
ConOut( "" )

//Definindo bloco para ordenacao descendente
bSort := { |x,y| x[1] > y[1] }

//Ordenando Array
aSort( aStru , NIL , NIL , bSort )

//Mostrando as informações do Array no Console
ConOut( "Lista ordenada descendente" )
ConOut( “” )
aEval( aStru , { |aElem| ConOut( aElem[1] ) } )
ConOut( “” )

Protheus :: Programação Básica em Advpl “Combo XXXIX”

Buscando um Array Bidimensional:

Existem várias funções de manipulação de Array. Uma delas é a aScan() que serve para efetuar pesquisa em um Array e retorna o numero do elemento do Array ( Índice ) se a pesquisa foi satisfeita e Zero (0) caso contrário. Essa função poderá ser utilizada tanto em Arrays unidimensionais como em Arrays Bidimensionais. aScan() recebe quatro parâmetros: O primeiro é o Array a ser pesquisado, o segundo é um Bloco de Código com as instruções para pesquisa, o terceiro é a posição inicial ( índice ou elemento inicial ) para pesquisa e o quarto é a posição final para pesquisa ( índice ou elemento final ). O primeiro parâmetro o Array a ser pesquisado é obrigatório, já do segundo ao quarto são opcionais. O segundo parâmetro deve ser passado quando o array a ser pesquisado for um array Multidimensional ou Bidimensional e, quando o Bloco de Codificação for passado aEval() se comportará de maneira diferente do usual. Em vez de aScan() comparar internamente elementos do Array com o que estamos procurando, ela permitirá que o bloco passado como parâmetro faça isso. aScan(), como aEval(), faz um Loop em cada elemento do Array passando cada elemento por sua vez para o bloco de codificação. O bloco de codificação, passado como parâmetro, deve retornar um valor lógico, indicando se o elemento que aScan() acabou de passar era o que estávamos procurando; .T. ( true = verdadeiro ) significa que o atual elemento é o que estamos procurando; .F. ( false = falso ) significa que não é. Se a avaliação do Bloco de Código for verdadeira, aScan() interrompe a busca e retorna o número do atual elemento. Se a avaliação do Bloco de Código for falsa, aScan() salta para o próximo elemento e efetua a avaliação do Bloco mais uma vez. aScan() prossegue seu processo de busca até terminar todos os elementos, em cujo caso retornará um Zero (0), ou até a avaliação do Bloco retornar verdadeiro. aScan() é semelhante a aEval(), exceto pelo fato de não existir uma maneira de interromper aEval() prematuramente.

Exemplos:

Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}

Local bExemplo := { |uElem| ConOut( uElem ) }

aEval( aStru[1] , bExemplo )

/*/
Neste Caso, processamos apenas os elementos do sub-array aStru[1] e Teremos a seguida saída no Console do Server:

SNome
C
10
0

/*/

Para processar todos os elementos do array poderíamos fazê-lo da seguinte forma:

Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}

Local bExemplo := { |uElem| ConOut( uElem ) }
Local nLoop
Local nLoops

//Utilizando um Loop For/Next
nLoops := Len( aStru )
For nLoop := 1 To nLoops
aEval( aStru[ nLoop ] , bExemplo )
Next nLoop

//Ou aEval de forma recursiva
aEval( aStru , { |aElem| aEval( aElem , bExemplo ) } )

/*/
Neste Caso, processamos Todos os elementos do Array e seus sub-arrays. Teremos, dessa forma, a seguida saída no Console do Server:

SNome
C
10
0
PNome
C
10
0
Ender1
C
30
0
Ender2
C
40
0
/*/

Quando usarmos um Bloco de Codificação com aScan(), ela passara cada elemento por vez para o bloco. Se esse elemento for outra Array o parâmetro formar conterá uma referência para esse Array. O bloco poderá então usar um só subscrito com seu parâmetro para acessar determinado elemento. Por exemplo, o seguinte fragmento de código localiza o número do elemento do Array que descreve o campo “PNome”

Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
//Monto bloco para comparação exata
Local bAscan := { |aCpo| Upper( AllTrim( aCpo[1] ) ) == “PNOME” }
Local nPos

nPos := aScan( aStru , bAscan ) //nPos irá retornar 2
IF ( nPos > 0 )
ConOut( "A Posicao eh:" + Str( nPos ) )
ConOut( "Conteudo:" + aStru[ nPos , 1 ] )
Else
ConOut( "Elemento nao encontrado" )
EndIF

Protheus :: Programação Básica em Advpl “Combo XXXVIII”

Processando Array com aEval():

aEval() é uma “internal function” que é utilizada para processar variáveis do tipo Array. Em sua forma mais simples usa dois parâmetros, um Array e um bloco de codificação. aEval() simplesmente faz um Loop em cada elemento do Array, passando um de cada vez, como um parâmetro para o Bloco de Codificação. Se o Array contém 5 elementos, aEval() avalia o bloco 5 vezes. Como exemplo, a seguinte chamada a aEval() mostra no Console do Server cada elemento do Array.

Local aArray := {“Marinaldo”,“Paulo Lira”,“Paulo Martins”,“Sergio”,“Henry” }

aEval( aArray , { |cElem| ConOut( cElem ) } )

Observamos que aEval() está avaliando o bloco de codificação, passando um parâmetro por vez. Denominamos o parâmetro como cElem, mas ele pode ser atribuído com qualquer nome.

aEval() é, realmente, uma função muito simples. Poderíamos até criar a nossa. Por exemplo:

User Function aEval( aArray , bEval )

Local nLoop
Local nLoops

nLoops := Len( aArray )
For nLoop := 1 To nLoops
Eval( bEval , aArray[ nLoop ] )
Next nLoop

Return( NIL )

E se usarmos a nossa função para processar um array teríamos o mesmo resultado de aEval().

Local aArray := {“Marinaldo”,“Paulo Lira”,“Paulo Martins”,“Sergio”,“Henry” }

U_aEval( aArray , { |cElem| ConOut( cElem ) } )

Na prática usaremos a função interna ( bem mais rápida ), mas tivemos uma idéia de como ela pode ser implementada em Advpl.

Protheus :: Programação Básica em Advpl “Combo XXXVII”

Arrays Multidimensionais:

Examine a seguinte declaração de um array bidimensional:

Local aValores[ 3 , 2 ]

/*/
que também pode ser declarado como:
Local aValores := Array( 3 , 2 )
Local aValores := { { NIL , NIL } , { NIL , NIL } , { NIL , NIL } }
Local aValores := { Array( 2 ) , Array( 2 ) , Array( 2 ) }
/*/

Em Advpl essa(s) declaração(ões) serão implementadas como um array de três elementos do tipo array e cada array com dois elementos ( onde os valores iniciais são NIL )

Podemos testar da seguinte forma


ValType( aValores ) //A
Len( aValores ) //3
ValType( aValores[1] ) ) //A
Len( aValores[1] ) //2
aValores[1,1] //NIL
ValType( aValores[1,1] ) //U

Conforme demonstrado acima, aValores é uma referência a um Array de Três elementos, em que cada elemento é uma referência à outra array de dois elementos.


Cada elemento de aValores é simplesmente outro array e podemos testá-lo dessa forma. Por exemplo, para localizar aValores[1] passe-o para a função aScan(), como em:

nPos := aScan( aValores[1] , { |x| x == “Naldo” } )

Para classificar aValores[2], passe-o para aSort() com:

aSort( aValores[2] )

ou

aSort( aValores[2] , NIL , NIL , { |x,y| x < y } )

Abaixo segue a estrutura de memória de um array bidimensional.


É possível referenciar um elemento do array aValores como aValores[1,1] ou aValores[1] mas, aValores[ , 1 ], apesar de não ocasionar erro durante a compilação gerará erro durante a execução. Isso significa que não podemos classificar, localizar ou selecionar colunas diretamente de um array bidimensional.

Nota: Em Advpl os Arrays Multidimensionais são implementados como Arrays de Arrays.

Protheus :: Programação Básica em Advpl “Combo XXXVI”

Elementos de Arrays como Parâmetros:

Em Advpl os elementos do array são sempre passados por valor. Para podermos modificar um elemento do array passado como parâmetro, devemos preceder a variável com o operador de referência @, conforme fragmento de código abaixo.

Local aTeste[1]

aTeste[1] := "Valor Antigo"
Alert( aTeste[1] ) //”Valor Antigo”

aTeste( aTeste[1] )
Alert( aTeste[1] ) //”Valor Antigo”

aTeste( @aTeste[1] )
Alert( aTeste[1] ) //”Novo Valor”

Static function aTeste( cTeste )

cTeste := "Novo Valor"

Return( NIL )

Protheus :: Programação Básica em Advpl “Combo XXXV”

Passando Arrays por Referência:

Verificamos que as variáveis do tipo Array são realmente passadas por valor. Mas podemos, também, passá-las por referência. Para que isso aconteça, temos que preceder o parâmetro real com o símbolo @. Dessa forma, poderemos mudar os elementos do array como antes, e a rotina continuará recebendo uma referência aos elementos do array. Mas, neste caso, a diferença é que, agora, podemos mudar aquilo a que o array se refere, uma vez que a própria variável array foi passada por referência. Podemos ilustrar isso da seguinte forma:

Local aNomes := { “P.A.Cabral” , “Santos Dumont” }

U_Test( @aNomes )

Alert( aNomes[1] ) // “Einstein”

User Function Test( aFormal )

Alert( aFormal[1] ) // P.A.Cabral

aFormal := { “Einstein” , “Leibiniz” }

Return( NIL )

Se quisermos que a rotina chamada nunca altere o valor do array original, podemos forçar que ela sempre receba uma cópia do array ao invés do próprio array. Isso pode ser feito utilizando a função aClone() da seguinte forma:

Local aNomes := { “P.A.Cabral” , “Santos Dumont” }

U_Test( aClone( aNomes ) )

Alert( aNomes[1] ) // “P.A.Cabral”

User Function Test( aFormal )

Alert( aFormal[1] ) // P.A.Cabral

aFormal[1] := “Einstein”

Alert( aFormal[1] ) //“Einstein”

Return( NIL )

A função aClone() para uma cópia do Array criando uma nova referência. Dessa forma, garantimos, na função chamadora a integridade do array mas, na função chamada podemos alterar o conteúdo da maneira que quisermos.

Para exemplificar melhor a passagem por referência, tomemos como exemplo o seguinte fragmento de código que referência uma variável do tipo numérica.

Local nVar := 1
Local cVar

U_Test( @nVar )
cVar := Str( nVar )
Alert( cVar ) //Aqui nVar terá o valor 3

User Funcion Test( nFormal )
nFormal := 3
Return( NIL )

Passamos nVar por referência para a função U_Test(); portanto, no retorno, nVar foi alterada. Um aspecto importante a observar é que, dentro da função U_Test(), não sabemos se nFormal foi passado por referência ou por valor. Apenas fizemos a atribuição. Este processo é diferente em linguagens como C, que não suportam envio por referência. Na linguagem C, se quisermos permitir que uma rotina altere um parâmetro, precisaremos passar o endereço do parâmetro. Conseqüentemente, a rotina receptora receberá um ponteiro para a verdadeira variável e a modificará por vias indiretas. Neste caso a rotina receptora precisa saber como o parâmetro foi passado ( o que não acontece em Advpl ).

Em Advpl ocorre uma via indireta “implícita” quando fazemos uma atribuição a um parâmetro formal que a rotina chamadora passa por referência. Isso poderá ser visualizado se imaginarmos que a codificação em Advpl recebesse um ponteiro para um parâmetro real e, toda vez que mudássemos um parâmetro passado por referência, estaríamos alterando o parâmetro real. O compilador ou o sistema, na execução, estarão implementando a via indireta.

Mas como isso se aplica aos arrays passados por referência? Já vimos que os arrays são implementados como ponteiros; portanto, ao passar um array por referência, na verdade, estamos passando um ponteiro para um ponteiro. O Advpl manipula a via indireta quando acessamos os elementos, mas, como recebemos uma referência à variável do array, podemos mudar aquilo para o qual o array aponta, além do seu conteúdo.

Protheus :: Programação Básica em Advpl “Combo XXXIV”

Arrays como Parâmetro:

Ao escrever uma função nós a definimos em termos de parâmetros. Chamamos esses parâmetros de Parâmetros Formais da função. Quando chamamos a função, fornecemos os parâmetros que denominamos Parâmetros Reais.

Quando passamos um Array como parâmetro, o Advpl faz o parâmetro formal apontar para o mesmo local indicado pelo parâmetro real. Dessa forma, poderemos mudar os elementos do Array fazendo atribuições a seus parâmetros.

Se o parâmetro real tiver um escopo Public ou Private, ele ficará também visível dentro da rotina chamada. Portanto, poderemos usar o operador == para verificar se eles realmente apontam para o mesmo array. Exemplo:

Private aNomes := { “P.A.Cabral” , “Santos Dumont” }

Test( aNomes )

Function Test( aFormal )
Return( aFormal == aNomes ) //.T.(apenas porque se referem ao //mesmo ponteiro)



Podemos, concluir, a partir disto, que os array são passados por referência;
acima de tudo, podemos mudar seus elementos. Entretanto, precisamos distinguir entre a própria variável array e os elementos a que ela se refere. Ao passar um array, como demonstrado, estamos passando uma referência aos elementos do array; conseqüentemente, a rotina receptora poderá alterá-los por meio de referência. Entretanto, ela pode mudar a própria referência? Ela pode fazer a referência apontar para outra array?

Examinemos o seguinte fragmento de programa:

Local aNomes := { “P.A.Cabral” , “Santos Dumont” }

U_Test( aNomes )

Alert( aNomes[1] ) // P.A.Cabral

User Function Test( aFormal )

Alert( aFormal[1] ) // P.A.Cabral

aFormal := { “Einstein” , “Leibiniz” }

Return( NIL )

A função U_Test() atribui seu parâmetro formal, aFormal, a um novo Array, mas isto não afeta o que aNomes indica. Ela simplesmente cria um novo Array e faz aFormal apontar para ele; aNomes permanece inalterado. É como se tivéssemos atribuído um tipo de dado diferente a aFormal – aNomes continua inalterado no retorno.

Disso podemos concluir que os Arrays são passados por valor! Não podemos mudar o parâmetro. Parece confuso mas: a variável array é passada por valor. Isto significa que não podemos mudar “aquilo para o qual ela estiver apontando”. Entretanto, como as variáveis de arrays são simples referências para os arrays, podemos mudar o conteúdo dos elementos do array, uma vez que sua rotina recebe uma referência a eles.

Protheus :: Programação Básica em Advpl “Combo XXXIII”

Estruturas de Dados usando Arrays:

Podemos dizer que em Advpl os Arrays são tratados como referências ou ponteiros. Eles podem ser utilizados para armazenar estruturas de dados complexas como outros arrays, dados numéricos, strings, objetos e até blocos de codificação.

Arrays como Referência:

Ao declarar um array de nElementos, na verdade, estamos criando duas estruturas de memória. O Advpl cria uma estrutura para conter a própria variável e depois uma seqüência de estruturas para armazenar cada elemento do Array. Imagine uma variável contendo um array como separada dos próprios elementos do array. Pense na variável como se estivesse “apontando” ou “citando” os elementos. Presumindo-se a seguinte declaração:

Local aNomes[4]

Poderíamos afirmar que aNomes refere-se ao primeiro elemento do Array.



Em Advpl podemos fazer com que outra variável Array faça referência ( ou aponte ) para uma outra variável Array. Neste caso elas serão equivalentes.

Ex.:

Local aNomes[4]
Local aVar

aVar := aNomes


Qualquer alteração efetuada nos em aNomes será automaticamente refletida em aVar e vice-e-versa.

Podemos utilizar o operador == para determinar se duas variáveis do tipo Array se referem a um mesmo Array. Este operador não compara os elemento por elemento do Array; em vez disso, ele retorna verdadeiro se elas se referirem à mesma Array; caso contrário, falso. Por Exemplo:

Local aNomes
Local aNomesNovos
Local aVar

aNomes := { “P.A.Cabral” , “Santos Dumont” }
aNomesNovos := { “P.A.Cabral” , “Santos Dumont” }
aVar := aNomes

aVar == aNomes // .T. (apenas porque referem-se ao mesmo ponteiro)
aNomes == aNomesNovos //.F.
aVar == aNomesNovos //.F.

Embora os elementos dos Arrays aNomes e aNomesNovos contenham os mesmos valores, eles são arrays distintas ( cada um ocupando uma referência diferente na memória ) e, por isso, o operador == retorna falso.

Nota: O operador == é uma sobreposição. Se for utilizado com string de caracteres, ele retornará verdadeiro se as duas strings foram exatamente iguais. Se for utilizado com duas variáveis arrays, ele retornará verdadeiro se elas se referirem aos elementos do mesmo array.

Existem duas funções, desenvolvidas em AdvPl, que comparam dois arrays, elemento por elemento, e retorna .T. se forem iguais ou .F. caso contrário.

ArrayCompare() e fCompArray()

Usando o mesmo exemplo acima, se utilizarmos ArrayCompare() ou fCompArray() teremos:

Local aNomes
Local aNomesNovos
Local aVar

aNomes := { “P.A.Cabral” , “Santos Dumont” }
aNomesNovos := { “P.A.Cabral” , “Santos Dumont” }
aVar := aNomes

ArrayCompare( aVar , aNomes ) // .T.
ArrayCompare(aNomes , aNomesNovos ) //.T.

fCompArray( aVar , aNomes ) // .T.
fCompArray (aNomes , aNomesNovos ) //.T.

Neste caso ArrayCompare() e fCompArray() retornarão sempre .T. uma vez que os elementos no Array são iguais.

Protheus :: Programação Básica em Advpl “Combo XXXII”

Parâmetros de Blocos de Codificação:

As barras verticais utilizadas na montagem do Bloco de Codificação são as delimitadoras das especificações dos parâmetros formais e o que caracteriza a Montagem de um bloco de código, se elas não forem utilizadas o sistema interpretará que o valor que está sendo atribuído à variável é um array e não um Bloco de Código. A definição e uso desses parâmetros é opcional mas as barras não.

Exemplo:

bVar := { | nVar1 , nVar2 | nVar1 + nVar2 }
nRetult := Eval( bVar , 10 , 20 )

Quando Eval avaliar o bloco de codificação nVar1 terá o conteúdo 10 e nVar2 terá o conteúdo 20. Como em uma função, os parâmetros deverão ser passados na seqüência em que foram declarados. Outro exemplo poderia ser:

bVar := { |nVar1,nVar2,cVar3,aVar4| U_Teste(nVar1,nVar2,cVar3,aVar4)}
Eval( bVar , 10 , , “C” , {} )

Neste caso, Eval, na avaliação do bloco de codificação, irá efetuar a chamada à função U_Teste() com passagem de parâmetros. Observe que o segundo parâmetro não foi passado, sendo assim, U_Teste() receberá, respectivamente: 10, NIL, “C” e {} que serão os conteúdos reais das variáveis nVar1, nVar2, cVar3 e aVar4.

Protheus :: Programação Básica em Advpl “Combo XXXI”

Blocos de Codificação:

Os Blocos de Codificação ou CodeBlock são variáveis que utilizamos para armazenar fragmentos de codificação do programa. Esses fragmentos podem ser passados para funções que irão executá-los.

Criamos um bloco de codificação à partir da seguinte sintaxe:

bVar := { || Teste() }

O caractere { significa “inicio do bloco de codificação” e o caractere “}”, “fim do bloco de codificação”. As duas barras verticais ( || ) delimitam a lista de parâmetros formais do bloco de codificação.

A variável bVar pode ser Local, Static, Private ou Public; não faz diferença. Ela só contém um fragmento de codificação e quando avaliado ele chama a função Test()

Para avaliar – ou executar – um bloco de código, use a função Eval(), como em: nResult := Eval( bVar ) esta chama a função Test(). Como toda função Eval() retorna um resultado ( que é o último elemento do bloco de código )

Ex.:

bRetVal := {|nVar1,nVar2,nVar3|nVar2:=nVar1+nVar2, nVar2*nVar3}
nResult := Eval( bRetVal , 1 , 2 , 3 )

//Após avaliar o bloco, Eval retornará para nResult 9 que é o resultado //da operação definida no bloco de código.

Protheus :: Programação Básica em Advpl “Combo XXX”

LOCAL, STATIC e MACROS:

As variáveis Local e Static não são mantidas na tabela de símbolos de execução, portanto, os programas que as utilizam executam com mais velocidade e menos memória. Uma conseqüência disto, conteúdo, é que não poderemos acessá-las a partir de expansão de macro. O operador de macros & ou a internal function __ExecMacro() compilam a codificação, durante o processo de execução, à partir de uma string de caracteres contendo uma expressão. Se a expressão citar uma variável, o sistema na execução precisará encontrá-la na tabela de símbolos. O problema com as Locai e Static, evidentemente, é que o sistema, por não armazená-las na tabela de símbolos, nas as encontra fazendo com que ocorra erro durante a execução do sistema se foram expandidas por macro.

Por exemplo:

nVar := 1
cVar := “nVar”

Alert( Str( &( cVar ) ) //ou Alert( Str( __ExecMacro( cVar ) )

Se nVar for uma variável Local, o programa falhará. Observe que cVar pode ser uma variável Local ou Static; mas só que qualquer coisa contida na string da expressão não pode ser uma Local nem uma Static. Uma forma de contornar isso seria usando blocos de codificação para poder referir-se a variável Local ou Static definidas em outro programa ou função.

Nota: Não é possível referir-se a variável Local ou Static dentro de uma expansão de macro.

A função Type() usa macros internamente. Use a função Type() para determinar o tipo de uma variável. Envie o nome da variável entre aspas, como em:

nVar := 1
cType := Type( “nVar” )

Alert( cType ) //”N”


O problema é que, se nVar for uma variável Local ou Static, a expansão interna da macro não a encontrará na tabela de símbolos e a função Type() retornará “U”, indicando que a variável não está definida.

Existe uma função semelhante, a ValType(). As principais diferenças entre ValType() e Type() são que ValType() realmente opera com variáveis Local e Static e não é preciso enviar os parâmetros de ValType() entre aspas, como em:

Local nVar := 1
Local cType := ValType( nVar )

Alert( cType ) //”N”

Pelo fato de não ser preciso colocar o parâmetro de ValType() entre aspas, a variável citada precisa ser declarada, caso contrário ocorrerá erro durante a execução. Uma variável pode ser declarada incluindo-a numa lista de parâmetros formais, seja como Private, atribuindo-se um valor a ela, seja como uma instrução de declaração explícita. Se ela não for declarada obteremos um erro de execução por estar tentando passar uma variável não definida para uma função. Observe, entretanto, que ValType() ainda retornará “U” se enviarmos a ela uma variável declarada, mas indefinida como em:

Local nVar
Local cType := ValType( nVar )

Alert( cType ) //”U”

Isso ocorre porque as variáveis, Private, Local e Static declaradas e não inicializadas tem sempre um valor NIL. Portanto, estamos passando, na realidade, NIL para ValType() e ela retornará um “U”.

Protheus :: Programação Básica em Advpl “Combo XXIX”

STATIC e PUBLIC:

Estas duas instruções também declaram variáveis. PUBLIC declara variáveis que ficam visíveis durante todo o programa. Os problemas que examinamos com relação as variáveis Private também ocorrem com as variáveis Public, contudo com mais abrangência. Em vez de isolados a rotinas chamadas a partir de uma função, os problemas ocorrem na aplicação inteira.

As variáveis STATIC podem ajudar a solucionar este problema. Existem dois tipos de variáveis static: internas e externas. A diferença está na abrangência. As variáveis static em nível de arquivo abrangem o arquivo: elas ficam visíveis em qualquer função ou rotina dentro do arquivo de programa no qual estão declaradas. As variáveis static declaradas dentro de uma função ( static internas ) tem escopo local, exatamente como as variáveis local.

Obs.: No Protheus, as variáveis Static criadas à partir do menu, serão liberadas sempre que se voltar para o menu. Já as Static criadas durante a entrada do sistema serão mantidas enquanto o sistema estiver sendo usado.

A característica principal da variável Static é que ela pode sair do escopo e não conseguirmos mais acessá-la, mas, na próxima vez que em que entrarmos no programa ou função que a declarou ela terá o mesmo valor da última vez em que o programa ou função foi executado. Exemplo:

TEST1.PRG

Static nVar := 0 //Static em nível de arquivo ( externa )

User Function A()

Alert( Str( nVar ) ) //nVar será zero aqui...
nVar := 10

Return( NIL )

User Function B()

Alert( Str( nVar ) ) //nVar será 10 aqui...

Return( NIL )

User Function C()

Static nVar //Static em nível de função ( interna )

DEFAULT nVar := 0

Return( ++nVar )

TEST2.PRG

User Function Test() //nVar não está disponível em Test2()

Local nLoop
Local nLoops
Local nRetC

U_A() //aqui acessando a variável Static Externa
U_B() //aqui acessando a variável Static Externa

nLoops := 15
For nLoop := 1 To nLoops
nRetC := U_C() //aqui acessando a variável Static Interna
Alert( Str( nRetC ) )//nVar em U_C será incrementada a cada
//iteração do Loop
Next nLoop

Return( NIL )

segunda-feira, 19 de janeiro de 2009

Protheus :: Programação Básica em Advpl “Combo XXVIII”

LOCAL, PRIVATE:

Essas duas instruções declaram variáveis para uso subseqüente. Entretanto, existem diferenças importantes entre os tipos de variáveis que elas criam. PRIVATE declara variáveis visíveis na rotina na qual você as declara e também em qualquer rotina que você chame a partir do ponto da declaração.

User Function Test()

Private nI

U_Test1()

Return( NIL )

User Function Test1()
//aqui poderemos enxergar e fazer uso da variável nI criada em //U_Test()
Return( NIL )

A instrução LOCAL declara variáveis somente visíveis na rotina na qual foram declaradas; dessa forma:

User Function Test()

Local nI

U_Test1()

Return( NIL )

User Function Test1()
//aqui não poderemos enxergar e nem fazer uso da variável nI criada //em U_Test()
Return( NIL )

O problema com as Private:

As variáveis Private representam um dos maiores obstáculos para a criação de rotinas modulares e confiáveis. O principal problema é que elas não são privadas de modo algum e isto ocasiona sérios problemas. Por exemplo, examine o seguinte:

User Function Test()

Private nI := 0
Private aTest[10]

While ( ++nI <= 10 )
Test1()
//nI será sempre 4 aqui
End While

Return( NIL )


Static Function Test1()

nI := 0
While ( ++nI <= 3 )
aTest[ nI ] := nI
End While

Return( NIL )

No fragmento de código acima, a função Test1() usa uma variável nI para o controle do Loop, mas como a função Test(), sua chamadora, já possui uma variável nI, Test1() usa esta última. O resultado é que, no retorno a Test(), nI contém o valor 4, fazendo com que o programa fique em loop infinito. Uma solução para o problema seria declarar a variável nI como private em Test1(). Desta forma a variável nI criada em Test() será preservada no retorno de Test1(). A melhor solução, seria criar nI como Local, tanto em Test() como em Test1().

User Function Test()

Local nI := 0
Local aTest[10]

While ( ++nI <= 10 )
Test1( @aTest )
End While

Return( NIL )


Static Function Test1( aTest )

Local nI := 0

While ( ++nI <= 3 )
aTest[ nI ] := nI
End While

Return( NIL )

Obs.: Muito cuidado ao usar variáveis Private no Programa. Isso pode causar probemas muito difíceis de depurar. Use Variáveis Locais e abra mão da referência para obter retorno. Garanta que todas as Variáveis utilizadas no Programa tenham sido previamente declaradas.

Protheus :: Programação Básica em Advpl “Combo XXVII”

Instruções de Declarações:

Uma instrução de declaração é uma instrução para um compilador. Neste seção estamos interessados nas seguintes declarações:

LOCAL
PRIVATE
PUBLIC
STATIC

LOCAL, PRIVATE, PUBLIC e STATIC declaram variáveis.

Obs.: Para evitar ambigüidade entre campos e variáveis Private com o mesmo nome é recomendado a prefixação da variável Private com M->[Nome da Variável ] para identificar que é uma variável de memória e/ou prefixar o campo com o respectivo Alias usando o operador -> como em: [Alias]->[ Campo da Tabela ]. Obs.: Melhor do que declarar uma variável com o Mesmo nome do campo é declarar a variável utilizando a notação Húngara Modificada onde as variáveis são prefixadas com a letra minúscula correspondente ao seu tipo ( Em Inglês ).

Tipo: Exemplo:

"N"umeric nNumber "C"haracter cChar
"B"lock bBlock
"D"ate dDate
"L"ogic lOk
"O"ject oObj
"U"ndefined uVar

Lembrando que em Advpl as Variáveis não são prototipadas o tipo da variável só será conhecido apos a atribuição de algum valor a ela e que para testar o tipo podemos utilizar as funções ValType( ) ou Type( ). Onde: ValType avalia o conteúdo da variável passada como parâmetro para verificar o seu tipo, e Type(), primeiro, "macro-executa" a variável para obter seu conteúdo e de pois o avalia. Sendo assim, para usar a função Type() é necessário que a variável seja do Tipo Private ou Public

Protheus :: Programação Básica em Advpl “Combo XXVI”

Recursividade:

As funções e rotinas podem ser recursivas, isto é, podem chamar a si mesmas. Cada chamada recebe uma nova cópia das variáveis Local e Private e de quaisquer parâmetros. Quando uma função retorna, os valores anteriores são restabelecidos. Obs.: Não são alocadas novas cópias de variáveis STATIC.

Exemplo:

User Function SaveArray( uArray , cFileName , nErr )

Local cValTypeuArray := ValType( uArray )
Local lSaveArray := .F.

Local aArray
Local nfHandle

Begin Sequence

IF !( cValTypeuArray $ "A/O" )
Break
EndIF

IF ( cValTypeuArray == "O" )
aArray := ClassDataArr( uArray )
Else
aArray := uArray
EndIF

lSaveArray := FileCreate( cFileName , @nfHandle , @nErr )
IF !( lSaveArray )
Break
EndIF

SaveArr( nfHandle , aArray ) //Aqui chamamos a função para salvar o Array
fClose( nfHandle )

End Sequence

Return( lSaveArray )


Static Function SaveArr( nfHandle , aArray )

Local cElemType

Local uCntSave

Local nLoop
Local nLoops

nLoops := Len( aArray )
uCntSave := ( "A" + StrZero( nLoops , 10 ) )
fWrite( nfHandle , uCntSave )
For nLoop := 1 To nLoops
cElemType := ValType( aArray[ nLoop ] )
IF ( cElemType $ "A/O" ) //Aqui efetuamos a chamada recursiva
IF ( cElemType == "A" )
SaveArr( nfHandle , aArray[ nLoop ] )
Else
SaveArr( nfHandle , ClassDataArr( aArray[ nLoop ] ) )
EndIF
Else
IF ( cElemType == "B" )
uCntSave := GetCBSource( aArray[ nLoop ] )
ElseIF ( cElemType == "C" )
uCntSave := aArray[ nLoop ]
ElseIF ( cElemType == "D" )
uCntSave := Dtos( aArray[ nLoop ] )
ElseIF ( cElemType == "L" )
uCntSave := IF( aArray[ nLoop ] , ".T." , ".F." )
ElseIF ( cElemType == "N" )
uCntSave := Transform( aArray[ nLoop ] , RetPictVal( aArray[ nLoop ] ) )
EndIF
uCntSave := ( cElemType + StrZero( Len( uCntSave ) , 5 ) + uCntSave )
fWrite( nfHandle , uCntSave )
EndIF
Next nLoop

Return( NIL )

Protheus :: Programação Básica em Advpl “Combo XXV”

Chamando Funções Indiretamente:

Em Advpl é possível efetuar a chamada de funções indiretamente usando o operador de macros. Este procedimento lhe permitirá usar aplicativos controlados por dados.

Pode-se usar um bando de dados ( à exemplo do SX3 ) para armazenar uma seqüência de funções a serem chamadas, e depois usá-lo para gerar telas, efetuar validação de campos, iniciar o conteúdo de campos, etc.

Para passar uma função como parâmetro, passe o nome da função como uma string de caracteres ou como uma chamada de função dentro de um bloco de codificação. Para chamá-la, use o operador de macros & ou a função __ExecMacro() quando estiver passando uma string de caracteres ou Eval() para avaliar o bloco de código.

Exemplo:

cExecTeste := “U_TESTE()”
&( cExecTeste )

ou

cExecTeste := “U_TESTE()” //Executa a função U_TESTE()
__ExecMacro ( cExecTeste ) //Executa a função U_TESTE()

ou ainda

bExecTeste := { || U_TESTE() )
Eval( bExecTeste ) //Executa a função U_TESTE()

Protheus :: Programação Básica em Advpl “Combo XXIV”

Envio Por Referência ou por Valor:

Um aspecto importante do envio de parâmetros para as funções é se eles são passados por referência ou por valor. O método de envio de um argumento dependerá do método de chamada; ele não pode ser determinado no ponto em que a própria função é definida.

Quando os parâmetros são passados por referência, a função recebe ponteiros para os verdadeiros parâmetros, e quaisquer mudanças que ela introduzir neles serão imediatamente refletidas em seus valores:

User Function Test1()

Local nVar

nVar := 3

U_Test2( @nVar )
//Apos o retorno de U_Test2() nVar terá o conteúdo igual 1

Return( NIL )

User Function Test2( nVar1 )

nVar1 := 1

Return( NIL )

Para efetuarmos a passagem por referência devemos prefixar a variável com o operador de referência @.

Se passarmos um parâmetro por valor, o valor inicializará a variável local ou privada associada com o parâmetro formal, mais quaisquer mudanças introduzidas neste valor pela função desaparecerão. O verdadeiro parâmetro não é alterado pela operação da função.

User Function Test1()

Local nVar

nVar := 3

U_Test2( nVar )
//Apos o retorno de U_Test2() nVar terá o conteúdo igual 3

Return( NIL )

User Function Test2( nVar1 )

nVar1 := 1

Return( NIL )

A chamada a U_Test2( nVar ) determina o envio de parâmetros por valor, pois o operador de referência @ não foi utilizado.

Os campos de banco de dados são exceções. Eles sempre são passados por valor, independentemente do estilo de chamada; caso contrário, toda vez que o parâmetro formal fosse mudado, seria necessário atualizar o banco de dados. Mas isso só é valido se o campo do banco de dados estiver prefixado pelo alias. Exemplo:

dbSelectArea( “SRA” ) //Seleciona a área de trabalho SRA
U_Teste1( @SRA->RA_MAT ) //irá gerar erro durante a compilação
U_Teste1( @RA_MAT ) //irá gerar erro na execução
U_teste1( @_Field->RA_MAT ) //irá gerar erro na execução
SRA->( U_teste1( @RA_MAT ) ) //irá gerar erro na execução

Recomendação: Para que não ocorra erro de execução, sempre prefixe o campo com o alias correspondente para que seja possível identificar se um campo de tabela está sendo passado por referência.

Protheus :: Programação Básica em Advpl “Combo XXIII”

Funções/Parâmetros

Ao fazer uma chamada a uma função valores são fornecidos para os parâmetros. Esses valores podem assumir a forma de constantes, variáveis, campos, elementos de arrays ou expressões, e eles são citados como parâmetros reais ou argumentos de chamada.

Uma chamada de função pode, também, omitir argumentos para qualquer número de parâmetros, deixando um vazio onde o argumento deveria estar. Será necessário incluir as vírgulas delimitadoras na lista de argumentos, exceto para quaisquer argumentos omitidos no final da lista. Isto assegura não existir ambigüidade sobre quais parâmetros foram deixados em branco. Quaisquer argumentos omitidos de uma chamada de função serão tratados exatamente como se o valor NIL tivesse sido realmente passado. Não há como distinguir, dentro da definição de função, entre o argumento omitido e um valor NIL passado explicitamente. A chamada:

User Function Test( nS , nE , nI , nD )
Return

U_Test( nRow , , 2 )

À função U_Test(), conforme definida acima, resultará no corpo da função sendo executada como se os parâmetros formais tivessem sido inicializados assim:

nS := nRow
nE := NIL
nI := 2
nD := NIL

sexta-feira, 16 de janeiro de 2009

Protheus :: "Windows® PowerShell® Script Stop and Start Services"

Como é do conhecimento de todos, os serviços do Protheus tem um "pequeno" problema no que diz respeito ao gerenciamento de memória (quando executado sobre a plataforma Windows Server 32). Sendo assim, de vez em quando faz-se necessário "reiniciar" esses serviços de forma a liberar memória e evitar efeitos colaterais. Em função desse "pequeno problema", disponibilizo um "script" em Windows® PowerShell® para "reiniciar" os serviços do Protheus. Para adaptá-lo às suas necessidades altere: $IniPort; $EndPort; o nome do serviço a ser "parado/reiniciado" e o intervalo de definição das "portas" (presumindo que existam vários serviços do Protheus "escutando" em um mesmo "server").

#Script Name P10Restart.ps1
$IniPort = 5110
$EndPort = 5911
for ( $Port = $IniPort ; $Port -le $EndPort ; $Port+=100 )
{
if ( get-service | where { $_.status -eq "running" -and $_.name -eq "P10Server"+$Port} )
{
Stop-Service -force P10Server$Port
Start-Service P10Server$Port
}
elseif ( get-service | where { $_.name -eq "P10server"+$Port} )
{
Start-Service P10Server$Port
}
}

Crie um agendamento (no meu caso, aos domingos) para que esse "script" seja executado.

Obs.:

  • Para que o "script" possa ser executado no "Task Manager" faz-se necessário informar o caminho completo do programa que irá executá-lo e o "script" a ser executado. Ex.: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe c:\totvs\p10\scripts\P10Restart.ps1; e
  • Para que o "script" possa ser executado faz-se necessário autoriza-lo através do Windows® PowerShell® , sendo assim, execute o Windows® PowerShell® e digite o seguinte comando: get-help about_signing. Esse comando irá listar as opções de "Diretivas de Assinatura e de Execução" de "Script", leia-as atentamente. Mas se quiser pular esse passo (eu não recomendo)digite o seguinte comando no Windows® PowerShell® : Set-ExecutionPolicy Unrestricted. Esse comando fará que qualquer "Script" seja executado em sua máquina (mas atente para os ítens de segurança listados à partir do comando get-help about_signing).

quinta-feira, 15 de janeiro de 2009

Protheus :: Programação Básica em Advpl “Combo XXII”

Funções:

As funções, em Advpl, podem ser entendidas como um mecanismo de sub-rotinas. Elas sempre retornam um valor. Uma função pode retornar qualquer tipo de dados ( array, numérico, string, objeto, lógico... ) e, se nenhum tipo de retorno for definido a função retornará NIL. Elas retornam seu valor como uma expressão após a instrução RETURN ( Obs.: a rotina chamadora da função não precisa, necessariamente, usar o valor de retorno ).

O uso adequado das funções possibilita a grande reutilização da codificação.

As funções são definidas uma só vez dentro de um programa, mas podem ser chamadas várias vezes. Elas podem ser definidas em qualquer ordem e você pode chamar uma antes de defini-la. Todas as funções são armazendas no Repositório de Objetos. E Podem ser declaradas como:

Públicas: Podem ser chamadas em qualquer programa:

Funções Reservadas ( Microsiga )

Function -> Reservado para desenvolvimento no ERP na Microsiga
Template Function -> Para a criação de Templates
Project Function -> Para a criação de Projetos

Funções Comuns ( Microsiga / Clientes usuários )

User Function -> Para criação de funções de usuário
Web Function -> Para criação de funções para uso na Web
Html Function -> Para criação de funções para uso na Web

Estáticas: Podem ser chamadas apenas no programa onde foram declaradas ( Static )

Static Function

Obs.: Consulte o Header ( arquivo de cabeçalho ) Protheus.ch ( \mp8\include\ para verificar os detalhes sobre o padrão de conversão das delcarações acima nas chamadas de função.

Parâmetros:

No Advpl, a forma de passagem de parâmetros para as funções é colocando os nomes dos parâmetros entre parênteses e delimitando-os por vírgula. Este método é conhecido como lista de parâmetros formais. Como em:

Function Teste( a , b , c )
User Function Teste( a , b , c )

Onde a, b, c são os parâmetros formais da função. O escopo destes parâmetros é Local ( tem visibilidade e duração enquanto durar a execução da função ).

Protheus :: Programação Básica em Advpl “Combo XXI”

Compilação Condicional:

A compilação condicional nos permite incluir ou excluir codificação baseada numa condição. O propósito usual é manter diferentes versões de um programa, sem precisar separar cópias da codificação de origem. Por exemplo, poderíamos ter uma verão utilizando o TopConNect e outra para CodBase,

As diretivas do pré-processador para a compilação condicional se parecem com as instruções IF/ELSE/ENDIF. O pré-processador inclui ou exclui codificação do arquivo de saída, baseado no resultado do teste. O teste, conseqüentemente, atua como um filtro. O aspecto importante a observar é que o pré-processador faz o teste, não o programa. Dessa forma, a codificação não-selecionada nem sequer existe no arquivo de saída.

As diretivas verificam se um símbolo está definido ou se ele tem um valor específico ( podemos definir um símbolo com a diretiva #DEFINE ). As diretivas do pré-processador são:

#IFDEF - Verifica se um símbolo está definido
#IFNDEF - Verifica se um símbolo não está definido
#ELSE - Inclui algo se #IFDEF ou uma #IFNDEF falhar
#ENDIF - Indica o final da codificação controlada por um #IFDEF, #IFNDEF ou #ELSE


Podemos definir um símbolo sem atribuir um valor a ele. Por Exemplo, podemos escrever:

#DEFINE TOP

E testá-la, em seguida, com:

#IFDEF TOP

Neste caso, estamos apenas verificando se ela está definida, não checamos o seu valor;

Observemos agora, como o pré-processador manipula um exemplo simples:

#DEFINE TOP

#IFDEF TOP
Local cquery := “SELECT * FROM...”
#ELSE
( cAlias )->( dbSeek( ... ) )
#ENDIF

Protheus :: Programação Básica em Advpl “Combo XX”

Macros do compilador:

Podemos visualizar uma macro do compilador como uma função diretamente na codificação de origem do programa onde poderíamos parametrizar uma seqüência de comandos, exatamente como uma função. A diferença é que as macros do pré-processador são substituídas diretamente no arquivo de origem ( elas não são chamadas do mesmo modo que uma função ) . Por exemplo, poderíamos escrever uma macro de pesquisa de mínimo como:

#DEFINE MIN( a , b ) IIF( a < b , a , b )

Quando, subseqüentemente, chamar-mos a MIN com:

nMin := MIN( nY , nZ )

O pré-processador a substituirá por sua definição como em:

nMin := IIF( nY < nZ , nY , nZ )

Defina a macro usando parâmetros formais, exatamente como uma função. Quando o pré-processador a expandir, ele os substituirá pelos verdadeiros parâmetros (argumentos). Isto é basicamente o que acontece quando você chama uma rotina ou função. A diferença é que a substituição dos argumentos ocorre no tempo do pré-processador com as macros do compilador, não durante a execução.

Protheus :: Programação Básica em Advpl “Combo XIX”

Arqivos Include:

As constantes simbólicas só são conhecidas no arquivo de origem que as define. O pré-processador só conhece seus valores depois da tradução. Portanto se definirmos uma constante um programa ela só valerá durante a compilação deste programa. Para que uma constante possa valer para mais de um programa, devemos usar um recurso de inclusão de arquivos através da diretiva #include. Ou seja, as constantes seriam incluídas em um arquivo à parte e todo programa que fosse utilizar essas constantes teria a seguinte linha #include “”.

Protheus :: Programação Básica em Advpl “Combo XVIII”

Pré-Processador:

O pré-processador é um importante recurso no Advpl. Imagine-o como um programa que executa antes do compilador. Ele lê o programa como entrada e gera um arquivo pré-processado como saída. O arquivo de saída, então, serve como a entrada do compilador.

Podemos incluir comandos do pré-processador, chamadas de diretivas, no programa de origem. O pré-processador as manipulará; o compilador nunca as percebe, uma vez que ele só manipula o arquivo de saída.

O pré-processador do Advpl tem os seguintes recursos:

- constantes simbólicas ou manifestas
- arquivos include
- macros do compilador
- compilação condicional
- comandos definidos pelo usuário

Constantes Simbólicas:

No Advpl é possível a definição de constantes simbólicas para uso no processo de pré-compilação. Devemos abrir mão desse recurso para tornar os programas mas legíveis e fácil de entender. Por Exemplo, no fragmento de codificação:

IF ( nOpc == 1 )
Inclui()
ElseIF ( nOpc == 2 )
Altera()
ElseIF ( nOpc == 3 )
Exclui()
EndIF

Só conseguiremos identificar o que nOpc representa se acompanharmos todo o processo ou se o programa estiver documentado. Mas, se utilizássemos constantes, teríamos:

Obs.: Podemos definir constates com a diretiva do pré-processador #DEFINE.

#DEFINE INCLUI 1
#DEFINE ALTERA 2
#DEFINE EXCLUI 3

IF ( nOpc == INCLUI )
Inclui()
ElseIF ( nOpc == ALTERA )
Altera()
ElseIF ( nOpc == EXCLUI )
Exclui()
EndIF

Não precisaríamos avaliar todo o programa para saber a que se refere o valor de nOpc. E o código fica muito mais fácil de interpretar. Na realidade INCLUI, ALTERA e EXCLUI, serão substituídos no processo de pré-compilação por 1 , 2 e 3 ( respectivamente ).

Regra: A tradução de #DEFINE feita pelo pré-processador é sensível a letras maiúsculas e minúsculas.

Dica: Defina todas as constantes em letras maiúsculas com a diretiva #DEFINE

Protheus :: Programação Básica em Advpl “Combo XVII”

Loops:

O Advpl oferece os loops FOR e WHILE. A instrução FOR executa instruções até o NEXT correspondente durante um determinado número de vezes. Devemos informar a quantidade de vezes a iterar, como em

Local nLoop
Local nLoops := 10

For nLoop := 1 To nLoops
...
...
Next nLoop

A inicialização da variável para controle do Loop deverá é feita na declaração For nLoop :=, o NEXT incrementa-a automaticamente e, se ele não tiver ultrapassado seu limite, o controle retornará para a primeira instrução dentro do loop. O valor do incremente é igual a 1, a menos que especificado de outra maneira com a opção STEP.

Local nLoop
Local nLoops := 100

For nLoop := 1 To nLoops Step 10

Next nLoop

ou

Local nLoop
Local nLoops := 0

For nLoop := 100 To nLoops Step –10

Next nLoop


Podemos usar os comandos EXIT e LOOP dentro do corpo do loop FOR. Exit encerra o Loop, transferindo o controle para a instrução depois do NEXT; o LOOP dá um salto para o início, reavaliando a condição.

Obs.: Use os dois comandos com cuidado; eles podem dificultar o entendimento e a manutenção dos loops.

O Loop for sempre reavalia a expressão limite a cada passagem. Por exemplo, em:

Local nLoop

For nLoop := 1 To RecCount()
...
Next nLoop

RecCount() será sempre reavaliada a cada passagem do Loop, o que é um desperdício. Atribua o valor retornado per RecCount() à uma variável para melhorar a performance, como em:

Local nLoop
Local nLoops := RecCount()

For nLoop := 1 To nLoops

Next nLoop

Já um Loop WHILE continua até que sua condição venha a ser falsa. A condição é avaliada primeiro, para que o loop não possa ser inserido de modo algum. A avaliação da condição será interrompida assim for conhecido o resultado.

Obs.: Apenas o necessário de uma condição é avaliado para se determinar o resultado.

Para executar o loop pelo menos uma vez, simulando as cláusulas REPEAT... UNTIL ou DO ... UNTIL de outras linguagens, poderemos escrever:

Local lContinue := .T.

While ( lContinue )
...

IF
lContinue := .F.
EndIF

End While

Estaremos, essencialmente, testando a condição ao final do loop.

Obs.: Não uso While .T. como uma condição de loop. É melhor colocar a condição de saída na instrução. Desta maneira, poderemos ver claramente o que o loop está tentando alcançar.

Protheus :: Programação Básica em Advpl “Combo XVI”

Estruturas de controle:

Como acontece com a maioria das linguagens, o Advpl fornece instruções para suportar desvios e loops. As instruções de desvio permitem que seus programas selecionem entre alternativas; as instruções de loops permitem executar um grupo de instruções repetidas vezes.

Desvio:

No Advpl existem dois métodos de desvio: o bloco de instruções IF/ELSE/ELSEIF/ENDIF ou o bloco DO CASE/ENDCASE. ELSEIF é apenas uma abreviação que evita a necessidade de se escrever outro IF/ENDIF. Os dois exemplos que se seguem são equivalentes:

IF ( nX < 10 )
...
Else
IF ( nX > 20 )
...
EndIF
EndIF

ou

IF( nX < 10 )
...
ElseIF ( nX > 20 )
...
EndIF

Observe que, como o Advpl sempre insiste num ENDIF encerrando todo o bloco de IF, sempre saberemos a quem pertence um Else.

Obs.: Ao testar várias condições, é preferível uma instrução CASE a uma longa seqüência de IF/ELSEIFs. Na implementação das instruções CASE, o Advpl avalia uma condição de cada vez, até encontrar uma verdadeira. Em seguida ele avalia todas as instruções até o próximo CASE, ENDCASE ou OTHERWISE e, finalmente, salta para a instrução seguinte ao ENDCASE.

A cláusula OTHERWISE serve como última condição; ela localiza toda situação não correspondida por um CASE anterior.


BEGIN SEQUENCE/END

O par BEGIN SEQUENCE/END oferece outro método de desvio. Use-o para delimitar um bloco de codificação; em seguida, a partir de qualquer local dentro dele, você poderá emitir uma instrução BREAK. Ele passará o controle para a instrução seguinte ao END.

Exemplo:

BEGIN SEQUENCE

IF ...

IF ...

IF ...
Break
EndIF
EndIF
EndIF

...

END SEQUENCE

BEGIN SEQUENCE pode ser aninhado; um BREAK passará o controle para o END mais próximo


BEGIN SEQUENCE

IF !(... )
BREAK
ENDIF

BEGIN SEQUENCE

IF ...
BREAK
ELSEIF ...
BREAK
ENDIF

IF ...
BREAK
ELSEIF ...
BREAK
ENDIF

END SEQUENCE
...
END SEQUENCE

...

O uso básico de BEGIN SEQUENCE é manipular exceções. Ele fornece um local adequado para saltar quando ocorre um erro. Podemos usá-lo como um ponto de interrupção para a lógica profundamente aninhada. Por exemplo, vamos supor que precisemos testar três condições, mas só possa testar uma após o término com êxito da anterior ( como uma seqüência de bloqueios de arquivos ). Usando o aninhamento tradicional escreveríamos:

IF
...
...
IF
...
...
IF
...
...
ENDIF
ENDIF
ENDIF

Usando BEGIN SEQUENCE, poderíamos escrever:

BEGIN SEQUENCE

IF !
BREAK
ENDIF

...
...

IF !
BREAK
ENDIF




IF !
BREAK
ENDIF

END SEQUENCE

Pode-se, também, colocar uma instrução RECOVER dentro de um par BEGIN SEQUENCE/END. Quando seu programa executar, subseqüentemente, uma instrução de interrupção, o controle será transferido para a instrução RECOVER em vez de para o END.

Obs.: Tenha cuidado usar BEGIN SEQUENCE e BREAK; poderemos criar uma codificação ilegível
com o uso excessivo desse par.

Protheus :: Programação Básica em Advpl “Combo XV”

O último operador que examinaremos é o $ ( contido em ). Use-o para verificar se uma string está contida em outra. Por exemplo, para constatar se a variável de memória de caractere isolado cTipoPeca é R, D, ou b, podemos escrever:

IF cTipoPeca $ “RDB”
...

Para verificar se uma variável de memória de dois caracteres é “PR”, “SP”, ou “AM”, escreva:

IF cEstado $ “PR_SP_AM”
...

Obs.: Os separadores “_” são necessários para invalidar combinações, como “RS” e “PA” que são estados válidos.

Dica: Use o operador $ ao invés de várias condições separadas. Ex.:

Do Case
Case cEstado == “PR” ; Execute(“PR”)
Case cEstado == “SP” ; Execute(“SP”)
Case cEstado == “AM” ; Execute(“AM”)
EndCase

Poderia ser escrito como

IF ( cEstado $ “PR_SP_AM” )
Execute(“PR_SP_AM”)
EndIF

Expressões condicionais:

IIF() ou IF() é uma expressão condicional. São abreviações de uma simples seqüência de IF/ELSE/ENDIF. Por exemplo, para encontrar o mínimo de a e b, poderíamos escrever:

nMinimo := IIF( a < b , a , b )
ou
nMinimo := IF( a < b , a , b )

que seria equivalente a escrever:

IF ( a < b )
nMinimo := a
Else
nMinimo := b
EndIF

As vantagens de IIF() e/ou IF() é que se acomodam em qualquer lugar em que uma expressão possa estar ( incluindo blocos de codificação ) e gera menos codificação.

Protheus :: Programação Básica em Advpl “Combo XIV”

Conectivos Lógicos:



É possível negar uma expressão lógica com .NOT. ou com o ponto de exclamação, que é equivalente. No Advpl as expressões lógicas são avaliadas da esquerda para a direita ( parando assim que o resultado é conhecido ). Esse processo é chamado “avaliação de curto-cirquito”. Por exemplo:

#DEFINE NLIMITE 10

Local aArr[NLIMITE ]
Local nIndice := 1
Local nMax := 50

...
...

While ( ( nIndice <= NLIMITE ) .and. ( aArr[ nIndice ] < nMax ) )
Process()
nIndice := nIndice + 1 //ou ++nIndice
End While

Quando o valor de nIndice for 11, a primeira expressão provocará a interrupção da avaliação. ( no exemplo, a segunda expressão não será avaliada, caso contrário ocorreria o erro conhecido como “erro de subscrito” – O erro ocorreria porque “aArr” for definido com 10 elementos ( NLIMITE 10 ); se a segunda comparação fosse efetuada, teríamos aArr[11 ] para um array de 10 elementos apenas.

O Advpl avalia os operandos de operadores binários, tanto aritméticos como relacionais, da esquerda para a direita. Na maioria dos casos a ordem não é importante, mas:

IF A() < B()

Onde A() e B() são funções? O problema é que A() e B() poderiam provocar efeitos colateriais. Se elas dependerem de uma variável comum, a qual pode mudar ambas, a ordem de avaliação terá importância.

IF aArr1[ ++nI ] < aArr2[ nI++ ]



Se ambos os lados tiverem efeitos colaterais, seria necessário considerar a ordem de avaliação.

Os operadores, em Advpl, são sobrecarregados. Isso significa que eles se aplicam a operandos de diversos tipos. Por exemplo, pode-se utilizar o operador + para somar números, concatenar string e efetuar a aritmética de datas. De modo semelhante, podemos usar o operador <= para comparar datas, números e strings.

O Advpl suporta dois operadores de atribuições, = e := ( “torna-se igual a”). A diferença entre os dois é que := retorna um valor, enquanto = é sobrecarregado. Quando usado dentro de uma expressão, = efetua uma comparação; caso contrário, ele é uma atribuição. O operador := sempre efetua uma atribuição e ele retorna um valor no lado direito para que possamos usá-lo dentro de uma expressão, como em:

IF( nHandle := fOpen( “ERROS.TXT” ) ) != 0


A variável nHandle obtém o valor retornado pela abertura do arquivo ERROS.TXT. Este valor é, então, comparado com 0. Poderíamos obter o mesmo resultado com:

nHandle := fOpen( “ERROS.TXT )
IF ( nHandle != 0 )
...

Mas no modelo anterior uma instrução foi economizada. Na versão em linha inserimos a atribuição entre parênteses. Isso ocorreu porque o operador := tem menos precedência que o operador !=, portanto nHandle receberia, de outra maneira, um valor lógico, resultante da comparação do valor retornado por fOpen() com 0. Uma boa prática, então, é colocar entre parênteses as instruções de atribuição em linha, para evitar problemas de precedência.


O operador := associa da direita para a esquerda, portanto podemos escrecer:

cPNome := cSnome := cCep := cTel := Space( 10 )

que o efeito sera identico a escrever:

cPNome := ( cSnome := ( cCep := ( cTel := Space( 10 ) ) ) )

A vantagem do operador := é que ele pode aparecer dentro de expressões. Um bom uso deste operador é na determinação de causa de falha de uma complexa estrutura While. Por exemplo.

While TeclaValid() .and. NaFaixa()
...
End While

IF TeclaValid()
..
EndIF

Quando o loop terminar, precisamos verificar que condição provocou a saída dele; para tanto, precisamos chamar novamente uma função demorada. Podemos reescrever usando atribuições em-linha, assim:

While ( ( lTv := TeclaValid() ) .and. ( lNf := NaFixa() ) )

End While

IF ( lTv )
..
EndIF

Quando sairmos do Loop, não precisaremos chamar nenhuma das funções demoradas para determinar por que o loop terminou.

Protheus :: Programação Básica em Advpl “Combo XIII”

Operadores Relacionais:

No Advpl são permitidos os seguintes operadores relacionais:



Os operadores relacionais têm menos prioridade do que os aritméticos. Por exemplo:

nI < nLimite – 1

é avalidado como:

nI < ( nLimite – 1 )

e não como:

( nI < nLimite ) – 1

Protheus :: Programação Básica em Advpl “Combo XII”

Operadores Unários:

O Advpl suporta um sinal negativo ( o chamado operador unário). Ele tem a maior prioridade entre os operadores aritméticos. Por Exemplo:
  • - 1 + 2
  • resulta 1 e não -3.
O Advpl suporta os operadores de incrementos e decrementos unários ++ e --. O Operador ++ acrescenta 1 a seu operando, -- subtrai 1. Ambos têm parceiros nas instruções regulares de atribuições. Exemplo:

nContador++ é idêntico a nContador := nContador + 1

Entretanto, a vantagem desses operadores unários é o fato de serem concisos e de poderem ser utilizados para deferir a operação até depois de processado o valor. Por Exemplo, para carregar um array de campo CPnome de cada registro de um banco de dados chamado Alunos, poderíamos escrever:

Local acPNomes
Local nContador := 1

Use Alunos

acPNomes := Array( RecCount() )
While ( Alunos )->( !Eof() )
acPNomes[ nContador++ ] := ( Alunos )->CPNOME
( Alunos )->( dbSkip() )
End While

O Subscrito do array usa o valor de nContador, depois acrescenta 1. Dizemos que o ++, neste caso, é um operador pós-determinado. Você pode usá-lo como um operador de prefixos, em cujo caso o valor será incrementado ANTES de ser usado. Por exemplo, a codificação anterior deixa o nContador com um valor superior em 1 unidade ao número de registros processados. Para deixá-lo com o valor real, poderíamos escrever:

Local acPNomes
Local nContador := 0

Use Alunos

acPNomes := Array( RecCount() )
While ( Alunos )->( !Eof() )
acPNomes[ ++nContador] := ( Alunos )->CPNOME
( Alunos )->( dbSkip() )
End While

No modo de prefixo, o computador aplica o incremento antes de usar o valor.

Protheus :: Programação Básica em Advpl “Combo XI”

Operadores e Expressões:

O Advpl suporta os seguintes operadores binários:




Que agrupados em ordem de precedência ( prioridade, a mais alta primeiro ). Os operadores ** e ^ têm ambos a prioridade mais alta. Os operadores *, / e % tem níveis de prioridades iguais, assim como os operadores + e -. Os operadores *, / e % tem uma precedência maior do que + e -. O Advpl avalia os operadores, de igual precedência, da esquerda para a direita.


Protheus :: Programação Básica em Advpl “Combo X”

Variáveis Numéricas:

O Advpl, na leitura de uma variável numérica, atribui, automaticamente, dez posições antes da casa decimal, a despeito do seu tamanho atual. Portanto, esse tamanho é passível de ser aumentado. Já em relação ao número de posições depois do ponto decimal é determinado quando você o atribui a ela. Assim, se para montar um Get, tivermos o seguinte bloco de programa:

Local nValor := 15.1270

@ 10 , 10 Say “Digite um novo valor:” Get nValor

nValor terá 4 posições após o ponto decimal e duas posições antes do ponto decimal ( conforme valor atribuído à variável ); portanto o Get usa 7 colunas da tela: 2 para antes do ponto decimal, uma para o próprio ponto decimal e quatro depois dele. Ao inicializar variáveis numéricas para uso em um Get, certifique-se de atribuir a elas o número correto de casas decimais ou utilize picture como em:

Local nValor := 15.12

@ 10 , 10 Say “Digite um novo valor:” Get nValor Picture @R 999.99999

Apesar de nValor ter sido declarado como tendo apenas 2 posições após o ponto decimal e ele ainda possuir dez posições antes do ponto, ao delimitarmos a digitação da informação definindo a Picture teremos ( para digitação ): 3 posições antes do ponto decimal, 5 posições após o ponto decimal ( além do próprio ponto ) totalizando 9 o número de colunas no Get para a visualização e manutenção do valor.

Obs.: Ao contrário das variáveis do Tipo Array e Caractere, que utilizam a função Len() para retornar o seus tamanhos, em AdvPl não existe função ou operador que retorne o tamanho de uma variável do tipo numérica. Mas nada impede de que ela seja desenvolvida pelo próprio usuário do sistema.

Protheus :: Programação Básica em Advpl “Combo IX”

Protheus :: Programação Básica em Advpl “Combo IX”

Tamanho:

Faz-se necessário observar o tamanho de variáveis de caracteres e numéricas uma vez que um problema difícil de analisar é a comparação de variáveis de tamanhos diferentes; os espaços de preenchimento podem ser significativos.:

Variáveis Caractere

Ex.:

Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual

lEqual := ( cUsuario == cUser )

No exemplo acima lEqual irá retornar .F. uma vez que o conteúdo armazenado na variável cUsuario ( apesar de parecer igual ) é diferente do conteúdo da variável cUser. O que diferencia o conteúdo entre uma variável e outra são os espaços à direita. Para resolver o problema na comparação teremos que abir mão de uma função que remova os espaços à direita ( RTrim( ) ).

Ex.:

Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual

lEqual := ( cUsuario == RTrim( cUser ) ) //Removendo espaços a //direita
ou

lEqual := ( cUsuario == AllTrim( cUser ) ) //Removendo espaços a //direita e à esquecerda

Nos dois exemplos acima lEqual irá ser igual à .T.

Se ao invés de usarmos o operador de comparação de igualdade == (exatamente igual ) utilizássemos o operador = ( que pode ser utilizado tanto para comparação de igualdade como para atribuição – neste caso depende do contexto ) – teríamos:

Ex.:

Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual

lEqual := ( cUsuario = cUser )

No Exemplo acima, lEqual será .T. pois não fizemos uma comparação exata.

Em AdvPl é possível obter o tamanho de uma variável do tipo caractere ou array utilizando a função Len( ).

Ex.:

Local aArray1 := { “1” , “2” , “3” }
Local aArray2 := Array( 10 )

Local cUsuario := “Administrador”
Local cUser := “Administrador “

Local nArray1 := Len( aArray1 ) //3
Local nArray2 := Len( aArray2 ) //10

Local nUsuario := Len( cUsuario ) //13
Local nUser := Len( cUser ) //16

quarta-feira, 14 de janeiro de 2009

Protheus :: Personalizando o RHOnLine

Neste artigo vou demonstrar como personalizar o seu RHOnLine, deixando-o com a cara de sua empresa e no padrão W3C de forma a funcionar nos principais "browsers" (IE, Firefox, Google Chrome, etc. O RHOnLine do padrão só funciona com o IE).

Neste exemplo não teremos todas as funcionalidades do RHOnLine padrão, apenas uma tela de abertura, o Demonsrtrativo de Pagamento e a opção para alteração de senha. Mas isso não impede que as demais funcionalidades sejam implementadas, bastando seguir o modelo aqui fornecido. Nesse modelo utilizamos uma ferramenta do site BarcodesInc® para gerar o código de barras para o Demonstrativo de Pagamento de forma a dar-lhe mais "credibilidade".

A personalização do RHOnLine terá as seguintes características:
Tela de Abertura



Tela de Apresentação



Tela para escolha do período para impressão do Demonstrativo



Tela de mensagem caso não existam informações de Demonstrativo



Tela do Demonstrativo



Tela de rodapé do Demonstrativo (com código de barra)



Tela para a Alteração de Senha

Para a implementação:

  1. Descompacte os arquivos disponibilizados para download;
  2. Abra o projeto gpe_tcf.prj através da IDE do protheus e compile-o;
  3. Copie a pasta tcf para o diretório onde está configurado o módulo web tcf;
  4. Altere as imagens conforme necessidade de sua empresa;
  5. Altere o DEFAULTPAGE de sua configuração web TCF de forma que fique com DEFAULTPAGE=u_w_tcfinit.apw;
  6. Altere as configurações do Menu do RHOnLine no SIGATCF. Programa: TCFA120 - Menu:SIGATCF.XNU, Opção do Menu: &Atualizações->Configuracoes->Menu Rh-online. Desabilite as opções padrões e inclua as seguintes chamadas: Para o Demonstrativo Inclua a rotina: W_TCFDEM; Em Alterar senha, inclua a rotina W_TCFPWD; e na opção "Informativo" inclua a rotina W_TCFINF. Se desejar obter o arquivo ISAM com as opções do menu para depois efetuar um "Append" na tabela RBC, clique aqui;
  7. Execute. Obs.: Se estiver utilizando o IE8, execute em modo de compatibilidade ("Compatibility View").
Links disponibilizados:

segunda-feira, 12 de janeiro de 2009

Protheus :: Programação Básica em Advpl “Combo VIII”

Conversão de variáveis:

No Advpl é possível converter os tipos usando uma função ou expressão condicional.


Tabela de Conversão de Tipos (Clique na Imagem para ampliá-la)

Protheus :: Programação Básica em Advpl “Combo VII”

Tipo:

O Advpl suporta os seguintes Tipos:

Array
Bloco
Caractere ( Caractere isolado ou string até 1Mb )
Data
Lógico
Memo
Numérico
Objeto

Diferentemente das linguagens como C, C++, Java, etc..., as variáveis em Advpl não são prototipadas. Seus tipos são definidos no momento após a atribuição de valores. Para testar o tipo de uma variável pode-se utilizar a função Type() ou ValType(). Type() para variáveis públicas e privadas declaradas em outras instâncias do programa e ValType() para variáveis que pertencem à mesma instància.

Ex.:

Function Test1()

Local aTest

Private aTest1 := {}

Test2( aTest )

Return( NIL )

Function Test2( aTest )

IF ( Type( “aTest1” ) == “A” )//A variável deve estar entre aspas
IF ( ValType( aTest ) == “A” )
aTest := aTest1
Else
Alert( “A variavel aTest nao eh um Array” )
EndIF
EndIF

Return( NIL )

Antes de Atribuir um valor a uma variável, seu tipo é indefinido (U). Se a variável for declarada como Private, Local ou Static, seu valor, na declaração, será NIL. As variáveis declaradas como Public recebem automaticamente a atribuição de um valor lógico .F.. As variáveis só obterão o seu valor real na atribuição.

Protheus :: Programação Básica em Advpl “Combo VI”

Variáveis:

No Advpl, as regras para a nomeação de identificadores são parecidas com as das linguagens típicas de computadores. A nomeação poderá ser iniciada com uma letra ou um caractere de sublinhado e poderão conter letras, sublinhados e dígitos. Para manter compatibilidade com o “legado” foi mantida a limitação de dez primeiros caracteres como significativos. Por isso, deve-se ter muito cuidado com nomes extensos. Se for utilizar nomes extensos lembre-se que atualmente apenas os dez primeiros caracteres serão considerados mas, se declarar uma variável com nome extenso, utilize-a da forma como foi declarada para que, se um dia a limitação de dez caracteres foi modificada, não seja necessário estar modificando os programas.

A restrição em dez caracteres pode ser fonte de defeitos ardilosos. Por exemplo, as seguintes variáveis:

cSNome_do_Pai
cSNome_do_Avo

Serão consideradas idênticas. A truncagem dos identificadores longos é conseqüência do tamanho fixo dos nomes nas tabelas mantidas pelo compilador e pelo pré-processador, portanto os nomes completos dos identificadores são passados sem alteração para o pré-processador. Tenha cuidado, esse tipo de erro é muito difícil de se depurar.

O estilo de codificação recomendada é usar letras maúsculas para destacar partes significativas de um identificador ao inves de usar sublinhados para serpará-las. Dessa forma será possível criar identificadores menores, sem afetar a legibilidade. Como em:

cSNomeDoPai
cSNomeDoAvo

Apesar dos identificadores acima terem mais de dez caracteres, os 10 mais significativos não são conflitantes. O primeiro será considerado como cSNomeDoPa e o segundo como cSNomeDoAv

Lembre-se: Somente os dez primeiros caracteres do nome de uma variável são significativos.

Uma variável tem os seguintes atributos:

Tipo
Tamanho
Duração
Visibilidade

Protheus :: Programação Básica em Advpl “Combo V”

Comandos vs Literais:

Alguns comandos em AdvPl exigem argumentos literais. Uma literal é uma seqüencia de caracteres interpretada literalmente. Não será possível usar uma constante ou expressão no lugar da literal.

Ex.:

Use cTable

No exemplo acima, cTable é uma literal. Ela é interpretado literalmente, não como uma variável. O pré-compilador irá transformar a Literal em uma string de caracter da seguinte forma:

dbUseArea( .F.,, "cTable",, if(.F. .OR. .F., !.F., NIL), .F. )

Para que cTable não seja considerado como uma literal mas sim como uma variável coloque-a entre parenteses como em:

Use ( cTable )

Que o pré-processador converterá em:

dbUseArea( .F.,, ( cTable ),, if(.F. .OR. .F., !.F., NIL), .F. )

Protheus :: Programação Básica em Advpl “Combo IV”

Constantes:

Constantes são Valores predefinidos que não mudam durante o programa. Elas podem ser strings de caracteres, números, valores lógicos, blocos de codificação, arrays ( vetores ). Ex.:

“pressione uma tecla para continuar”
15
1970.15
.T.
{ || MyTest() }
{“Domingo”,“Segunda”,“Terça”,“Quarta”,“Quinta”,“Sexta”,“Sábado”}

No Advpl, as constantes numéricas são sempre decimais. Não são suportados outros sistemas de números nem notação científica.

Não há diferença entre caracteres isolados “a” e strings “Isso é uma string”. Uma string é simplesmente um ou mais caracteres. Para definir uma constante do tipo string maior que uma linha, continue-a com um ponto e vírgula e junte as partes com o operador “+”

Ex.: “esta string será continuada”+;
“na próxima linha”

No AdvPl não existem constantes de datas. Será necessário contruí-las à partir de uma string de caracteres e com a função CToD() como no exemplo:

Ex.: Ctod( “18/02/2005”)

O resultado será uma data, mas ela não será realmente uma constante. Pois o sistema na execução a avaliará. As constantes são sempre avaliadas no momento da compilação.

Protheus :: Programação Básica em Advpl “Combo III”

Comentários:

O compilador Advpl ignora as linhas em branco e a indentação. Ambas tornam o programas mais legível e, devemos abrir mão deste recurso para tornar os programas mais legíveis. Os comentários também são ignorados pelo compilador.

Na realidade, as linhas em branco, a identação e os comentários nos programas são tratados pelo pré-processador.

No Advpl são permitidas as seguintes formas de se comentar um programa:

//Para efetuar o comentário em uma linha. ( Tudo o que estiver à direita //das ( // ) será considerado como um comentário.

Obs.: Pode-se utilizar o sinal de * ou && no inicio da frase para se efetuar o comentário de uma única linha. Deve-se escolher uma por padrão e ser coerente.

/*
Para efetuar o comentário em mais de uma linha. ( Tudo o que estiver entre ( /* e */ ) será considerado como comentário.
*/

Obs.: para facilitar a utilização do modelo de comentário acima utilize /*/ para abrir o comentário e /*/ para garantir que um comentário nunca deixará de ser fechado.

É altamente recomendável que as rotinas importantes e de utilidade sejam comentadas com um bloco de abertura. Deverão ser especificados: o propósito da rotina, os tipos de parâmetros e, se for uma função que retorna algo, o valor de retorno.

/*/
Função : SomaUm( nLast )
Autor : Marinaldo de Jesus
Data : 18/02/2005
Versão : 1

SomaUm( nLast ) -> Retorna valor de nLast incrementado de 1
nLast -> Valor onde deverá ser somado um
/*/

Obs.: A melhor documentação de um programa é o próprio programa ( desde que bem escrito e seguindo-se as regras que foram estabelecidas )

Protheus :: Programação Básica em Advpl “Combo II”

Instruções e Comandos:

As instruções são elementos fixos da linguagem, que lidam com afirmações e com o controle do programa;

c := IF( a > b , a , b )

While ( ( cAlias )->( !Eof() )
//... mais instruções
( cAlias )->( dbSkip() )
End While

nLoops := 10
For nLoop := 1 To nLoops
//... mais instruções
Next nLoop
...

Já os comandos, representam o conjunto de tipos de instruções da linguagem que não são nem rotinas nem chamadas de função e nem atribuições.

Replace cCampo1 With “Conteudo do Campo 1”,;
cCampo2 Whit “Conteudo do Campo 2”

Obs.: Como padrão, um caractere de final de linha encerra uma instrução. Para continuar na outra linha, use o caractere de ponto-e-vírgula.

Use cTable New Via “DBFCDX”
Copy To cNewTable Via “DBFCDX”

Totas as instruções são fixadas em determinada versão da linguagem e, todos os comandos são convertidos pelo pré-processador em chamadas de funções e atribuições.

Onde: Replace cCampo1 With “Conteudo do Campo 1”,;
cCampo2 Whit “Conteudo do Campo 2”

será convertido pelo pré-processador em :

_FIELD->cCampo1 := "Conteudo do Campo1";
_FIELD->cCampo2 := "Conteudo do Campo2”

Use cTable New Via “DBFCDX”

será convertido pelo pré-processador em :

dbUseArea( .T., "DBFCDX", "cTable",, if(.F. .OR. .F., !.F., NIL), .F. )

Copy To cNewTable Via “DBFCDX”

será convertido pelo pré-processador em :

__dbCopy( "cNewTable", { },,,,, .F., "DBFCDX" )

No Advpl são permitidas várias instruções numa linha ( separando-as com um caracter de ponto-e-vírgula ) como em:

i := 0 ; j := 1 ; a := “c” ; d := 99 //...

Esse recurso é interessante ao criar macros do compilador e blocos de codificação, mas se utilizado em todo lugar prejudica a legibilidade do programa.

Protheus :: Programação Básica em Advpl “Combo I”

A linguagem Advpl é um Super-Conjunto da linguagem padrão xBase/CodBase e evoluiu à partir do padrão do Clipper 5.3 e das bibliotecas do FiveWin. Ela comporta quase todos os comandos e funções utilizados no Clipper 5.3 e no FiveWin.

O Advpl é a linguagem utilizada pela Totvs/Microsiga para o desenvolvimento do ERP e no desenvolvimento de rotinas específicas nas empresas usuárias desse sistema.

Os elementos sintáticos que formam as instruções de programação em Advpl podem ser divididas nos seguintes grupos: instruções, comandos, diretivas, funções, classes, métodos, constantes , campos e operadores.

quarta-feira, 7 de janeiro de 2009

Windows® PowerShell® "Script" para sincronização do Repositório do Protheus

Hoje Postarei uma dica de como criar um "script", utilizando o Windows® PowerShell® para sincronizar os "Repositórios de Objetos" do Protheus entre o ambiente de desenvolvimento e o de produção.

Crie o arquivo syncApos.ps1 e, utilizando um editor de texto de sua preferência (Notepad, Notepad++ ou Edit, etc.), inclua o código abaixo. Altere o "Path" <$path>, a "string" de busca <$find>, a "string" de substituição <$replace>,o caminho de onde o repositório deverá ser copiado e o diretório de destino do repositório para adequar às suas necessidades.

Para maiores informações de como instalar o Windows® PowerShell® e de como executar "scripts" leia o "Post" Conexão automática ao Claro3G.

Tendo feito isso basta criar um agendamento utilizando o Agendador de Tarefas do Windows® ("Task Manager") e seus repositórios serão sincronizados.

Obs.: Para que o "script" possa ser executado no "Task Manager" faz-se necessário informar o caminho completo do programa que irá executá-lo e o "script" a ser executado. Ex.: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe c:\totvs\p10\scripts\syncApos.ps1

Se desejar baixar os "scripts" de exemplo para a sincronização dos "apos/rpos" do Protheus clique nos respectivos links: mp8syncApos.ps1 e p10syncApos.ps1.

$path = "d:\totvs\p10\bin"
$find = "SourcePath=d:\\totvs\\p10\\rpos\\rpo1"
$iniFile = "totvs*.ini"
$match = get-childitem -path $path -include $iniFile -recurse | select-string $find -list
if ( $match -match $find )
{
$replace = "SourcePath=d:\totvs\p10\rpos\rpo2"
cd d:\totvs\p10\rpos\rpo2
xcopy \\100.0.9.125\p10rpo$\rpo1\tttp101.rpo /s /c /y /d
}
else
{
$find = "SourcePath=d:\\totvs\\p10\\rpos\\rpo2"
$replace = "SourcePath=d:\totvs\p10\rpos\rpo1"
cd d:\totvs\p10\rpos\rpo1
xcopy \\100.0.9.125\p10rpo$\rpo1\tttp101.rpo /s /c /y /d
}
get-childitem -path $path -include $iniFile -recurse | select-string $find -list |% { (get-content $_.Path) |% { $_ -replace $find, $replace } | set-content $_.Path }