Tous les articles par Mr COBOL

JCL – Concaténation de données

S’il existe plusieurs ensembles de données du même format, ils peuvent être concaténés et transmis en tant qu’entrée au programme sous un seul nom DD.
Voici un exemple de JCL :

//CONCATEX JOB CLASS=6,NOTIFY=&SYSUID
//*
//STEP10    EXEC PGM=SORT
//SORTIN    DD DSN=FICHIER.INPUT1,DISP=SHR
//          DD DSN=FICHIER.INPUT2,DISP=SHR
//          DD DSN=FICHIER.INPUT3,DISP=SHR
//SORTOUT   DD DSN=FICHIER.OUTPUT,DISP=(,CATLG,DELETE),
//          LRECL=50,RECFM=FB

Dans l’exemple ci-dessus, trois jeux de données sont concaténés et transmis en entrée au programme SORT dans le nom SORTIN DD.
Les fichiers sont fusionnés, triés sur les champs clés spécifiés, puis écrits dans un seul fichier de sortie FICHIER.OUTPUT dans le nom SORTOUT DD.

JCL – Définition des jeux de données

Un nom de dataset/jeu de données spécifie le nom d’un fichier et il est désigné par DSN dans JCL. Le paramètre DSN fait référence au nom du jeu de données physique d’un jeu de données nouvellement créé ou existant.
La valeur DSN peut être composée de sous-noms de 1 à 8 caractères chacun, séparés par des points et d’une longueur totale de 44 caractères (alphanumériques).

Voici la syntaxe :

DSN=&nom | *.stepname.ddname

Les ensembles de données temporaires n’ont besoin d’être stockés que pendant la durée de la tâche et sont supprimés à la fin de la tâche.
Ces ensembles de données sont représentés sous la forme DSN=&nom ou simplement sans DSN spécifié.

Si un jeu de données temporaire créé par un step doit être utilisé dans le step suivant, il est alors référencé sous la forme DSN=*.stepname.ddname.
C’est ce qu’on appelle le référencement arrière.

JCL – Paramètre RESTART

Vous pouvez redémarrer le traitement de l’éther de manière automatisée à l’aide du paramètre RD ou manuellement à l’aide du paramètre RESTART.

Le paramètre RD est codé dans l’instruction JOB ou EXEC et il aide au redémarrage automatisé de JOB/STEP et peut contenir l’une des quatre valeurs : R, RNC, NR ou NC.

  • RD=R autorise les redémarrages automatisés et considère le point de contrôle codé dans le paramètre CHKPT de l’instruction DD.
  • RD=RNC autorise les redémarrages automatisés, mais remplace (ignore) le paramètre CHKPT.
  • RD=NR indique que le travail/l’étape ne peut pas être redémarré automatiquement.
    Mais lorsqu’il est redémarré manuellement à l’aide du paramètre RESTART, le paramètre CHKPT (le cas échéant) sera pris en compte.
  • RD=NC interdit le redémarrage automatisé et le traitement des points de contrôle.

S’il est nécessaire d’effectuer un redémarrage automatique pour des codes de fin anormale uniquement, cela peut être spécifié dans le membre SCHEDxx de la bibliothèque parmlib du système IBM.

Le paramètre RESTART est codé dans l’instruction JOB ou EXEC et il aide au redémarrage manuel du JOB/STEP après l’échec du job.
RESTART peut être accompagné d’un checkid, qui est le point de contrôle écrit dans l’ensemble de données codé dans l’instruction SYSCKEOV DD.
Lorsqu’un ID de contrôle est codé, l’instruction SYSCHK DD doit être codée pour référencer l’ensemble de données de point de contrôle après l’instruction JOBLIB (le cas échéant), sinon après l’instruction JOB.

//CHKEX JOB CLASS=6,NOTIFY=&SYSUID,RESTART=(STP01,chk5)
//*
//SYSCHK    DD DSN=SIMPLE.CHK,DISP=OLD
//STP01     EXEC PGM=MYCOBB
//*SYSCKEOV	DD DSNAME=SIMPLE.CHK,DISP=MOD
//IN1       DD DSN=SIMPLE.IN,DISP=SHR
//OUT1      DD DSN=SIMPLE.OUT,DISP=(,CATLG,CATLG)
//          CHKPT=EOV,LRECL=80,RECFM=FB	

