Developpement 400 RPG COBOL Valid XHTML 1.1Cette page est conforme à la norme CSS!Mise à jour 03-2013
SOMMAIRE

Présentation
Exemples de sous-fichiers
Télécharger les exemples
XML, Dates, Inspect

PRESENTATIONRetour en haut de page

Cette page présente des modèles de programmes en RPG 3 et 4 et en Cobol.
Il ne s'agit pas d'un cours sur ces langages, mais plutôt d'un squelette de programme mettant en oeuvre des sous-fichiers.
A quelques différences près, ce modèle s'adapte aux différents langages.
Pour le cobol, des programmes mettant en oeuvre le traitement XML, les dates et l'instruction INSPECT.

Exemples de sous-fichiersRetour en haut de page

Fonctionnalités

L'exemple construit ici est très simplifié, tant au niveau de ses fonctionnalités que de ses données.
Il est organisé autour de deux tables : une table de clients (TESTPF1) attachés à des responsables (TESTPF2).
La clé des clients est composée de cinq champs, dont les deux premiers seulement nous importent ici : le code responsable et le code client.

Le programme principal (TEST02) se présente ainsi :
  • Un écran d'appel pour entrer un code responsable. La touche F4 appelle le second programme qui permet un choix.
    On contrôle qu'il existe des clients pour ce responsable.
  • Un sous-fichier STATIQUE pour afficher les clients du responsable choisi.
    Il est possible de se positionner à partir d'un n° de client saisi dans l'entête.
  • Sur le sous-fichier, plusieurs options peuvent être choisies simultanément.
    L'option 3 permet d'afficher le détail d'un client dans une fenêtre; l'option 9 est indisponible et déclenche un message d'erreur.
    La touche F12 abandonne l'exploitation des options.
Le deuxième programme (TEST03) se présente ainsi :
  • Un sous-fichier DYNAMIQUE en fenêtre pour afficher les responsables.
    Il est possible de se positionner à partir d'un nom saisi dans l'entête.
  • Sur le sous-fichier, plusieurs options peuvent être choisies simultanément.
    L'option 1 permet de choisir le responsable à traiter. Le premier choisi est sélectionné et le programme se termine
    L'option 3 pour afficher le détail d'un responsable en fenêtre.
    L'option 9 est indisponible et déclenche un message d'erreur.
    La touche F12 abandonne l'exploitation des options.
Le troisième programme (TEST04) se présente comme TEST02 avec les particularités suivantes :
  • Le sous-fichier est traité à travers la boucle principale et non plus dans une fonction unique. Cette architecture permet de définir deux types de comportement avec des routines similaires.
  • Le premier ("global") contrôle la totalité du sous-fichier, jusqu'à la première erreur. C'est seulement quand il n'y a plus d'erreur que le sous-fichier est traité ligne par ligne.
  • Le second ("par ligne") traite le contrôle de chaque ligne, suivi immédiatement en cas d'erreur de l'affichage de cette erreur, et en cas de données correctes de la validation du traitement demandé sur la ligne.
Les fichiers suivants à télécharger constituent les sources des fichiers et programmes, à copier-coller dans des membres source as400 :

tables.txtDDS des fichiers physiques et logiques
  
test02 dspf.txtDDS sous-fichier statique
test02_cobolSource cobol pour sous-fichier statique
test02_rpg3.txtIdem en RPG3
test02_rpgfree.txtIdem en RPG Free
  
test03 dspf.txtDDS sous-fichier dynamique
test03_cobolSource cobol pour sous-fichier dynamique
test03_rpg3.txtIdem en RPG3
  
test04_rpg3.txtIdem test02 avec variante en RPG3

Impératifs techniques

Les deux programmes sont construits de manière analogue et présentent peu de différences dans le traitement des sous-fichiers, qu'ils soient statique ou dynamique.

En Statique
Les données sont lues intégralement et chargées dans le sous-fichier. Une demande de positionnement recharge le sous-fichier à partir de la clé demandée. Le sous-fichier est affiché à partir de la première page.

