, , Trigger sur insert

Vous voulez créer un trigger qui vous indique la création d’un enregistrement dans un fichier par exemple pour superviser, dans notre exemple on enverra un email , il est conseillé de faire un fichier de paramétrage

En CLLE soit le programme Alerte_msg


             PGM        PARM(&BUFFER &BUFLEN)             
/* Paramètres */                                          
             DCL        VAR(&BUFFER) TYPE(*CHAR) LEN(200) 
             DCL        VAR(&BUFLEN) TYPE(*CHAR) LEN(4)   
/* Variables de travail */                                
             DCL        VAR(&USR) TYPE(*CHAR) LEN(10)     
             DCL        VAR(&JOB) TYPE(*CHAR) LEN(10)     
             DCL        VAR(&NBR) TYPE(*CHAR) LEN(06)
             DCL        VAR(&EMAIL) TYPE(*CHAR) LEN(50)
             DCL        VAR(&SUJET) TYPE(*CHAR) LEN(100)
             DCL        VAR(&NOTES) TYPE(*CHAR) LEN(200)
             RTVJOBA    JOB(&JOB) USER(&USR) NBR(&NBR)    
             CHGVAR     VAR(&EMAIL) VALUE('votre@mail.fr')
             CHGVAR     VAR(&SUJET) VALUE('Enregistrement crée')
             CHGVAR     VAR(&NOTES) VALUE('Job :' +
                          *BCAT &NBR *TCAT '/' *TCAT &USR *TCAT '/' +
                          *TCAT &USR)                                    
 SNDSMTPEMM RCP((&EMAIL)) SUBJECT(&SUJET) NOTE(&NOTES) +
             CONTENT(*HTML)            
  MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERREUR))
goto fin                                                           
/* Gestion des erreurs  */                                         
erreur:                                                            
             SNDUSRMSG  MSG('Envoi email impossible pour msgint ' +
                          *bcat &job *bcat &usr *bcat &nbr ) +     
                          MSGTYPE(*INFO)                           
fin:                                                               
ENDPGM 

Pour attacher votre programme et enregistrer votre trigger

 ADDPFTRG   FILE(&LIB/REP_VALID) TRGTIME(*AFTER)     
              TRGEVENT(*INSERT) PGM(&LIB/ALERT_MSG) 
              TRGLIB(&LIB) 

&lib sera le nom de votre bibliothèque

En SQL ca créera un programme CEE et ca l’associera au trigger

CREATE OR REPLACE TRIGGER ALERTE_MSG                                   
 AFTER  INSERT ON REP_VALID                                            
 REFERENCING NEW AS N                                                  
 FOR EACH ROW                                                          
 MODE DB2ROW                                                           
-- email destinataire                                                  
 BEGIN                                                                 
DECLARE W_EMAIL CHAR(50);                                              
DECLARE W_SUJET CHAR(100);                                             
DECLARE W_NOTES CHAR(200);                                             
DECLARE EXIT HANDLER FOR SQLSTATE '38501'                              
 RESIGNAL SQLSTATE '38501' SET MESSAGE_TEXT = 'ENVOI MAIL IMPOSSIBLE.';