Dans l’exemple ci-dessus, chk5 est le checkid, c’est-à-dire que STP01 est redémarré au point de contrôle5.
Veuillez noter qu’une instruction SYSCHK est ajoutée et que l’instruction SYSCKEOV est commentée dans le programme précédent expliqué dans la section Configuration du point de contrôle.

JCL – Points de contrôle

Vous pouvez définir un jeu de données de point de contrôle dans votre programme JCL à l’aide de SYSCKEOV, qui est une instruction DD.

Un CHKPT est le paramètre codé pour les jeux de données QSAM multi-volumes dans une instruction DD. Lorsqu’un CHKPT est codé comme CHKPT=EOV, un point de contrôle est écrit dans l’ensemble de données spécifié dans l’instruction SYSCKEOV à la fin de chaque volume de l’ensemble de données multivolume d’entrée/sortie.

//CHKEX JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP001    EXEC PGM=MYCOBB
//SYSCKEOV  DD DSNAME=SIMPLE.CHK,DISP=MOD
//IN1       DD DSN=SIMPLE.IN,DISP=SHR
//OUT1      DD DSN=SIMPLE.OUT,DISP=(,CATLG,CATLG)
//          CHKPT=EOV,LRECL=80,RECFM=FB	

Dans l’exemple ci-dessus, un point de contrôle est écrit dans le jeu de données SIMPLE.CHK à la fin de chaque volume du jeu de données de sortie SIMPLE.OUT.

JCL – IF-THEN-ELSE

Une autre approche pour contrôler le traitement des tâches consiste à utiliser des constructions IF-THEN-ELSE.
Cela donne plus de flexibilité et une manière conviviale de traitement conditionnel.

Syntaxe

Voici la syntaxe de base d’une construction JCL IF-THEN-ELSE :

//nom IF condition THEN
liste d'instructions 
//* action à entreprendre lorsque la condition est vraie
//nom ELSE 
liste d'instructions 
//* action à entreprendre lorsque la condition est fausse 
//nom ENDIF

Voici la description des termes utilisés dans la construction IF-THEN-ELSE ci-dessus :

  • nom : Ceci est facultatif et un nom peut avoir de 1 à 8 caractères alphanumériques commençant par l’alphabet, #,$ ou @.
  • Condition : Une condition aura un format : KEYWORD OPERATOR VALUE, où KEYWORDS peut être RC (Return Code), ABENDCC (System or user execution code), ABEND, RUN (étape démarrée en exécution).
    Un OPERATOR peut être un opérateur logique (AND (&), OR (|)) ou un opérateur relationnel (<, <=, >, >=, <>).

Exemple

Voici un exemple simple montrant l’utilisation de IF-THEN-ELSE :

//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//PRC1   PROC
//PST1	   EXEC PGM=SORT
//PST2	   EXEC PGM=IEBGENER
//       PEND
//STP01  EXEC PGM=SORT 
//IF1    IF STP01.RC = 0 THEN
//STP02     EXEC PGM=MYCOBB1,PARM=123
//       ENDIF
//IF2    IF STP01.RUN THEN
//STP03a    EXEC PGM=IEBGENER
//STP03b    EXEC PGM=SORT
//       ENDIF
//IF3    IF STP03b.!ABEND THEN
//STP04     EXEC PGM=MYCOBB1,PARM=456
//       ELSE
//       ENDIF
//IF4    IF (STP01.RC = 0 & STP02.RC <= 4) THEN
//STP05     EXEC PROC=PRC1
//       ENDIF
//IF5    IF STP05.PRC1.PST1.ABEND THEN
//STP06     EXEC PGM=MYABD
//       ELSE
//STP07     EXEC PGM=SORT
//       ENDIF

