Vous avez tous trop de SAVF sur vos systèmes voici une commande qui va vous aider à les manager : WRKSAVFD. Bibliothèque : Confirmer si suppression : Une option 5 : Gérer vous envoi sur la commande précédemment expliquée ici . Vous trouverez les sources sur le GitHub Gaia-Mini-Systemes .
Vous voulez savoir où un programme est utilisé sur votre partition.
Généralement vous avez une cross référence de vos programmes basée sur des sorties de DSPPGMREF, et vous retrouvez assez facilement les programmes qui l’utilisent dans votre application.
Mais votre programme peut être tagué dans d’autres ressources, (Systèmes, SQL, etc …), voici une liste et comment les analyser
1) Table des travaux planifiés WRKJOBSCDE
SELECT * FROM QSYS2.SCHEDULED_JOB_INFO where command_string like(‘%VOTREPGM%’);
2) Table des travaux planifiés AJS (Advanced Job Scheduler)
SELECT * FROM QUSRIJS.QAIJS1CM where cmcmd like(‘%VOTREPGM%’)
Rappel, il est gratuit à partir de la version V7R5
3) Triggers, fonctions, procédure (catalogage SQL)
select * from qsys2.sysroutine where external_name like(‘%VOTREPGM%’)
and routine_body = ‘EXTERNAL’;
4) Webservices
echo "Recherche : VOTREPGM" > lst_webservices.txt ;
echo "/www/*/webservices/services/*/WEB-INF/classes/*.config" >> lst_webservices.txt ;
echo "---------------" >> lst_webservices.txt ;
/usr/bin/grep -i -n "VOTREPGM" /www/*/webservices/services/*/WEB-INF/classes/*.config >> lst_webservices.txt
On suppose que vos configurations sont dans www ce qui est le défaut, et on écrit dans un fichier lst_webservices.txt de votre répertoire courant.
5) Menus SDA
SELECT *
FROM QSYS2.MESSAGE_FILE_DATA
where
MESSAGE_ID like(‘USR%’) and
MSG_TEXT like(‘%VOTREPGM%’)
6) Exit PGM
select *
from qsys2.exit_program_info where exit_program = ‘VOTREPGM‘ ;
7) Les watchers
select * from qsys2.watch_info where WATCH_PROGRAM = ‘VOTREPGM‘;
8) Les postes travaux à démarrage automatique des sous-systèmes
SELECT a.autostart_job_name, a.job_description_library, a.job_description, b.request_data
FROM QSYS2.AUTOSTART_JOB_INFO a
JOIN QSYS2.JOB_DESCRIPTION_INFO b
ON a.job_description_library = b.job_description_library
AND a.job_description = b.job_description
WHERE UPPER(b.request_data) Like(‘%VOTREPGM%’);
Remarques :
C’est sans doute pas exhaustif , mais c’est déjà ça
Pensez à regarder les planificateurs si vous en avez ?
Si vous avez une machine de PROD et de DEV, il peut être intéressant d’analyser les 2 partitions.
Voila, simple mais efficace
Contrôler l’existence d’un fichier stream (IFS)
Lorsque l’on doit tester l’existence d’un objet dans QSYS.LIB la question ne se pose pas, on utilise la commande prévue à cet effet CHKOBJ. Pour l’IFS par contre il n’y a pas de commande toute faite.
Voici donc trois exemples de solutions pour tester l’existence d’un fichier dans l’IFS en CL et en SQLRPGLE.
(Il existe d’autres méthodes, mais celles-ci sont les plus simples).
CHKOUT / CHKIN (clle)
La commande CHKOUT permet de verrouiller un objet, ainsi les autres utilisateurs et travaux ne peuvent plus que le lire ou le copier. Il suffit de monitorer cette commande en attendant le message CPFA0A9 qui indique que le fichier n’existe pas.
Cette méthode est donc utile lorsque l’on souhaite par la même occasion verrouiller l’objet recherché.
Il ne faut pas oublier de déverrouiller l’objet une fois votre opération terminée avec la commande CHKIN.
PGM PARM(&FILEPATH)
DCL VAR(&FILEPATH) TYPE(*CHAR) LEN(256)
DCL VAR(&EXISTS) TYPE(*LGL)
CHGVAR VAR(&EXISTS) VALUE('1')
/* Verrouillage de l'objet */
CHKOUT OBJ(&FILEPATH)
MONMSG MSGID(CPFA0A9) EXEC(DO)
/* Fichier non trouvé */
/* Traitement de l'exception... */
CHGVAR VAR(&EXISTS) VALUE('0')
SNDPGMMSG MSG('Fichier inexistant')
ENDDO
/* Libération de l'objet si existant */
IF COND(&EXISTS) THEN(DO)
CHKIN OBJ(&FILEPATH)
ENDDO
ENDPGM
MOV (clle)
Une solution plus simple encore, lorsque l’on n’a pas besoin de gérer le verrouillage de l’objet, est l’utilisation de la commande MOV. Initialement, elle permet de déplacer un objet, mais en indiquant le chemin de l’objet d’origine comme objet de destination, aucune action ne sera effectuée sur l’objet (pas de changement de la date de dernière modification) et on pourra encore une fois tester le message CPFA0A9.
PGM PARM(&FILEPATH)
DCL VAR(&FILEPATH) TYPE(*CHAR) LEN(256)
MOV OBJ(&FILEPATH) TOOBJ(&FILEPATH)
MONMSG MSGID(CPFA0A9) EXEC(DO)
/* Fichier non trouvé */
/* Traitement de l'exception... */
SNDPGMMSG MSG('Fichier inexistant')
ENDDO
ENDPGM
IFS_OBJECT_STATISTICS (SQL/SQLRPGLE)
En SQL et SQLRPGLE, le plus simple reste d’utiliser la fonction table IFS_OBJECT_STATISTICS. Pour s’assurer ne pas tomber sur un répertoire portant le nom du fichier ou autre, il est préférable de renseigner les paramètres subtree_directories et object_type_list (bien entendu en renseignant *DIR si on cherche un répertoire).
Il suffit ensuite de tester le sqlCode, s’il est égal à 100 cela signifie que le fichier est inexistant.
SELECT path_name
FROM TABLE (
qsys2.ifs_object_statistics(
start_path_name => '/home/jl/file.txt',
subtree_directories => 'NO',
object_type_list => '*STMF'
)
);
Remarque
Il faut tout de même prendre en compte les droits de l’utilisateur qui réalise ces tests, en fonction de la méthode utilisée, un autre message pourrait être émit ou le fichier pourrait lui apparaitre comme inexistant.
Pour plus de détails
Documentation IBM – CHKOUT : https://www.ibm.com/docs/en/i/7.5?topic=ssw_ibm_i_75/cl/chkout.html
Documentation IBM – CHKIN : https://www.ibm.com/docs/en/i/7.5?topic=ssw_ibm_i_75/cl/chkin.html
Documentation IBM – MOV : https://www.ibm.com/docs/en/i/7.5?topic=ssw_ibm_i_75/cl/mov.html
Documentation IBM – IFS_OBJECT_STATISTICS : https://www.ibm.com/docs/en/i/7.5?topic=services-ifs-object-statistics-table-function
Vous avez des application 5250 que vous avez décidé d’améliorer en les passant de 80 colonnes à 132 colonnes
c’est au niveau de votre écran que vous devez indiquer cette taille par le mot clé DSPSIZ(27 132 *DS4) .
Quand vous affichez votre écran sur une unité écran de type 3477 tout va bien mais quand vous l’affichez sur une unité écran de type 3179, vous avez une erreur d’entrée sortie.
Comment faire pour éviter ce plantage ?
Bien sur définir toutes les unités écrans en 132, mais on ne maitrise par forcément toujours cette démarche, beaucoup de systèmes étant en auto-configuration.
Voici une première solution minimaliste qui évitera le plantage, et qui enverra un message dans la log
Exemple.
dcl-f
VOTREECRAN WORKSTN
usropn ;
open(e) VOTREECRAN ;
if %error ;
// si erreur on considère que c’est la taille
dsply ‘Vous devez être en 132*27’ ;
// votre traitement ici
endif ;
c’est simple et efficace
Mais vous pouvez faire quelque chose d’un peu plus propre
En effet on peut avoir un mélange dans un dspf que vous allez créer
En indiquant les 2 mots clés dans votre source
Vous devez impérativement indiquer *DS4 en premier
A DSPSIZ(27 132 *DS4 –
A 24 80 *DS3)
La problématique est de trouver la taille de votre unité écran, pour l’instant il n’existe pas de vue SQL qui fasse un DSPDEVD, vous devrez utiliser les API fournies par IBM :
ici QDCRDEVD elle a un format DEVD0600 qui contient cette information.
le source de l’écran avec ses 2 formats
A*%%TS SD 20240308 152030 PLB REL-V7R4M0 5770-WDS
A* attention 132 doit être en premier
A* le premier fixe l'affichage maxi
A DSPSIZ(27 132 *DS4 -
A 24 80 *DS3)
A* Format à afficher en cas de taille 80
A R FMT80
A*%%TS SD 20240308 152030 PLB REL-V7R4M0 5770-WDS
A CA12(12)
A 5 11'Format de l''écran'
A DSPATR(UL)
A 6 11'Ecran 80'
A 7 2'F12=Retour'
A COLOR(BLU)
A* Format à afficher en cas de taille 132
A R FMT132
A*%%TS SD 20240308 152030 PLB REL-V7R4M0 5770-WDS
A CA12(12)
A 5 70'Format de l''écran'
A DSPATR(UL)
A 6 70'Ecran 132'
A 8 2'F12=Retour'
A COLOR(BLU)
Le source du programme
On a créé une procédure interne que vous pouvez facilement externaliser dans un programme de service par exemple, (dans notre exemple, il y a une variable globale liée à la SDS … )
**free
Ctl-Opt DFTACTGRP(*NO) ;
// Test d'une procédure pour déterminer la taille de l'écran DEVD
Dcl-f RTVDEVTYPE WORKSTN ;
// Contient 2 formats
// fmt132 de taille 132
// fmt80 de taille 80
// Information du Job
dcl-ds *N PSDS;
JOB Char(10) Pos(244);
end-ds;
// Variables globales
Dcl-S w_type Char(10);
// 3477 taille 132
// 3179 taille 80
// en interactif, le travail = nom de l'écran
//
w_type = getdevtyp('*') ;
If w_type = '3477' ;
exfmt fmt132 ;
Else;
exfmt fmt80 ;
EndIf;
*inlr = *on ;
// Récupération du type de l'écran
//
Dcl-Proc Getdevtyp Export;
Dcl-PI Getdevtyp Char(10);
Inp_Device Char(10) Const;
End-PI;
// Prototypes de la l'API QDCRDEVD récupération des attributs
// d'une unité
Dcl-PR QDCRDEVD ExtPgm('QDCRDEVD');
Rcvar Like(Rcvar);
Varlen Like(Varlen);
Format Like(Format);
Device Like(Device);
Apierr Like(Apierr);
End-PR;
Dcl-DS Apierr;
Bytprv BinDec(8:0) Pos(1) Inz(216);
Bytavl BinDec(8:0) Pos(5) Inz;
Errorid Char(7) Pos(9) Inz;
Reserved Char(1) Pos(16) Inz;
ErrorDesc Char(200) Pos(17) Inz;
End-DS;
Dcl-S device Char(10) INZ;
// Format pour unité écran
Dcl-S Format Char(8) Inz('DEVD0600');
Dcl-S Rcvar Char(5000) Inz;
Dcl-S Varlen BinDec(4:0) Inz(5000);
Dcl-S typ_dev Char(10) INZ;
// Si * on considère l'écran en cours
if Inp_Device = '*' ;
Device = JOB; // attention variable globale
else ;
Device = Inp_Device;
endif ;
// Appel API systéme
CallP QDCRDEVD(
Rcvar
:Varlen
:Format
:Device
:Apierr
);
If BytAvl = 0;
// Lecture position du type de DSP
typ_dev = %Subst(Rcvar:175:10);
EndIf;
Return typ_dev;
End-Proc Getdevtyp;
Remarque :
Il y a sans doute d’autres solutions, mais vous pouvez vous contenter de l’une des deux citées ci dessus.
Il peut y avoir quelques subtilités mais globalement ça fonctionne bien.
On utilise de moins en moins PDM et SEU, pour le remplacer par du RDI ou Visual studio code, ce qui est le sens de l’histoire, mais cette commande peut vous aider, surtout si vous ne disposez pas d’outils d’analyse (Arcad, X-Analysis, Grefer, etc…)
Cette commande permet de faire un FNDSTRPDM (option 25 dans la gestion des membres) sur une liste de fichiers sources, FNDSTRPDM étant limité à un seul fichier source par recherche
Vous devez définir la liste des fichiers à analyser
Pour ceci, vous avez un fichier modèle QAUOSR2 dans la bibliothèque QPDA
Il est conseillé de le dupliquer sous un nouveau nom dans votre bibliothèque
C’est un fichier source que vous pouvez éditer facilement par SEU, RDI, VSCODE, ou SQL
Il est composé de 3 zones de 10 caractères
Exemple :
Notre fichier s’appellera souvent FNDSTRPDM2 membre FNDSTRPDM2 et on choisira une bibliothèque.
£lib £file £member
GDATA QCLSRC *ALL
GDATA QRPGLESRC *ALL
GDATA QDDSSRC *ALL
GDATA QCMDSRC *ALL
GDATA QSQLSRC M*
Dans les membres vous pouvez indiquer *ALL ou un nom générique
Vous pouvez ensuite lancer la commande de recherche avec le paramétrage que vous désirez
Exemple
FNDSTRPDM2 STRING(DCL)
FILE(GDATA/FNDSTRPDM2)
MBR(FNDSTRPDM2)
OPTION(NONE) PRTMBRLIST(YES)
Ici on a demandé une liste que vous retrouvez dans votre spool, c’est un fichier QPUOPRTF, il y en a un par fichier source
Exemple :
Nb d'occurrences . . : 102
Création Dernière modif
Membre Type Date Date Heure Enreg
---------- ---------- -------- -------- -------- -------
AAAA CLLE 01/07/21 15/01/24 15:28:17 0000006
AAAA2 CLLE 10/01/24 16/09/23 10:25:24 0000009
AAA1 CLLE 16/05/23 06/06/23 10:24:58 0000017
AADB CLLE 02/01/24 02/01/24 10:59:24 0000005
AAPF2CL CLLE 03/07/23 03/07/23 13:53:03 0000008
Vous pouvez également demandé la liste des enregistrements comportant votre chaine, PRTRCDS(*ALL) vous obtenez également à une autre liste dans vos spools
Exemple :
Membre . . . . . . . : AAAA2
Type . . . . . . . . : CLLE
Texte . . . . . . . . : Liste des touches de fonction d'un RPG
Longueur d'enreg . . : 92
SEQNBR *...+....1....+....2....+....3....+....4....+....5....+....6....+...
DCL
4 DCL VAR(&TXT) TYPE(*CHAR) LEN(80)
DCL
5 DCL VAR(&id ) TYPE(*CHAR) LEN(7)
Nombre d'enregistrements explorés . . . . . . . . : 9
Nombre d'enregistrements à trouver . . . . . . . : *ALL
Nombre d'enregistrements trouvés . . . . . . . . : 2
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ F I N D U M E M B R E
On rencontre 2 usages principalement
Pour rechercher dans tous les sources d’une bibliothèque
£lib £file £member
GDATA QCLSRC *ALL
GDATA QRPGLESRC *ALL
GDATA QDDSSRC *ALL
GDATA QCMDSRC *ALL
Pour rechercher dans tous les sources RPGLE de plusieurs bibliothèques
£lib £file £member
GDATA QRPGLESRC *ALL
GDATA1 QRPGLESRC *ALL
GDATA2 QRPGLESRC *ALL
GDATA3 QRPGLESRC *ALL
Remarques :
Vous pouvez facilement générer cette liste dynamiquement si vous le désirez en utilisant SQL et les vues de QSYS2.
Exemple :
INSERT INTO GDATA/FNDSTRPDM2 VALUES((select max(srcseq) + 1 from
gdata/fndstrpdm2), 0, ‘Biblio ‘ concat ‘ ‘ concat
‘Fichier ‘ concat ‘ ‘ concat ‘Membre ‘)
Il existe d’autres solutions pour scanner vos fichier sources, mais celle ci est simple à mettre à œuvre, et vous avez de grande chance d’avoir QPDA sur vos machines.
Pour traduire du texte dans un programme RPGLE, on peut utiliser un appel à une API de traduction (via SQL). Les principales API de traductions publiques sont DeepL API et Google Cloud Translation. Dans cet article nous utiliserons l’API de DeepL dans sa version gratuite, limitée à 500 000 caractères par mois, mais DeepL propose également d’autres offres payantes pour son API.
Documentation de l’API DeepL : https://www.deepl.com/docs-api
Introduction
Pour réaliser simplement un appel API en RPGLE, il est possible d’utiliser plusieurs méthodes SQL qui permettent de :
- Formater le corps d’une requête API en JSON
- Exécuter la requête API en POST
- Récupérer des informations dans la réponse JSON
Nous allons voir ensemble un exemple d’utilisation de ces méthodes pour créer un programme qui traduit un texte donné dans la langue choisie.
Cas d’exemple
Prenons donc le cas d’un programme simple qui récupère les paramètres suivants :
- Le texte à traduire
- Le code de la langue ciblée (‘FR’, ‘EN’, ‘ES’, ‘DE’, …)
Le programme affichera ensuite via un dsply le résultat de la traduction et le code retour HTTP de la requête.
On commence par les déclarations du programme.

