01 - Programmazione01a - RPG

RPG – FAQ & Howtos (IT Part.1)

Questa è una raccolta di domante/risposte (FAQ) o di trucchetti presi da forum di settore o raccolti durante eventi e corsi di Faq400. E’ un post in continuo aggiornamento, ti invitiamo a salvarlo nei tuoi preferiti e di collaborare allo stesso post scrivendo nell’area commenti i tuoi trucchi o tecniche che possono interessare ad altri sviluppatori IBM i.

RPG-FAQ-001: RPG e le chiamate ricorsive.

Il problema delle chiamate ricorsive dei programmi RPG è sempre stato un tallone d’Achille per gli sviluppatori IBM i… fino all’introduzione dell’ILE e all’abbandono (era ora!) del ciclo primario RPG.

In pratica se voglio che il PGMA chiami il PGMB e che il PGMB chiami a sua volta il PGMA senza andare in ricorsione ho due possibili strade:

  1. Compilo il programma con DFTACTGRP(*NO) ACTGRP(*NEW) , indicandolo nelle specifiche CTL-OPT oppure come opzione in fase di compilazione con CRTBNDRPG o CRTPGM …. in questo caso viene creato un nuovo Activation Group ad ogni chiamata del PGMA con delle prestazioni non ottimali e spreco di memoria e risorse.
  2. Creo un programma senza ciclo primario RPG (linear main program) … quindi con una procedura dichiarata come MAIN … in questo caso dovrò stare attento a chiudere file e controllare i Lock non avendo più un vero e proprio *INLR di chiusura programma (il return di una procedura lascia aperti eventuali file e attivi eventuali lock)
Esempio:
 opt-ctl Main(MyMainProc);

dcl-pr MyMainProc ExtPgm('MYPGM');

dcl-proc Main;
  dcl-pi *N;
    ... Parameters if any
  end-pi

  ... Your code.

end-proc; 

Approfondimenti su questo post di MCPress Online “RPG Fundamentals: Work with linear main programs”

RPG-FAQ-002: RPG e Indicatori … è ancora attuale utilizzarli?

Gli indicatori in RPG ormai non hanno più senso … almeno intesi come indicatori *IN55 o *IN(55), cioè nel formato numerico degli stessi: rendono il codice criptico per chi non lo ha scritto perché non si auto-documentano … come farebbe invece una variabile con il proprio nome ma di tipo “ind”.

L’unica cosa che, in qualche modo, ci tiene legati agli indicatori sono gli attributi nei Display File DSPF o nei Printer File PRTF … ma anche qui ci sono dei trucchi proprio per farne a meno.

Riporto qui sotto i link ad alcuni post interessanti, anche se vecchiotti, che invitano ad un utilizzo più furbo degli indicatori:

RPG-FAQ-003: Funzioni di STEP (PASSO) di STRDBG vs Debug Rdi

Quando facciamo Debug di un programma o service program con il DEBUG di Rational Rdi abbiamo le seguenti funzioni di STEP:

  • F5 Step INTO
  • F6 Step OVER
  • F7 Step RETURN

Sono disponibili anche con il classico STRDBG?

Iniziamo a dire che la differenza tra lo STEP OVER e lo STEP INTO è che il primo quando arriva su una CALL di un programma o la chiamata di un Procedure, esegue la CALL o la Procedure e “si ferma” appena dopo … lo STEP INTO, come dice la parola, entra nel programma chiamato oppure nella Procedure chiamata.

Step OVER e Step INTO esistono anche in STRDBG… lo Step Return no!

  • F10 esegue lo STEP OVER … ma possiamo anche scrivere il comando STEP n-righe OVER …
  • F22 esegue lo STEP INTO … ma possiamo anche scrivere STEP n-righe INTO
  • Non esiste invece in ambiente STRDBG l’equivalente allo STEP RETURN di Rational Rdi

RPG-FAQ-004: STRDBG e SEP (Set Entry Point) come con Debug Rational Rdi?

Con Rational Rdi è molto comodo debuggare lavori in Batch … oppure web service chiamati o lavori di altri utenti immettendo un Service Entry Point SEP. E’ possibile settare un Entry Point (Punto di Ingresso di Controllo) con STRDBG come lo si fa con il Debug di Rational Rdi?