Essayons d’examiner le programme ci-dessus pour le comprendre un peu plus en détail :

  • Le code retour de STP01 est testé dans IF1. Si c’est 0, alors STP02 est exécuté. Sinon, le traitement passe à l’instruction IF suivante (IF2).
  • Dans IF2, si STP01 a démarré l’exécution, alors STP03a et STP03b sont exécutés.
  • Dans IF3, si STP03b ne ABEND pas, alors STP04 est exécuté. Dans ELSE, il n’y a pas d’instructions. C’est ce qu’on appelle une instruction NULL ELSE.
  • Dans IF4, si STP01.RC = 0 et STP02.RC <=4 sont TRUE, alors STP05 est exécuté.
  • Dans IF5, si l’étape de procédure PST1 dans PROC PRC1 dans l’étape de travail STP05 ABEND, alors STP06 est exécuté. Sinon STP07 est exécuté.
  • Si IF4 est évalué à faux, alors STP05 n’est pas exécuté. Dans ce cas, IF5 n’est pas testé et les étapes STP06, STP07 ne sont pas exécutées.

IF-THEN-ELSE ne sera pas exécuté en cas d’arrêt anormal de la tâche, comme l’annulation de la tâche par l’utilisateur, l’expiration de la durée de la tâche ou un jeu de données référencé en arrière à une étape qui est contournée.

JCL – Paramètre COND

Un paramètre COND peut être codé dans l’instruction JOB ou EXEC du JCL.
Il s’agit d’un test sur le code retour des étapes de travail précédentes.
Si le test est évalué comme vrai, l’exécution de l’étape de travail en cours est ignorée.
Le contournement est simplement une omission de l’étape de travail et non une fin anormale.
Il peut y avoir au plus huit conditions combinées dans un seul test.

Syntaxe

Voici la syntaxe de base d’un paramètre JCL COND :

COND=(rc,opérateur-logique)
or
COND=(rc,opérateur-logique,stepname)
or
COND=EVEN
or 
COND=ONLY

Voici la description des paramètres utilisés :

  • rc : C’est le code de retour
  • opérateur-logique : Cela peut être GT (supérieur à), GE (supérieur ou égal à), EQ (égal à), LT (inférieur à), LE (inférieur ou égal à) ou NE (différent de).
  • stepname : Il s’agit de l’étape/step de job dont le code retour est utilisé dans le test.

Les deux dernières conditions (a) COND=EVEN et (b) COND=ONLY, ont été expliquées ci-dessous.

Le COND peut être codé soit dans l’instruction JOB, soit dans l’instruction EXEC, et dans les deux cas, il se comporte différemment comme expliqué ci-dessous :

COND dans l’instruction JOB

Lorsque COND est codé dans l’instruction JOB, la condition est testée pour chaque step de job.
Lorsque la condition est vraie à un step de job particulier, elle est ignorée avec les steps de job qui la suivent.

Voici un exemple :

//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID,COND=(5,LE)
//*
//STEP10 EXEC PGM=PROG01  
//* STEP10 exécute sans aucun test ne soit effectué.

//STEP20 EXEC PGM=PROG02  
//* STEP20 est contourné si RC de STEP10 est supérieur ou égal à 5. 
//* Supposons que STEP10 se termine par RC4 et que le test est donc faux. 
//* Donc STEP20 s'exécute et disons qu'il se termine par RC16.

//STEP30 EXEC PGM=SORT
//* STEP30 est contourné car 5 <= 16.

COND dans l’instruction EXEC

Lorsque COND est codé dans l’instruction EXEC d’un step de job et trouvé vrai, seule ce step de job est ignoré et l’exécution se poursuit à partir du step suivante.

//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=SORT
//* En supposant que STP01 se termine par RC0.

//STP02 EXEC PGM=MYCOBB,COND=(0,EQ,STP01)
//* Dans STP02, la condition est évaluée à TRUE et l'étape est ignorée.

//STP03 EXEC PGM=IEBGENER,COND=((10,LT,STP01),(10,GT,STP02))
//* Dans STP03, la première condition échoue et donc STP03 s'exécute.
//* Étant donné que STP02 est contourné, la condition (10,GT,STP02) dans
//* STP03 n'est pas testée.

COND=EVEN

Lorsque COND=EVEN est codé, le step en cours est exécuté, même si l’un des step précédents se termine anormalement.
Si une autre condition RC est codée avec COND=EVEN, le step s’exécute si aucune des conditions RC n’est vraie.