On y retrouve :
- nos paramètres texte et langue_cible
- une ds qui contiendra les données retournées par la requête API (traduction et code HTTP)
- deux variables pour l’URL de la requête et le token d’authentification (qui s’obtient en créant un compte API sur DeepL)
On peut maintenant construire notre requête API en utilisant SQL.

La requête HTTP faite à l’API est exécutée via la fonction de table QSYS2.HTTP_POST_VERBOSE (qui est similaire à QSYS2.HTTP_POST, mais avec plus de détails en retour).
Elle prend en paramètres :
- l’URL d’appel de l’API
- le body de notre requête -> un objet JSON contenant le texte et la langue cible
- le header de notre requête -> un objet JSON contenant des informations supplémentaires requises comme le token d’authentification
Ici la traduction se fera avec une détection automatique de la langue source (celle du texte qu’on demande à traduire), mais on peut également ajouter le paramètre source_lang dans notre body si on veut préciser la langue source de notre texte.
On remarque l’usage des fonctions SQL JSON_OBJECT et JSON_ARRAY qui permettent de formater des données au format JSON (JSON_ARRAY pour un tableau JSON)
Les éléments que l’on récupère grâce à cette fonction sont au format JSON, on utilise donc la fonction JSON_VALUE pour les récupérer en VARCHAR dans notre ds résultat.
Dans notre cas, on s’intéresse au texte traduit et au code retour HTTP, c’est donc les valeurs translations et HTTP_STATUS_CODE qui sont extraites du JSON.
Vous pouvez obtenir plus d’informations sur la structure des requêtes API DeepL et leurs retours sur la documentation en ligne de l’API : https://www.deepl.com/docs-api
Pour finir, on affiche avec un simple dsply nos éléments de retour (dans le cas où la requête SQL a été exécutée sans erreur).