En Dynamique
Les données sont lues page par page et chargées dans le sous-fichier. Une demande de positionnement recharge le sous-fichier à partir de la clé demandée. Une demande de pagination avant charge la suite des données dans le sous-fichier. C'est cette dernière page qui est présentée à l'écran.
Se pose alors la nécessité :
  • d'avoir stocké le n° de rang de la dernière ligne enregistrée dans le sous-fichier, avant de continuer le chargement
  • d'avoir stocké la clé du fichier où le dernier chargement de page s'était arrêté. Le traitement des options des lignes peut avoir nécessité des accès sur le fichier servant au chargement; celui-ci est donc dépositionné.
Les fichiers (base de données et écrans) sont à description externe.
Pour les formats d'écran, le choix a été de séparer les indicateurs des champs de données et d'utiliser pour les touches de fonction les indicateurs spécifiques (*INKx en RPG, le n° de touche à 2 chiffres en Cobol).

Abordons maintenant la spécificité du COBOL par rapport au RPG.

Contrairement au RPG, Cobol nécessite de déclarer des buffers d'entrée et de sortie séparés pour chaque format d'écran. En effet, en FD les buffers se rédéfinissent les uns les autres et sont donc écrasés à chaque accès écran.
En général un format possède des champs en entrée-sortie et des champs en sortie. Ces deux définitions sont donc différentes et génèrent des noms de buffers suffixés par I et O. En revanche, si un format n'a par exemple que des champs de sortie (écran de visualisation), la définition d'un buffer d'entrée vide provoque une erreur de compilation. Il ne faut dans ce cas sélectionner que la description de sortie.
Ensuite, si un format n'a que des champs d'entrée-sortie, les buffers d'entrée et de sortie sont identiques et portent donc en commun le nom du format d'écran, sans suffixe; on se trouve alors devant une ambiguité de nom.
Du fait de l'emploi de buffers séparés, chaque lecture d'écran doit être suivie d'un transfert immédiat des zones d'entrée dans les zones identiques de sortie (instructions MOVE CORRESPONDING).

Enfin, un programme Cobol se termine par STOP RUN, alors qu'un programme appelé (nommé sous-programme) doit se terminer par EXIT PROGRAM pour ne pas arrêter l'ensemble.

Description des routines de traitement

Volontairement, le parallèle a été conservé entre les différents langages afin de pouvoir facilement les comparer. Il est cependant évident que chacun propose des facilités d'écriture propres, qu'il est souhaitable d'utiliser (variables locales en RPG 4, définition de champ en RPG 3 lors de l'utilisation et non en début de programme).

Le traitement général est constitué d'une boucle qui, en fonction d'un code format, lance le traitement correspondant.
Le programme repasse à chaque itération par cette boucle générale. Par exemple, si une erreur est détectée sur le format d'appel, la routine de traitement, informée de l'anomalie, se termine sans avoir modifié le code format. La boucle générale reprend alors la main et relance donc la même routine.
En revanche, si un traitement est validé, celui-ci modifie le code format et rend la main à la boucle générale qui va s'orienter sur le nouveau traitement demandé.

Ensuite, viennent les routines de traitement dont l'explication suit.

INIT
En RPG3, décrit les clés d'accès aux fichiers et les variables du programme et les initialise à 0 ou blanc selon leur nature.
En RPG 4 free, initialise les variables décrites en début de programme, comme en Cobol.

INIS1
Initialise le sous-fichier en l'effaçant puis lance le chargement des données. Le code format prend la valeur S01 qui forcera la boucle principale à traiter le sous-fichier.

TRTS1
Affiche le sous-fichier et interprète la transaction :
  • Touche F3 ou F12 pour abandonner le traitement. Le code format devient alors FIN pour sortir du programme; pour retourner au format d'appel s'il existe, lancer la routine d'initialisation de celui-ci (INIF1).
  • Touche de pagination avant. Remplissage de la page suivante.
  • Demande de positionnement par détection de l'indicateur de modification de champ. Nouvel appel de la routine INIS1.
  • Si aucune touche de fonction n'a été employée, boucle de traitement des options des lignes de sous-fichier