SET W_NOTES = 'Job : ' concat trim(N.REPNBR)                  
concat '/' concat trim(N.REPUSER) concat '/' concat trim(N.REPJOB) ;
SET W_EMAIL = 'votre@email.fr' ;                 
SET W_SUJET = 'Enregistrement crée' ;   
CALL QCMDEXC('SNDSMTPEMM RCP((''' concat trim(w_email) concat           
''')) SUBJECT(''' concat trim(replace(w_sujet , '''', '"'))             
concat ''') NOTE('''                                                    
concat trim(replace(W_NOTES , '''' , '"')) concat''') CONTENT(*HTML)') ;
END;  

Remarques :

Dans les 2 cas si l’utilisateur n’est pas inscrit à la liste de distribution votre email ne sera pas envoyé
c’est plus simple de gérer l’erreur en CLP.
Si vous devez accéder aux données du buffer ca sera plus rapide et plus simple en SQL ici n.zone

C’est des triggers après , puisque l’information doit être écrite dans tous les cas .

, , Afficher plusieurs colonnes d’enregistrements dans un sous-fichier

Contexte

Un sous-fichier nous permet d’afficher un nombre de lignes qui est limité par la taille de l’écran.
Cette taille est définie dans le script source de l’écran par le paramètre SFLPAG.

On possède un fichier que l’on souhaite afficher et qui contient plus de 19 enregistrement. Il serait donc intéressant de l’afficher sur plusieurs colonnes.

Solution

Une petite modification du script source permet de créer un sous fichier qui contient plusieurs colonnes. Il faut donc indiquer le nombre total de données que l’on souhaite voir à l’écran dans SFLPAG ainsi que le nombre de caractère qui séparent deux colonnes

La maquette se présente ainsi, le paramètre de SFLLIN correspond à l’espace (en caractères) entre deux colonnes.

En exécutant le programme, on obtient :

, Prompt override program

Le programme de substitution d’invite (prompt override program) vous permet de pouvoir précharger les valeurs d’une commande afin de pouvoir modifier les paramètres existants. Comme les commandes CHGUSRPRF, CHGCMD etc…

Dans mon exemple, j’ai créé une commande CHGCLIENT afin de pouvoir modifier le libellé et le pays d’un client. Je veux que l’utilisateur saisisse le numéro du client et que ma commande récupère les valeurs actuelles. On est d’accord qu’il faudrait rajouter plus de contrôles mais c’est pour montrer le fonctionnement.

Tout d’abord il faut créer la commande en précisant le mot clé KEYPARM(*YES) sur les paramètres clé.

Ecrire ensuite le programme de récupération des données du client (CHGCLIENTO). Dans mon exemple c’est un programme RPG mais vous pouvez faire un programme CL.

Il doit recevoir en paramètres :
1. Le nom de la commande (alpha de 10)
2. Les paramètres clés (numéro client pour moi)
3. La suite des paramètres de la commande (LIBELLE et PAYS). Il faudra préciser la taille du paramètre en hexa en début de variable.

Ecrire le programme de traitement de la commande (CHGCLIENT).

Il ne vous restera plus qu’à créer votre commande en précisant le paramètre PMTOVRPGM

Exemple : CRTCMD CMD(CURLIB/CHGCLIENT) PGM(CURLIB/CHGCLIENT) TEXT(‘Modification client’) PMTOVRPGM(*CURLIB/CHGCLIENTO)

Au lancement de la commande, vous saisissez le numéro du client et les autres informations seront chargées.

, , Intégrer Windows dans vos applications 5250

Vous avez des possibilités en standard sur votre IBMi :
Pour générer du PDF
Pour générer du CSV
Pour Générer du TXT

Comment rendre efficace et intégrer ces fichiers sous WINDOWS ?

1 ) Le profil utilisateur propose en standard un répertoire par défaut, /home/votreuser.

Vous devez créer cette directory qui deviendra alors votre répertoire par défaut.
La prise en compte est à la prochaine connexion
Vous pouvez contrôler en faisant
==> WRKLNK

2 ) Sur l’ibmi vous devez monter un partage, s’il n’existe pas encore sur le répertoire /home

3 ) Sur votre PC il est conseillé de monter un partage windows sur /home/votreuser chez nous lettre P.

4 ) Sur votre PC vous devrez vérifier que vos associations d’extension de fichier sont bien rattachées au bon logiciel

5 ) Vous devez ensuite générer le fichier

Exemple :

Génération d’un csv par les commandes de l’IBMI

CPYTOIMPF FROMFILE(QGPL/QAUOOPT)
TOSTMF(Liste_Options_PDM.csv)
MBROPT(REPLACE) STMFCCSID(PCASCII)
RCDDLM(CRLF) STRDLM(NONE) FLDDLM(‘;’)
ADDCOLNAM(*SQL)

Le fichier produit s’appelle Liste_Options_PDM.csv

Si vous allez dans le partage windows vous le voyez

Si on voulait boucler la boucle il faudrait pouvoir le lancer directement à partir de l’interface 5250

Vous disposez de 2 commandes coté IBMi pour faire