Test du programme
Lorsqu’on appelle notre programme avec les bons paramètres :

On obtient bien une traduction du texte saisi, et le code retour 200 (réussite).

Un des concepts de base qui différentie le RPG de beaucoup de langage est la bufférisation unique, je vais essayer de vous expliquer ce que c’est.
Le principe
C’est qu’une zone quelque soit son buffer de provenance DS, FMT, Zone élémentaire, etc … pour un nom et une description unique occupera qu’un seul espace mémoire dans le programme.

On dit également qu’une zone n’est pas qualifiée contrairement à d’autres langages ou on parlera de zone de tel ou tel format ou buffer….
Exemple en cobol
move nomcli of database to nomcli of ecran
dans notre cas il n’y a pas de move puisqu’on a une seule zone
évitez le (lol)
numcli = numcli ;
Les avantages
Grosse diminution des transferts de zones
Enormément utilisé dans l’existant
Les inconvénients
-Risque d’écrasement nom maitrisé, on peut avoir une zone dans 5 ou 6 buffers par exemple des zones clés
-Les doublons de zone dans 2 tables
Exemple
Filer1 dans la table1 char 30
Filer1 dans la table2 char 40
Le programme ne saura pas déterminer la définition à utiliser
Il faudra renommer une des 2 Zones, ou qualifier les zones dans les buffers ce qui revient à annuler le principe de bufferisation unique
Pour renommer une zone sur une déclaration de fichier
Vous devrez avoir une carte I
exemple ci dessous