//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=SORT
//* En supposant que STP01 se termine par RC0. 

//STP02 EXEC PGM=MYCOBB,COND=(0,EQ,STP01)
//* Dans STP02, la condition est évaluée à TRUE et l'étape est ignorée. 

//STP03 EXEC PGM=IEBGENER,COND=((10,LT,STP01),EVEN)
//* Dans STP03, la condition (10,LT,STP01) est évaluée à vrai, 
//* donc l'étape est ignorée.

COND=ONLY

Lorsque COND=ONLY est codé, le step en cours est exécuté, uniquement lorsque l’un des steps précédentes se termine anormalement.
Si une autre condition RC est codée avec COND=ONLY, le step s’exécute si aucune des conditions RC n’est vraie et si l’un des steps précédents échoue anormalement.

//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=SORT
//* En supposant que STP01 se termine par RC0. 

//STP02 EXEC PGM=MYCOBB,COND=(4,EQ,STP01)
//* Dans STP02, la condition est évaluée à FALSE, l'étape est exécutée
//* et suppose que l'étape se termine anormalement.

//STP03 EXEC PGM=IEBGENER,COND=((0,EQ,STP01),ONLY)
//* Dans STP03, bien que STP02 se termine anormalement, la condition 
//* (0,EQ,STP01) est remplie. Par conséquent, STP03 est contourné.

JCL – Traitement conditionnel

Le système d’entrée de job utilise deux approches pour effectuer un traitement conditionnel dans un JCL.
Lorsqu’un travail se termine, un code de retour est défini en fonction de l’état d’exécution.
Le code de retour peut être un nombre compris entre 0 (exécution réussie) et 4095 (une condition d’erreur différente de zéro).

Les valeurs conventionnelles les plus courantes sont :

  • 0 = Normal – tout va bien
  • 4 = Avertissement – erreurs ou problèmes mineurs.
  • 8 = Erreur – erreurs ou problèmes importants.
  • 12 = Erreur grave – erreurs ou problèmes majeurs, les résultats ne doivent pas être fiables.
  • 16 = Erreur terminale – problèmes très graves, ne pas utiliser les résultats.

L’exécution d’une étape/step de job peut être contrôlée en fonction du code de retour de l’étape ou des étapes précédentes à l’aide du paramètre COND et de la construction IF-THEN-ELSE.

JCL – Procédures

Les procédures JCL sont un ensemble d’instructions à l’intérieur d’un JCL regroupées pour exécuter une fonction particulière.
Généralement, la partie fixe du JCL est codée dans une procédure. La partie variable du Job est codée dans le JCL.

Vous pouvez utiliser une procédure pour réaliser l’exécution parallèle d’un programme à l’aide de plusieurs fichiers d’entrée.
Un JCL peut être créé pour chaque fichier d’entrée, et une seule procédure peut être appelée simultanément en passant le nom du fichier d’entrée comme paramètre symbolique.

Syntaxe

Voici la syntaxe de base d’une définition de procédure JCL :

//*
//Nom-Step EXEC procedure nom 

Le contenu de la procédure est contenu dans le JCL pour une procédure instream.
Le contenu est conservé dans un membre différent de la bibliothèque de base pour une procédure cataloguée.
Ce chapitre va expliquer deux types de procédures disponibles en JCL et enfin nous verrons comment imbriquer différentes procédures.

Procédure Instream

Lorsque la procédure est codée dans le même membre JCL, elle est appelée procédure Instream.
Il doit commencer par une instruction PROC et se terminer par une instruction PEND.

//EXINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//INSTPROC   PROC                
//*DEBUT DE LA PROCEDURE
//PROC1	     EXEC PGM=SORT
//SORTIN     DD DSN=&DSNAME,DISP=SHR
//SORTOUT    DD SYSOUT=*MYINCL
//SYSOUT     DD SYSOUT=*
//SYSIN	     DD DSN=&DATAC LRECL=80
//           PEND               
//*FIN DE LA PROCEDURE
//*
//STEP001    EXEC INSTPROC,DSNME=MYDATA.URMI.INPUT1,
//           DATAC=MYDATA.BASE.LIB1(DATA1)
//*
//STEP002    EXEC INSTPROC,DSNME=MYDATA.URMI.INPUT2
//           DATAC=MYDATA.BASE.LIB1(DATA1)
//*                               