CTLS1 (test04 uniquement)
Lit une ligne de sous-fichier et contrôle la validité de la saisie effectuée sur cette ligne.
En cas d'erreur, la main est rendue à l'affichage du sous-fichier (TRTS1).
Ensuite le traitement dépend du mode dans lequel on travaille :
  • En mode "global", la prochaine transaction est le contrôle de la ligne suivante du sous-fichier (CTLS1). A la fin du sous-fichier (donc aucune erreur détectée à ce point), le contrôle est passé à la routine de validation du sous-fichier (VALS1)
  • En mode "par ligne", la prochaine transaction est la validation de la ligne de sous-fichier (VALS1), sans-relecture.
    A la fin du sous-fichier, la main est rendue à l'affichage du sous-fichier (TRTS1).
VALS1 (test04 uniquement)
Exécute le traitement d'une ligne de sous-fichier, selon le mode choisi :
  • En mode "global", il faut relire le sous-fichier. La prochaine transaction est le traitement de la ligne suivante du sous-fichier (VALS1). A la fin, le contrôle est passé à la routine d'affichage du sous-fichier (TRTS1)
  • En mode "par ligne", la ligne de sous-fichier vient d'être lue par la routine de contrôle. La prochaine transaction est le contrôle de la ligne suivante de sous-fichier (CTLS1).

Viennent ensuite des fonctions utilitaires.

REMPS1
Repositionne le fichier à la dernière clé chargée, et le rang du sous-fichier à la dernière ligne écrite.
Charge ensuite une page de données (en statique, limite=9999 lignes). A la fin du chargement, si le sous-fichier est vide, une ligne avec les champs à blanc est ajoutée. Un indicateur est nécessaire pour masquer tous les champs d'entrée de cette ligne.
Positionne l'affichage du sous-fichier à l'endroit désiré : 1ere page pour un statique, dernière page chargée pour un dynamique.
Stocke les valeurs de rang et de clé actuelles.

En mode statique, les sauvegardes de clé et de rang sont bien sûr inutiles mais permettent de conserver une logique d'écriture unique pour tous les cas.

LECS1
Lit la prochaine ligne modifiée du sous-fichier.

TRTL1
Exploite une ligne de sous-fichier en fonction de l'option sélectionnée. La routine se termine systématiquement par une mise à jour de la ligne.
En cas de succès de traitement de la ligne, le code option est remis à blanc. En cas d'échec, les indicateurs d'erreur sont mis en fonction et le sous-fichier positionné sur la page contenant l'anomalie.

INIC1
Initialise la boucle de contrôle du sous-fichier (indicateur de lecture et format à traiter).

INIS1
Initialise la boucle de validation du sous-fichier (indicateur de lecture et format à traiter).


Les fonctions suivantes sont consacrées au format d'appel du premier programme.

INIF1
Initialise les champs du format d'appel. Le code format devient F01, pour forcer la boucle principale à traiter ce format.

TRTF1
Affiche le format et interprète la transaction :
  • Touche F3 : le code format devient alors FIN pour sortir du programme
  • Touche F4, avec test du champ sur lequel s'est produit le F4, permet de lancer le (sous-)programme de choix.
  • Si aucune touche de fonction n'a été employée, contrôle des données du format (CTLF1). Si erreur, la boucle principale renvoie sur l'affichage du format, sinon le traitement de validation est exécuté, à savoir l'appel de INIS1 pour initialiser le sous-fichier et forcer son traitement.
CTLF1
Contrôle la validité des données du format. En cas d'erreur, les indicateurs attachés aux champs sont activés et l'indicateur général d'erreur est positionné.


Exemples XML parse et datesRetour en haut de page

XML Parse Suiv.

Cette fonction très simple permet d'analyser séquentiellement un fichier XML. Pour des besoins simples, cela peut convenir; sinon il vaut mieux se tourner vers d'autres outils, qui permettent de naviguer dans l'arborescence du document.

L'instruction PARSE balaye séquentiellement le document entier, chargé dans une variable.
A chaque élément trouvé (balise, attribut, valeur d'attribut, contenu de balise), un événement est déclenché, accompagné de la valeur de l'élément en question.
Pour extraire une valeur, cette méthode est suffisante, à part qu'il faut avoir chargé intégralement le document dans une variable. Pour des traitements plus complexes, il faut gérer soi-même les imbrications de balises, sans pouvoir aller directement à l'endroit désiré.

Le programme se contente de recevoir les événements et les valeurs associées. Un traitement réel doit tester la description des événements et déclencher le traitement adapté.
Ci-dessous la liste du programme, suivie de la liste des événements détectés :
       Identification division.
       Program-id. xmlparse.

       Data division.
       Working-storage section.
      ***   Déclarations SQL
           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  ligne pic x(2000).
           EXEC SQL END DECLARE SECTION   END-EXEC.
           EXEC SQL INCLUDE SQLCA         END-EXEC.
      *                                                                *
      ***   Document XML                                               *
       01 xml-document.
           05 pic x(30) value "<?xml version=""1.0"" ".
           05 pic x(30) value " standalone=""yes""?>".
           05 pic x(39) value "<!--This document is just an example-->".
           05 pic x(30) value "<Groupe1 grp=""grp1"">".
           05 pic x(30) value "  <Ligne1 type=""type1"">".
           05 pic x(30) value "  Détail ligne 1".                      .
           05 pic x(30) value "  </Ligne1>xxxxx".
           05 pic x(30) value "  <Ligne1 type=""type2"">".
           05 pic x(30) value "  Détail ligne 2".                      .
           05 pic x(11) value "  </Ligne1>".
           05 pic x(30) value "</Groupe1>".
       01  xml-document-length computational pic 999.

       Procedure division.
       debut.
           XML PARSE xml-document
             PROCESSING PROCEDURE xml-handler
             ON EXCEPTION
               display "XML document error " XML-CODE
             NOT ON EXCEPTION
               display "XML document successfully parsed"
           END-XML

           goback.

       xml-handler.
           move spaces to ligne.
           string
                   'EVENT   = ' xml-event
                   'ELEMENT = ' xml-text
           delimited by size
           into ligne
           end-string.
           exec sql
               insert into dj/xmlparse
                   values(:ligne)
           end-exec.
Liste des événements :
EVENT   = START-OF-DOCUMENT             ELEMENT = <?xml version="1.0"     ...  </Groupe1\>
EVENT   = VERSION-INFORMATION           ELEMENT = 1.0
EVENT   = STANDALONE-DECLARATION        ELEMENT = yes
EVENT   = COMMENT                       ELEMENT = This document is just an example
EVENT   = START-OF-ELEMENT              ELEMENT = Groupe1
EVENT   = ATTRIBUTE-NAME                ELEMENT = grp
EVENT   = ATTRIBUTE-CHARACTERS          ELEMENT = grp1
EVENT   = CONTENT-CHARACTERS            ELEMENT =
EVENT   = START-OF-ELEMENT              ELEMENT = Ligne1
EVENT   = ATTRIBUTE-NAME                ELEMENT = type
EVENT   = ATTRIBUTE-CHARACTERS          ELEMENT = type1
EVENT   = CONTENT-CHARACTERS            ELEMENT =          Détail ligne 1
EVENT   = END-OF-ELEMENT                ELEMENT = Ligne1
EVENT   = CONTENT-CHARACTERS            ELEMENT = xxxxx
EVENT   = START-OF-ELEMENT              ELEMENT = Ligne1
EVENT   = ATTRIBUTE-NAME                ELEMENT = type
EVENT   = ATTRIBUTE-CHARACTERS          ELEMENT = type2
EVENT   = CONTENT-CHARACTERS            ELEMENT =          Détail ligne 2
EVENT   = END-OF-ELEMENT                ELEMENT = Ligne1
EVENT   = END-OF-ELEMENT                ELEMENT = Groupe1
EVENT   = END-OF-DOCUMENT               ELEMENT =
Pour traiter les différents éléments, il suffit de tester le nom de l'événement. A noter que l'élément groupe1 fait l'objet d'une variable de 30 caractères; après l'identification de cet élément, le suivant est donc un contenu composé uniquement d'espaces. Dans un flot normalement constitué, ces blancs n'existeraient pas.

Dès qu'une anomalie est détectée (exemple : balise de fin manquante), un événement d'erreur est déclenché et met fin à l'analyse du fichier.

Fonctions de dates Préc.Suiv.

Ce programme exemple montre l'utilisation des fonctions de date : conversion et calculs.

Les zones date sont définies grâce au mot-clé format, en lieu et place du classique picture.

Un mouvement entre deux zones dates ne nécessite pas de conversion explicite, la conversion s'effectuant grâce au format de chacune des zones.
Un mouvement entre zones de natures différentes nécessite de préciser le format de la zone qui n'est pas une date.
La fonction de conversion est sans effet sur des zones non dates.

Les principaux codes utilisables dans la définition des formats sont définis ci-après. Voir pour plus de détails la documentation de la clause FORMAT.
  • %d = jour (jj)
  • %m = n° de mois (mm)
  • %y = année sur 2 chiffres (aa)
  • %Y = année sur 4 chiffres (aaaa)
  • @Y = année (aaaa)
0001.00                                                                                      
0002.00        IDENTIFICATION DIVISION.                                                      
0003.00       ***                                                                            
0004.00          PROGRAM-ID. cbldates.                                                       
0005.00          AUTHOR. DJ.                                                                 
0006.00          DATE-WRITTEN. 04-02-2013.                                                   
0007.00                                                                                      
0008.00       *-------------------------------------------------------------------------     
0009.00       *                                                                              
0010.00       *-------------------------------------------------------------------------     
0011.00                                                                                      
0012.00        ENVIRONMENT DIVISION.                                                         
0013.00       ***                                                                            
0014.00        CONFIGURATION SECTION.                                                        
0015.00          SOURCE-COMPUTER. IBM-AS400.                                                 
0016.00          OBJECT-COMPUTER. IBM-AS400.                                                 
0017.00                                                                                      
0018.00       *-------------------------------------------------------------------------     
0019.00       *                                                                              
0020.00       *-------------------------------------------------------------------------     
0021.00                                                                                      
0022.00        DATA DIVISION.                                                                
0023.00                                                                                      
0024.00       ***                                                                            
0025.00        WORKING-STORAGE SECTION.                                                      
0026.00                                                                                      
0027.00        01  date1       pic  9(6)              value   120703.                        
0028.00        01  date2       format date "%d.%m.%y".                                       
0029.00        01  date3       format date "%Y-%m-%d".                                       
0030.00                                                                                      
0031.00        01  date11      format date "%Y-%m-%d" value   "2012-07-03".                  
0032.00        01  date12      pic 9(6).                                                     
0033.00        01  date13      pic 9(8).                                                     
0034.00                                                                                      
0035.00        01  date21      format date "%Y-%m-%d" value   "2012-07-03".                  
0036.00        01  date22      format date "%d.%m.%y".                                       
0037.00                                                                                      
0038.00        01  date31      pic  9(6)              value   030712.                        
0039.00        01  date32      pic  9(8).                                                    
0040.00                                                                                      
0041.00        01  date41      format date "%Y-%m-%d" value   "2012-07-03".                  
0042.00        01  date42      format date "%d%m%y"   value   "020812".                      
0043.00        01  date43      pic s9(5).                                                    
0044.00       *-------------------------------------------------------------------------     
0045.00        PROCEDURE DIVISION.                                                           
0046.00       *-------------------------------------------------------------------------     
0047.00                                                                                      
0048.00       * numérique vers dates avec conversion                                         
0049.00            move function convert-date-time (date1 date "%y%m%d")                     
0050.00                 to date2 date3.                                                      
0051.00            display "NUM -> DATE  avec conversion "                                   
0052.00                    date1 " " date2 " " date3.                                        
0053.00       *            120703    03.07.12  2012-07-03                                    
0054.00       *            9(6)      %d.%m.%y  %Y-%m-%d                                      
0055.00                                                                                      
0056.00       * numérique vers dates sans conversion                                         
0057.00            move date1 to date2 date3.                                                
0058.00            display "NUM -> DATE  sans conversion "                                   
0059.00                    date1 " " date2 " " date3.                                        
0060.00       *            120703    12.07.03  0012-07-03                                    
0061.00       *            9(6)      %d.%m.%y  %Y-%m-%d                                      
0062.00                                                                                      
0063.00       * date vers numériques avec conversion                                         
0064.00            move function convert-date-time (date11 date "%y%m%d")                    
0065.00                 to date12.                                                           
0066.00            move function convert-date-time (date11 date "%d%m%Y")                    
0067.00                 to date13.                                                           
0068.00            display "DATE -> NUM  avec conversion "                                   
0069.00                    date11 " " date12 " " date13.                                     
0070.00       *            2012-07-03 120703     03072012                                    
0071.00       *            %Y-%m-%d   9(6)       9(8)                                        
0072.00                                                                                      
0073.00       * date vers numériques sans conversion                                         
0074.00            move date11 to date12 date13.                                             
0075.00            display "DATE -> NUM  sans conversion "                                   
0076.00                    date11 " " date12 " " date13.                                     
0077.00       *            2012-07-03 120703     20120703                                    
0078.00       *            %Y-%m-%d   9(6)       9(8)                                        
0079.00                                                                                      
0080.00       * date vers date avec conversion                                               
0081.00            move function convert-date-time (date21 date )                            
0082.00                 to date22.                                                           
0083.00            display "DATE -> DATE avec conversion "                                   
0084.00                    date21 " " date22.                                                
0085.00       *            2012-07-03 03.07.12                                               
0086.00       *            %Y-%m-%d   %d.%m.%y                                               
0087.00                                                                                      
0088.00       * date vers date sans conversion                                               
0089.00            move date21 to date22.                                                    
0090.00            display "DATE -> DATE sans conversion "                                   
0091.00                    date21 " " date22.                                                
0092.00       *            2012-07-03 03.07.12                                               
0093.00       *            %Y-%m-%d   %d.%m.%y                                               
0094.00                                                                                      
0095.00       * numerique vers numérique avec conversion                                     
0096.00            move function convert-date-time (date31 date "%d%m%y")                    
0097.00                 to date32.                                                           
0098.00            display "NUM  -> NUM  avec conversion "                                   
0099.00                    date31 " " date32.                                                
0100.00       *            030712     00030712                                               
0101.00       *            9(6)       9(8)                                                   
0102.00                                                                                      
0103.00       * numerique vers numérique sans conversion                                     
0104.00            move date31 to date32.                                                    
0105.00            display "NUM  -> NUM  sans conversion "                                   
0106.00                    date31 " " date32.                                                
0107.00       *            030712     00030712                                               
0108.00       *            9(6)       9(8)                                                   
0109.00                                                                                      
0110.00       * calcul de différence TOUJOURS avec compute (pas utilisation directe)         
0111.00            compute date43 =                                                          
0112.00                 function find-duration (date41 date42 days).                         
0113.00            display "DIFFERENCE                   "                                   
0114.00                    date41 " " date42 " " date43.                                     
0115.00       *            2012-07-03 020812     00030                                       
0116.00       *            %Y-%m-%d   %d%m%y     9(5)                                        
0117.00                                                                                      
0118.00            stop run.                                                                 

Inspect Préc.

Petite explication de l'instruction inspect, destinée à réaliser des fonctions de scan de chaines de caractères. Il existe aussi l'api QCLSCAN.
move 0 to ind.                                                         
compute lg = function length ( chaine )                  
INSPECT chaine                                                          
    TALLYING ind                                                          
    for characters before function trim(recherche)                            
Le résultat (ind) de l'instruction est le nombre de caractères qui précèdent la chaine de recherche. Si elle n'est pas trouvée dans la chaine initiale, le résultat = longueur de la chaine (lg).

Une autre syntaxe de Inspect permet de remplacer des caractères par d'autres. Hélas, la chaine de remplacement doit être aussi longue que la chaine remplacée.
La méthode suivante permet d'insérer une sous-chaîne dans un texte à un endroit variable, marqué par un caractère spécial.
Dans la phrase "Le solde de votre compte ($) devra nous être adressé ...", le $ identifie le montant à inscrire.
Il suffit donc de :
  • rechercher le $ dans la phrase (ind=26)
  • concaténer le début de la phrase, la valeur du montant, la fin de la phrase
L'instruction suivante permet de réaliser ceci :
string 
	chaine(1:ind)
	montant_alpha
	chaine(ind + 2: lg - ind - 1 )
	delimited by size
	into resultat
end-string