Pour qualifier vous devrez indiquer le mot clé qualified sur la ds ou le fichier

du coup on revient à un usage classique pour les affectations de valeurs
database.nomcli = ecran.nomcli ;
Conclusion :
Ca peut être un vrai avantage, mais l’arrivée de SQL chamboule beaucoup la donne
Il faut faire un peu attention dans le cadre de la maintenance.
Les nouveaux développeurs préfèrerons un monde qualifié …
Déclaration des zones packées dans les programmes à partir de zones référencées en base de données.
Voici un exemple
Soit le fichier TESTNUM :
A R TESTNUMF
A NUMPCK 5P 0
A NUMETE 5S 0
Programme 1 :
dcl-f
TESTNUM
USAGE(*INPUT ) ;
DCL-DS DS_fichier extname('TESTNUM')
END-DS;
read TESTNUM ;
// Fin de programme
*InLr = *on;
Résultat de la compilation :
*RNF7031 NUMETE S(5,0) 10D 78M
Programme 2 identique à Programme 1 mais avec la DS_fichier qualified :
**free
//
dcl-f
TESTNUM
USAGE(*INPUT ) ;
DCL-DS DS_fichier extname('TESTNUM')
qualified
END-DS;
read TESTNUM ;
// Fin de programme
*InLr = *on;
Résultat de la compilation :
*RNF7031 NUMETE P(5,0) 78D
Conclusion
Les zones qualifiées sont automatiquement déclarées en packed, en effet elles sont considérées comme des zones internes.
Très peu d’impact en réalité, sauf à des DS passées en paramètre par exemple.
C’est le cas également pour les likerec où les zones sont implicitement qualified :
dcl-ds enreg likerec( TESTNUM) ;
if enreg.numete = … ;
Il existe une commande DSPSAVF qui permet de visualiser le contenu d’un Save File (SAVF), elle est très utile et nous nous sommes demandés si nous pouvions améliorer son ergonomie. Depuis l’intégration des Technical Release 7.5 TR2 et 7.4 TR8, de nouvelles vues et tables de fonctions permettent d’obtenir des informations à propos des SAVF […]
Afficher des formats en superposition
Vous voulez afficher 3 formats en même temps

Premier point
Les formats ne doivent pas se chevaucher
Deuxième point
les formats qui devront s’afficher en plus d’un format affiché devront avoir le mot clé OVERLAY
Troisième points
Pour que le contenu d’un format soit lu il faut
Exfmt + ou touche CF
ou
Write + Read
Notre écran a 3 formats (Haut , Milieu, Bas)
c’est le milieu qui doit être en exfmt (reçoit le focus)
Le DSPF
A DSPSIZ(24 80 *DS3)
A R HAUT
A ZONE1 10A B 6 3
A 4 3'Format haut'
A R MILIEU
A OVERLAY
A CA03(03)
A ZONE2 10A B 12 3
A 10 3'Format milieu'
A R BAS
A OVERLAY
A 22 10'F3 = Exit'
A 21 3'Format bas'
**free
Ctl-Opt DFTACTGRP(*NO);
Dcl-f overlay WORKSTN;
dou *in03 ;
write haut ;
write bas ;
exfmt milieu ;
if not *in03 ;
read haut ;
dsply zone1 ;
dsply zone2 ;
endif ;
enddo ;
*inlr = *on;
Remarque :
cette règle s’applique dans les formats Fenêtres (WINDOW)
par contre les règles de chevauchement pour les fenêtres sont différentes