Sì!, anche se la cosa non è così semplice… lo spiega molto bene questo post IT-Jungle “Debugging Server Jobs In Green Screen” di Susan Gantner:

  • STRDBG mypgm
  • SBREAK line-number (oppure SBREAK line-number USER)
  • F12
  • SBMJOB o chiamata al programma da debuggare
  • Attendere che esca il messaggio “Service Entry Point…” nella sessione dove immesso il debug
  • Premere F1 per prendere i riferimenti al JOB
  • Apire una seconda sessione ed eseguire un STRSRVJOB JOB(123456/FAQ400/MYPGM) indicando i riferimenti al JOB sopra
  • Eseguire uno STRDBG sullo stesso programma da questa nuova sessione e si vede il sorgente fermo alla riga indicata precedentemente
  • Tornare sulla sessione precedente e premere INVIO per “liberare” il JOB
  • Proseguire nel DEBUG con l’altra sessione!

RPG-FAQ-005: Quale file in quale libreria sto usando?

E’ possibile sapere quale’è il file effettivo in uso in un programma… quale libreria sto utilizzando per quel file dichiarato nelle specifiche ‘F’

Si possono recuperare le informazioni su file e libreria effettivi dalla INFDS dichiarata nella specifica ‘F’ stessa. Esempio

 DCL-F MYFILE PRINTER(132) INFDS(OPNFBK);

   DCL-DS OPNFBK;
     ODP_TYPE      CHAR(2)    POS(81);     // ODP Type
     FILE_NAME     CHAR(10)   POS(83);     // File name
     LIBRARY       CHAR(10)   POS(93);     // Library name 

RPG-FAQ-006: XML-INTO Esempi

In questo blog Yusi4code c’è una guida all’uso di XML-INTO in ILE RPG per il parsing di documenti XML: “XML parsing in AS400 (IBM-i) using XML-INTO


RPG-FAQ-007: Recuperare la data creazione di un file in IFS

Per recuperare attributi di un oggetto IFS si può utilizzare la IFS API stat() … ma se ti serve la data di creazione dell’oggetto devi ricorrere alla API Qp0lGetAttr()–Get Attributes … puoi trovare un esempio di utilizzo su Think400: “CBX127 Change IFS attributes – CPP” .

Con la API stat() puoi recuperare invece altri attributi … ma non la data di creazione dell’oggetto!:

D stat            PR            10I 0 ExtProc('stat')                   
D path * Value Options(*string)
D buf
-------------
D statDS DS Qualified Template
D st_mode 10U 0
D st_ino 10U 0
D st_nlink 5U 0
D st_reserved2 5U 0
D st_uid 10U 0
D st_gid 10U 0
D st_size 10I 0
D st_atime 10I 0
D st_mtime 10I 0
D st_ctime 10I 0
D st_dev 10U 0
D st_blksize 10U 0
D st_allocsize 10U 0
D st_objtype 11A
D st_reserved3 1A
D st_codepage 5U 0
D st_ccsid 5U 0
D st_rdev 10U 0
D st_nlink32 10U 0
D st_rdev64 20U 0
D st_dev64 20U 0
D st_reserved1 36A
D st_ino_gen_id 10U 0
-----------
D fileStats DS Likeds(statDS)


if stat('/path/to/file': fileStats) < 0;
// error handling
endif;

// ccsid now in fileStats.st_ccs


RPG-FAQ-008: Convertire Hex to Decimal