Dans l’exemple ci-dessus, la procédure INSTPROC est appelée dans STEP1 et STEP2 à l’aide de fichiers d’entrée différents.
Les paramètres DSNAME et DATAC peuvent être codés avec des valeurs différentes lors de l’appel de la procédure et ceux-ci sont appelés comme paramètres symboliques.
Les différentes entrées du JCL telles que les noms de fichiers, les cartes de données, les valeurs PARM, etc., sont transmises en tant que paramètres symboliques aux procédures.

Lors du codage des paramètres symboliques, n’utilisez pas KEYWORDS, PARAMETERS ou SUB-PARAMETERS comme noms symboliques.
Exemple : N’utilisez pas TIME=&TIME mais oui, vous pouvez utiliser TIME=&TM et cela est supposé être une bonne façon de coder les symboles.

Les paramètres symboliques définis par l’utilisateur sont appelés symboles JCL.
Il existe certains symboles appelés symboles système, qui sont utilisés pour les exécutions de tâches de connexion.
Le seul symbole système utilisé dans les travaux par lots par les utilisateurs normaux est &SYSUID et il est utilisé dans le paramètre NOTIFY de l’instruction JOB.

Procédure cataloguée

Lorsque la procédure est séparée du JCL et codée dans un magasin de données différent, elle est appelée procédure cataloguée. Une instruction PROC n’est pas obligatoire pour être codée dans une procédure cataloguée. Voici un exemple de JCL où il appelle la procédure CATLPROC :

//JCLINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//STEP EXEC CATLPROC,PROG=CATPRC1,DSNME=MYDATA.URMI.INPUT
//          DATAC=MYDATA.BASE.LIB1(DATA1)

Ici, la procédure CATLPROC est cataloguée dans MYCOBOL.BASE.LIB1. 
PROG, DATAC et DSNAME sont passés comme paramètres symboliques à la procédure CATLPROC.

//CATLPROC PROC PROG=,BASELB=MYCOBOL.BASE.LIB1
//*
//PROC1     EXEC PGM=&PROG
//STEPLIB   DD DSN=&BASELB,DISP=SHR
//IN1       DD DSN=&DSNAME,DISP=SHR
//OUT1      DD SYSOUT=*
//SYSOUT    DD SYSOUT=*
//SYSIN     DD DSN=&DATAC
//*

Au sein de la procédure, les paramètres symboliques PROG et BASELB sont codés.
Veuillez noter que le paramètre PROG dans la procédure est remplacé par la valeur dans le JCL et que PGM prend donc la valeur CATPRC1 lors de l’exécution.

Procédures imbriquées

L’appel d’une procédure à partir d’une procédure est appelé une procédure imbriquée.
Les procédures peuvent être imbriquées jusqu’à 15 niveaux. L’imbrication peut être entièrement instream ou cataloguée.
On ne peut pas coder une procédure instream dans une procédure cataloguée.

//SJCLINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//SETNOM    SET DSNM1=INPUT1,DSNM2=OUTPUT1
//INSTPRC1  PROC               
//* DEBUT DE LA PROCEDURE 1
//STEP001      EXEC PGM=SORT,DISP=SHR
//SORTIN       DD DSN=&DSNM1,DISP=SHR
//SORTOUT      DD DSN=&DSNM2,DISP=(,PASS)
//SYSOUT       DD SYSOUT=*
//SYSIN        DD DSN=&DATAC
//*
//STEP002      EXEC PROC=INSTPRC2,DSNM2=MYDATA.URMI.OUTPUT2
//          PEND               
//* FIN DE LA PROCEDURE 1
//*
//INSTPRC2  PROC               
//* DEBUT DE LA PROCEDURE 2
//STEP001      EXEC PGM=SORT
//SORTIN       DD DSN=*.INSTPRC1.STEP1.SORTOUT
//SORTOUT      DD DSN=&DSNM2,DISP=OLD
//SYSOUT       DD SYSOUT=*
//SYSIN        DD DSN=&DATAC
//          PEND               
//* FIN DE LA PROCEDURE 2
//*
//JSTEP1    EXEC INSTPRC1,DSNM1=MYDATA.URMI.INPUT1,
//          DATAC=MYDATA.BASE.LIB1(DATA1)
//*

Dans l’exemple ci-dessus, le JCL appelle la procédure INSTPRC1 dans JSTEP1 et la procédure INSTPRC2 est appelée dans la procédure INSTPRC1.
Ici, la sortie de INSTPRC1 (SORTOUT) est transmise comme entrée (SORTIN) à INSTPRC2.

Une instruction SET est utilisée pour définir les symboles couramment utilisés dans les étapes ou les procédures de job.
Il initialise les valeurs précédentes dans les noms symboliques.
Il doit être défini avant la première utilisation des noms symboliques dans le JCL.

Jetons un coup d’œil à la description ci-dessous pour en savoir un peu plus sur le programme ci-dessus :

  • Le paramètre SET initialise DSNM1=INPUT1 et DSNM2=OUTPUT1.
  • Lorsque INSTPRC1 est appelé dans JSTEP1 de JCL, DSNM1=MYDATA.URMI.INPUT1 et DSNM2=OUTPUT1., c’est-à-dire que la valeur initialisée dans l’instruction SET est réinitialisée avec la valeur définie dans l’une des étapes/procédures de travail.
  • Lorsque INSTPRC2 est appelé dans STEP2 de INSTPRC1, DSNM1=MYDATA.URMI.INPUT1 et DSNM2=MYDATA.URMI.OUTPUT2.

JCL – Instruction JCLLIB

Une instruction JCLLIB est utilisée pour identifier les bibliothèques privées utilisées dans le job.
Il peut être utilisé à la fois avec les procédures instream et cataloguées.

Syntaxe

Voici la syntaxe de base d’une instruction JCL JCLLIB :

//nom JCLLIB ORDER=(library1, library2....) 

Les bibliothèques spécifiées dans l’instruction JCLLIB seront recherchées dans l’ordre indiqué pour localiser les programmes, les procédures et le membre INCLUDE utilisés dans le job.
Il ne peut y avoir qu’une seule instruction JCLLIB dans un JCL ; spécifié après une instruction JOB et avant les instructions EXEC et INCLUDE, mais il ne peut pas être codé dans un membre INCLUDE.

Exemple

Dans l’exemple suivant, le programme MYPROG3 et le membre INCLUDE MYINCL sont recherchés dans l’ordre de MYPROC.BASE.LIB1, MYPROC.BASE.LIB2, bibliothèque système.

//MYJCL JOB ,,CLASS=6,NOTIFY=&SYSUID
//*
//MYLIB JCLLIB ORDER=(MYPROC.BASE.LIB1,MYPROC.BASE.LIB2)
//*
//STEP1   EXEC PGM=MYPROG3
//INC     INCLUDE MEMBER=MYINCL
//OUTPUT1 DD DSN=MYFILES.SAMPLE.OUTPUT1,DISP=(,CATLG,DELETE),
//           RECFM=FB,LRECL=80
//*                                

JCL – Instruction INCLUDE

Un ensemble d’instructions JCL codées dans un membre d’un PDS peut être inclus dans un JCL à l’aide d’une instruction INCLUDE.
Lorsque le JES interprète le JCL, l’ensemble d’instructions JCL dans le membre INCLUDE remplace l’instruction INCLUDE.

Syntaxe

Voici la syntaxe de base d’une instruction JCL INCLUDE :

//nom INCLUDE MEMBER=nom-membre  

L’objectif principal de l’instruction INCLUDE est la réutilisabilité.
Par exemple, les fichiers communs à utiliser dans de nombreux JCL peuvent être codés en tant qu’instructions DD dans le membre INCLUDE et utilisés dans un JCL.

Les instructions DD factices, les spécifications de carte de données, les instructions PROC, JOB et PROC ne peuvent pas être codées dans un membre INCLUDE.
Une instruction INLCUDE peut être codée dans un membre INCLUDE et une imbrication supplémentaire peut être effectuée jusqu’à 15 niveaux.