La première (STRPCO) va démarrer l’intégrateur qui va vous permettre de passer des commmandes sur votre pc

La deuxième (STRPCCMD) va exécuter la commande windows demandée

Ci joint un petit scripte pour enchainer tout ca ici c’est un programme indépendant, mais vous pouvez facilement le glisser dans un pgm de services

Je vous ai joint une commande pour habiller, si vous voulez tester

Le programme

**free
 // Paramètres
DCL-PI *N;
  P_Commande         CHAR(1023);  // IN / COMMANDE À EXECUTER
  P_Status           CHAR(02);    // OUT/ STATUS KO ET OK
END-PI;
if %parms() < 2 ;
dsply 'Ce programme nécessite 2 paramétres' ;
  *inlr = *on ;
  return ;
endif ;
  // démarrage de PCO
  exec sql call qsys2.qcmdexc('STRPCO') ;
  // démarrage de explorer
  exec sql
  call qsys2.qcmdexc('STRPCCMD PCCMD(''' concat :P_Commande  concat ''') PAUSE(*NO)') ;
  if sqlcode = 0 ;
    P_Status = 'OK'  ;
  else ;
    P_Status = 'KO'  ;
  endif ;
// Fin de programme
*inlr = *on ;

La commande

             CMD        PROMPT('Exécution commande windows')
             PARM       KWD(COMMANDE) TYPE(*CHAR) LEN(1023) +
                          DFT(EXPLORER) PROMPT('Commande windows')
             PARM       KWD(STATUS) TYPE(*CHAR) LEN(2) +
                          PMTCTL(*PMTRQS) PROMPT('Status sur +
                          l''exécution')

Exemple :

On demande l’ouverture du fichier généré

==>EXECWIND COMMANDE(‘P:LISTE_DES_OPTIONS_DE_PDM.CSV’)
P: lettre de partage
.CSV associé à excel

Conclusion :

Simple mais efficace
Ne pas oublier de faire du ménage dans les répertoires utilisateurs

, Nouvelle vue SYSMEMBERSTAT

Avec TR4 de la version V7R5 est arrivé la vue SYSMEMBERSTAT qui permet d’avoir des statistiques sur les membres, des informations supplémentaires à celles existantes à ce jour.

Voici un exemple :

SELECT TABLE_SCHEMA,
       TABLE_NAME,
       SYSTEM_TABLE_MEMBER,
       SOURCE_TYPE,
       LAST_CHANGE_TIMESTAMP,
       LAST_SAVE_TIMESTAMP,
       NUMBER_ROWS,
       OPEN_OPERATIONS,
       CLOSE_OPERATIONS,
       INSERT_OPERATIONS
    FROM QSYS2.SYSMEMBERSTAT
    WHERE table_schema = 'GMODERN'
          AND last_change_timestamp > CURRENT DATE - 1 MONTH
          AND SOURCE_TYPE IS NOT null
    ORDER BY last_change_timestamp DESC

Résultat

Rappel :

Il existe déjà une vue SYSPARTITIONSTAT qui donne sensiblement les mêmes informations

et ne rêver pas vous n’avez toujours pas le dernier utilisateur qui a modifié

Comment faire afficher un écran à un traitement batch ?

Les traitements 5250 sont faits pour tourner en interactif, mais pour différentes raisons vous pouvez vouloir les faire tourner en batch :

Pas de login
sécurisation pas de attn request
etc …

Voici un exemple, dans une société ou j’ai travaillé, les unités de productions pouvaient demander un mot de passe à l’astreinte, par exemple la nuit.

La solution la plus simple qu’on avait trouvée était de lui afficher un programme de changement sur son unité écran.

On lui demandait donc son profil utilisateur et son unité écran et on lançait le programme sur celle ci, il pouvait changer son mot de passe directement.

C’est cet exemple que j’ai simplifié qui est ci dessous

le DSPF source

                                                                 
     A                                      DSPSIZ(24 80 *DS3)             
     A                                      CA03(03)                       
     A                                      INDARA                         
     A          R FMT01                                                    
     A*%%TS  SD  20240527  074541  PLB         REL-V7R4M0  5770-WDS        
     A                                  4 20'Réinitialiser un mot de passe'
     A                                  7  2'Utilisateur         :'        
     A                                  8  2'Nouveau mot de passe  :'        
     A                                 22  1'F3=Exit'                      
     A            USER          10A  O  7 26                               
     A            PWD           32A  B  8 26CHECK(LC)                      
     A            DEV           10A  O  4 54                               
     A            TEXTE         30   O 14  3                               

Le CLP source

pgm  (&dev &user)                                                
/*----------------------------------------------------------*/   
/* Ce programme permet de faire afficher un écran en batch  */   
/* vous devez acquérir l'unité écran                         */   
/* pour réinitialiser un mot de passe                        */   
/* sbmjob cmd(call initpwdr (&dev &user) job(QPADEV0001)    */   
/*----------------------------------------------------------*/   
dcl &dev  *char 10                                               
dcl &user *char 10                                               
/* Contrôles existence                      */                   
             CHKOBJ     OBJ(&DEV) OBJTYPE(*DEVD)                 
             MONMSG     MSGID(CPF9801) EXEC(DO)                  
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +           
                          MSGDTA('L''unité écran doit exister') +
                          MSGTYPE(*ESCAPE)                       
             enddo 
            ALCOBJ     OBJ((&DEV *DEVD *EXCLRD)) WAIT(0)       
            monmsg CPF1002  exec(do)                           
            SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +          
                         MSGDTA('Impossible d''allouer l''unité +
                         écran doit exister') MSGTYPE(*ESCAPE) 
            enddo                                                                                            
             CHKOBJ     OBJ(&user) OBJTYPE(*usrprf)              
             MONMSG     MSGID(CPF9801) EXEC(DO)                  
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Le +
                          profil utilisateur doit exister') +         
                          MSGTYPE(*ESCAPE)                            
             enddo                                                    
 /* Lancement du programme sur l'écran remote  */                      
             SBMJOB     CMD(CALL PGM(INITPWDR) PARM((&DEV) (&USER))) +
                          JOB(&DEV) JOBQ(QSYSNOMAX)  
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +          
                          MSGDTA('Travail, ' *bcat &dev *bcat + 
                          'démarré') MSGTYPE(*comp)                              
endpgm                                                                

Le CLP cible

pgm  (&dev &user)                                                
/*----------------------------------------------------------*/   
/* Ce programme réinitiliase  un mot  de passe              */   
/* Peut être envoyé sur un écran remote qui est sur la      */   
/* Mire                                                     */   
/*----------------------------------------------------------*/   
dcl &dev *char 10                                                
dclf initpwdr                                                    
monmsg cpf0000 exec(goto erreur)                                 
/* controles fait dans le programme appelant */                  
             OVRDSPF    FILE(INITPWDR) DEV(&DEV) OVRSCOPE(*JOB)  
          chgvar &pwd     'ici mot de passe'                     
             dountil &in03                                       
             SNDRCVF    RCDFMT(FMT01)                            
             if cond(*not &in03) then(do)                        
             if (&pwd *ne 'ici mot de passe') then(do)           
             CHGUSRPRF  USRPRF(&USER) PASSWORD(&PWD) PWDEXP(*YES)
             Chgvar &texte ('Mot de passe réinitialisé')         
             enddo                                               
             enddo                                                   
             enddo                                                   
/* fin traitement */                                                 
             DLTOVR     FILE(INITPWD) LVL(*JOB)                      
             MONMSG     MSGID(CPF0000)                               
return                                                               
erreur:                                                              
             SNDUSRMSG  MSG('Réinitialisation de mot de passe pour' +
                          *BCAT &USER *BCAT ', actuellement +        
                          impossible') MSGTYPE(*INFO)                
             MONMSG     MSGID(CPF0000)                               
endpgm                                                               

La seule spécificité est de choisir l’unité écran d’affichage, pour qu’elle soit éligible elle doit être à la mire de connexion.

La commande pour habiller

             CMD        PROMPT('Réinitialisation de PWD')           
             PARM       KWD(UNITE) TYPE(*NAME) LEN(10) MIN(1) +     
                          PROMPT('Unité écran')                     
             PARM       KWD(PROFIL) TYPE(*NAME) LEN(10) MIN(1) +    
                          PROMPT('Profil utilisateur')              

le lancement job source:

La saisie sur le job cible :

Sur un changement de mot de passe, il peut être important d’avoir un suivi ?

Bien sur, vous pouvez ajouter des contrôles …

Le job cible qui tourne en batch !

Donc pas de possibilité d’ATTN Request , et messages d’erreurs dans QSYSOPR

Remarques :

Vous pouvez choisir l’utilisateur de lancement, mais QSECOFR est interdit dans un SBMJOB
Simple mais efficace, mais difficilement généralisable, on doit avoir un OVRDBF par DSPF

Intéressant pour des demandes ponctuelles

Dans les CLP vous pouvez indiquer directement l’unité sur la commande SNDRCVF

, Mettre un délai sur un programme 5250

Vous avez tous des enregistrements qui restent verrouillés dans vos traitements 5250, par exemple parce que la personne à quitter son poste.

Vous voulez donc qu’au bout d’un certain temps, votre programme déverrouille l’enregistrement

Vous avez une première solution qui est système

Elle se base sur les valeurs systèmes

QINACTITV qui indique un délai en seconde
QINACTMSGQ ou vous indiquez:
Une MSGQ, un message CPI1126 sera envoyé, à vous de mettre en place un watcher qui analyse et traite ces messages
*DSCJOB votre travail sera déconnecté et le système tentera de vous reconnecter (attention ca ne déverrouille pas toutes les ressources)
*ENDJOB votre travail sera arrêté, et donc tous sera libéré avec risques de déphasage dans votre base de données.

Mais vous pouvez être plus précis en le prévoyant dans les développements que vous ciblerez comme étant sensible, voici un exemple ci-dessous .

Notre exemple commenté

Dans le DSPF, vous devrez compiler votre écran avec un paramètre WAITRCD(délai en secondes), passer ce délai le système quittera le READ en attente.

Vous pouvez également indiquer ce paramètre temporairement par la commande OVRDSPF

Dans le programme RPGLE

Vous devrez indiquer l’option maxdev(*file) sur votre écran (c’est celle qui vous permet d’avoir plusieurs travails sur le même écran, pour faire une bataille navale par exemple)

Dans votre traitement vous ne pouvez pas utiliser l’instruction EXFMT (qui est un condensé d’un write et d’un read)

Vous devez explicitement indiquer les 2 instructions

WRITE du format ;
READ du fichier ;

Pour tester et savoir si vous avez dépassé le delai, vous pouvez tester la fonction %status qui vous renverra la valeur 01331 si vous avez débordé, vous pouvez également utiliser L’infds de votre écran.

Voici un exemple ou on a mis que ce qui est indispensable, à vous d’ajouter ce qui sera nécessaire à votre cas .

L’écran DSPF

A* OPTIONS DE COMPILE pour GDDS -------------------->          
A*<COMP>WAITRCD(60)</COMP>                                      
A* ------------------------------------------------->          
A*                                                         
A                                      DSPSIZ(24 80 *DS3)      
A                                      CA03(03)                
A                                      INDARA                  
A          R FMT01                                             
A* 
A                                      INVITE                  
A                                  5  3'Votre format de saisie'
A                                  7 12'Zone 1 :'              
A            ZONE1          4   B  7 23                        
A                                 22  2'F3=Exit'               

Dans notre exemple nous utilisons GDDS qui permet de mémoriser les options de compile pour les DSPF chez vous, vous devrez le mettre dans la commande de création de votre écran

==>CRTDSPF … WAITRCD(60) …

Le RPGLE

**free                                                    
// Ecran compiler avec WAITRCD(60)                          
dcl-f TESTDSPF workstn                                    
 maxdev(*file) ;                                          
 //                                                       
   dou     *in03        ;                                 
  // remplace un EXFMT insensible au Paramètre WAITRCD    
       write FMT01   ;                                    
       read(e) TESTDSPF ;                                 
  // 11331 indique que le temps est dépassé               
  select  ;                                               
  when  (%status  = 01331) ;                              
       dsply  'temps dépassé ...' ;                       
       *in03  = *on ;                                     
  // F3 activé                                            
  when *in03   ;                                          
       dsply  'sortie demandée .' ;                       
  // Touche entrée avec  lecture des zones                                      
  other    ;                                              
       dsply  'validation ok ...' ;
  endsl    ;                       
 enddo  ;                          
//                                 
*inlr = *on ;

Conclusion :

C’est simple et efficace et certains traitements devraient avoir cette option

Vous pouvez aussi utiliser facilement cette technique pour faire du rafraichissement auto, par exemple un tableau de bord toutes les 3 minutes …

Voir également cet exemple en CLP dans un article précédent ici : https://www.gaia.fr/reaffichage-automatique-dun-dspf/

, , , Convertir un SAVF en PF

il faut savoir qu’un fichier SAVF est un fichier PF de 528 de long.

Vous pouvez avoir besoin pour différentes raisons de convertir SAVF en PF pour manipuler plus simplement par SQL, pour contourner des restrictions d’utilisations etc ..

Vous trouverez ici 2 commandes qui vont vous permettent de réaliser cette opération : https://github.com/Plberthoin/PLB/tree/master/CPYSAVF

Copier un SAVF dans un PF

Voir le PF

Copier un PF dans un SAVF

Voir le Résultat

Pour voir ce résultat taper la commande DSPSAVF , Voir WRKSAVF si vous avez installez notre outil

Remarque :

Outil simple mais efficace qui peut rendre des services à des administrateurs

, , Plugin JCRCMDS pour RDI

Plugin open source peu connu de Craig Rutledge mais qui est bien utile pour convertir les spécifications H, F et D en RPG full free.

Il existe plusieurs outils pour convertir ces spécifications mais l’avantage de celui-ci est qu’on peut le faire directement dans RDI .

Lien pour le téléchargement :

https://sourceforge.net/projects/jcrcmds/

Installation

Extraire l’archive.

Comme pour le plugin isphere, il faut lancer RDI puis Aide > Installer un nouveau logiciel

Il suffit ensuite de sélectionner le dossier précédemment extrait.

Utilisation

1ère solution : Sélectionnez le code à convertir puis faites un clic droit JCRCMDS > convert selection(H,F,D).

Résultat :

2ème solution : Faites un clic droit directement dans le code puis JCRCMDS > Convert all(H,F,D)

Toutes les spécifications H, F ou D seront transformées.

, Utilisez un booléen dans une commande

Voici un exemple classique, vous devez définir dans vos commandes, un paramètre qui peut prendre comme valeur oui ou non par exemple

Généralement vous allez faire comme ceci

dans la commande

PARM KWD(CRTFILE) TYPE(CHAR) LEN(4) RSTD(YES) +
DFT(NO) VALUES(NO *YES) PROMPT('Création du +
fichier')


dans le programme

         PGM        PARM(.... &CRTFILE)
/* Paramètres */
//
DCL &CRTFILE *CHAR 4
//
IF COND(&CRTFILE = '*YES' ) THEN(DO)
//
ENDDO

Voici une solution en utilisant un booléen dans le programme

dans la commande

        PARM       KWD(CRTFILE) TYPE(*CHAR) LEN(4) RSTD(*YES) + 
                     DFT(*NO) VALUES(*NO *YES) SPCVAL(('*YES' + 
                     '1') ('*NO' '0')) PROMPT('Création du +    
                     fichier')                                  

dans le programme

         PGM        PARM(.... &CRTFILE)
/* Paramètres */
//
DCL &CRTFILE *LGL
//
IF COND(&CRTFILE) THEN(DO)
//
ENDDO

L’astuce consiste à indiquer une valeur à renvoyer dans la commande ici on se borne à ‘0’ et ‘1’ ce qui peut être interprété par votre programme comme un booléen

L’intérêt d’utiliser un booléen c’est d’avoir un code simplifier , un peu comme les indicateurs qui simplifient la lecture d’un code RPGLE ou CLLE