Come convertire una stringa esadecimale Hex al suo valore decimale Dec … come fa questo servizio online ( https://www.binaryhexconverter.com/hex-to-decimal-converter ) ?

Con la api ctvch:

 **free

ctl-opt dftactgrp(*no);

dcl-pr cvtch extproc('cvtch');
  dest pointer value;
  src pointer value;
  len int(10) value;
end-pr;

dcl-s hexValue char (8) inz('00BC614E');
dcl-s binValue int (10);
dcl-s len int(10);

len = %len(hexValue);

cvtch(%addr(binValue):%addr(he
xValue):len);

*inlr = *on; 

Oppure, più un generale con le api strol() e itoa() … dove converti da una base ad un’altra … esempio da binario a decimale o esadecimale ecc.

      ctl-opt actgrp(*new) option(*srcstmt:*nodebugio);

        dcl-pr strtol int(10) extproc('strtol');
          iString pointer value options(*string);
          oBuf pointer value;
          iRadix int(10) value;
        end-pr;
        dcl-s pdummy pointer;

        dcl-pr itoa extproc('__itoa');
          iNum int(10) value;
          oBuf pointer value;
          iRadix int(10) value;
        end-pr;

        dcl-s buffer char(20);
        dcl-s value int(10);

        value = strtol( '00000000000000000000000000111101':%addr(pdummy): 2);
        dsply value;  // Shows 61

        itoa( value: %addr(buffer): 16);
        dsply buffer;  // Shows 3d

        *inlr = *on;
 

Anche il REXX permette queste conversioni

 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*             XCALC EXP('REVERSE("String")')                    */
/* ______________________________
______________________________
_ */
/*                                                               */
/*  Yet another *FREEWARE* utility from Prime Suspect Software,  */
/*        creating slightly above average products for the       */
/*                enlightened masses since 1982.                 */
/* ______________________________
______________________________
_ */
/*                                                               */
/*  REXX Program Name... XCALC                                   */
/*  Programmer.......... Matt Sargent                            */
/*  Internet Address.... M.SARGENT@GENIE.GEIS.COM                */
/*                                                               */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

signal on syntax

parse arg string
parse value space(string) with "'"expression"'"
interpret 'answer =' expression
answer = strip(expression) '=' answer
'SNDPGMMSG MSG(&answer)'
exit

syntax:
   syntaxerr = errortext(rc)
   'SNDPGMMSG MSG(&syntaxerr)'

================

  CMD        PROMPT('Expression Calculator')
  PARM       KWD(EXP) TYPE(*CHAR) LEN(256) MIN(1) +
               CHOICE('Character value') +
               PROMPT('Mathmatical expression')

=========

d2x() is the REXX function to convert Decimal to heXdecimal.  The
data conversions available in the AS400 implementation of REXX are:
b2x()  binary    to hex
x2b()  hex       to binary
c2d()  character to decimal     (EBCDIC)
d2c()  decimal   to character   (EBCDIC)
c2x()  character to hex         (EBCDIC)
x2c()  hex       to character   (EBCDIC)
d2x()  decimal   to hex
x2d()  hex       to decimal 

Se invece l’esigenza è la trasformazione di un packed salvato in una stringa al suo valore numerico originale … lo si può fare anche in SQL (fonte ITJungle “Extracting Zoned and Packed Decimal Values from Character Fields“)

 D usrf01          ds 
 D   TypeCode              1      2s 0 
 D   Category              3      4p 0 
 D   Amount                5      8p 2 

-- Get the amount checking is sign
select dec(
         dec(substr(hex(substr(usrf01,5,4)),1,5)||'.'||
             substr(hex(substr(usrf01,5,4)),6,2),
         7,2) *
         (case when substr(hex(substr(usrf01,5,4)),8,1) = 'D'
                 then -1 else 1 end),
       7,2) as Price
  from usrfldpf

RPG-FAQ-009: Crypt e Decrypt di una stringa in RPG (AES 128)

In RPG non ci sono delle funzioni specifiche ma puoi utilizzare SQL Embedded e criptare/decriptare non solo in Advanced Encryption Standard (AES), ma anche in Triple DES (TDES) e in RC2.

Vediamo qui con SQL come criptare e poi decriptare una stringa

With mytext as (
 select  Encrypt_AES('This is my secret', 'Pa$$w0rd') as txtEncrypted
 from sysibm.sysdummy1)

 select Decrypt_char(txtEncrypted, 'Pa$$w0rd')
 from mytext;

In RPG con SQL embedded puoi fare come nell’esempio che segue: prestare attenzione alla lunghezza della variabile per il testo criptato … anche un testo breve può diventare molto lungo una volta criptato! Anche indicare la password in una costante scritta nel sorgente non è una bella cosa, sarebbe meglio una tabella oppure utilizzare direttamente l’ENCRYPTION PASSWORD special register!

DCL-S Text   VarChar(20);
DCL-S Encrypted VarChar(256);
DCL-S Password VarChar(15)  inz('yourPassWord')

Text = 'Whatever Text'
Exec SQL  Set :Encrypted = Encrypt_AES(Text, PassWord);
 

Se non ti piace SQL e preferisci farti male con le API di sistema puoi anche optare per

Encrypt Data (QC3ENCDT, Qc3EncryptData) API

Riferimenti:

RPG-FAQ-010: API by example

Se cerchi esempi di API in RPG c’è questo ottimo sito di Carsten Flesburg:

Articles on API – API by example – Carsten Flesburg

--- Roberto De Pedrini Faq400.com
About author

Founder di Faq400 Srl, IBM Champion, ideatore del sito Faq400.com e del Blog blog.faq400.com. Sviluppatore RPG da quando avevo i pantaloni corti, forte sostenitore della piattaforma IBM i (ex AS400), ho sempre cercato di convididere le mie conoscenze con gli altri tramite forum, eventi e corsi. Oggi, tramite Faq400 Srl, cerchiamo di aiutare le aziende a sfruttare al meglio questa fantastica piattaforma IBM i.

Rispondi

%d blogger hanno fatto clic su Mi Piace per questo: