Mostrando entradas con la etiqueta CALL. Mostrar todas las entradas
Mostrando entradas con la etiqueta CALL. Mostrar todas las entradas

jueves, 31 de mayo de 2012

Respuestas de la semana III

Pregunta 1:
llamadas estaticas y dinamicas

Respuesta 1:
Las llamadas a módulos se realizan mediante la sentencia CALL. Estas llamadas pueden realizarse de dos formas:
Estática: lleva el nombre del módulo entre comillas.
CALL 'PROGRAMA' USING XXXXX
Después de la llamada el módulo queda residente en memoria, porque se integra en el programa objeto. Lo que quiere decir que el código de la rutina queda integrado dentro del objeto del programa llamante. Si se modificase la rutina, habría que recompilar todos los programas que la llaman para que integren el nuevo código y no se queden con el viejo.

Dinámica: utiliza una variable que contiene el nombre del programa.
CALL WX-PROGRAMA USING XXXX
donde
01 WX-PROGRAMA PIC X(8) VALUE 'PROGRAMA'.

Después de la llamada el módulo es descargado de la memoria. Son objetos independientes de los programas llamantes.


Pregunta 2
perform thru

Respuesta 2:
En los ejemplos del consultorio siempre utilizamos la sentencia PERFORM para llamar a un párrafo, sin utilizar el THRU. La razón es que en los clientes en los que hemos trabajado es la norma.
Un PERFORM/THRU se codificaría de este modo:
PERFORM 1000-INICIO
   THRU 1000-INICIO-EXIT

Donde
1000-INICIO.
    código cobol

1000-INICIO-EXIT.
    EXIT.


El párrafo 1000-INICIO-EXIT sólo contiene la sentencia EXIT.

Otra opción sería:
PERFORM PARRAFO1
   THRU PARRAFO3


Y ejecutaría todo el código que hubiese entre ambos párrafos, y PARRAFO3 contendría la sentencia EXIT.

Hoy día se podría decir que no se utilizan del mismo modo que no se utiliza el GO TO. Evitas riesgos. Para un cliente grande puede haber cientos de programadores codificando cada día, y seguro que no todos somos unos craks... Así que mejor no arriesgar! Sin olvidar que lo normal es que el párrafo del THRU vaya justo después del párrafo indicado en el PERFORM (como en el ejemplo 1), por lo que su utilidad sería dudosa cuando menos. Además, ahorramos líneas de código y tiempo si pasamos del THRU :P


Pregunta 3:
enviar sysout a archivo

Respuesta 3:
Cuando estamos probando un programa (ya sea batch u online), solemos ir a la cola SYSOUT a ver los DISPLAYs que hayamos puesto (o los mensajes de error, o lo que sea). Esta información a veces es útil guardarla en fichero para no perderla (y no tener que copiar pantalla a pantalla a un txt...).
Para ello:
Entramos con ? en nuestra ejecución (nombre del job para batch, cola del CICS/IMS para online).
Nos posicionamos en la cola SYSOUT que contiene los mensajes y escribimos SE.


Ahora en la línea de comandos escribimos CREATE y un nombre de fichero (el que sea) y copiamos todo el código (C9999).


Elegimos la opción 2 para indicar los atributos del fichero y rellenamos los campos necesarios (podemos abrir otro fichero que ya exista y ver sus atributos escribiendo una S delante del nombre del fichero).
Y listo, tenemos nuestra información guardada en fichero^^


Pregunta 4:
error b37 cobol

Respuesta 4:
El error B37 aparece en el artículo de Errores de sistema y nos indica que un fichero se ha quedado sin espacio.
Esto puede ocurrir porque hemos definido un fichero como TRK y necesitamos un CYL, o incluso un TAPE. Por ejemplo:
SPACE=(TRK,(100,50)) si aumentamos pistas: SPACE=(TRK,(500,50))
SPACE=(CYL,(100,50)) si aumentamos cilindros: SPACE=(CYL,(500,50))
Y sino habremos llegado al UNIT=TAPE^^

Pero si estamos probando algo nuevo, puede ser que haya un error en el programa y estemos escribiendo en el fichero indefinidamente (nos hemos embuclado y estamos llamando al párrafo que hace el WRITE indefinidamente). Por si acaso, antes de aumentar las pistas o los cilindros, revisad el código no vaya a ser!

lunes, 10 de octubre de 2011

Ejemplo 6: programa que llama a otro programa

En este ejemplo vamos a ver un programa que llama a otro programa, denominado rutina, para recuperar información.
Se trata de un programa sin DB2 que recibirá un número de DNI por SYSIN y llamará a una rutina para calcular la letra de dicho NIF. La información recuperada la mostrará por SYSOUT.

JCL:

//PROG6 EXEC PGM=PRUEBA6
//SYSOUT DD SYSOUT=*
//SYSIN DD *
32684930
/*


donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSOUT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSOUT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa


PROGRAMA:

 IDENTIFICATION DIVISION.
 PROGRAM-ID. PRUEBA6.
*==========================================================*
*     PROGRAMA QUE LLAMA A OTRO PROGRAMA (RUTINA)
*==========================================================*
*
 ENVIRONMENT DIVISION.
*
 CONFIGURATION SECTION.
*
 SPECIAL-NAMES.
     DECIMAL-POINT IS COMMA.
*
 DATA DIVISION.
*
 WORKING-STORAGE SECTION.
*

 01 WX-SYSIN.
    05 WX-NUMERO-NIF     PIC X(8).
    05 FILLER            PIC X(72).

 01 WX-RUTINA.
    05 WX-NIF-COMPLETO.
       10 WX-NUMERO-NIF  PIC 9(8).
       10 WX-LETRA-NIF   PIC X.
    05 WX-RETORNO        PIC X(2).
 01 RUTINA1              PIC X(7) VALUE 'RUTINA1'.

*
************************************************************
 PROCEDURE DIVISION.
************************************************************
*  |     00000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA
************************************************************
 00000-PRINCIPAL.
*
     PERFORM 10000-INICIO
*
     PERFORM 20000-PROCESO
*
     PERFORM 30000-FINAL
     .

*
************************************************************
*  |     10000 - INICIO
*--|------------+----------><----------+-------------------*
*  | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Lectura de SYSIN
************************************************************
 10000-INICIO.
*
     INITIALIZE WX-SYSIN

                WX-RUTINA

     ACCEPT WX-SYSIN FROM SYSIN

     .
************************************************************
*  |     20000 - PROCESO
*--|------------------+----------><------------------------*
*  | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el acceso a base de datos
************************************************************
 20000-PROCESO.
*

     MOVE WX-NUMERO-NIF OF WX-SYSIN 
       TO WX-NUMERO-NIF OF WX-RUTINA

     CALL RUTINA1 USING WX-RUTINA  

     IF WX-RETORNO EQUAL 'OK'

        DISPLAY 'LA LLAMADA HA IDO BIEN'
        PERFORM 21000-GRABAR-SALIDA
     ELSE
        DISPLAY 'LA LLAMADA HA IDO MAL'
        PERFORM 30000-FINAL
     END-IF
     .

*************************************************************
*  |     21000 - GRABAR - SALIDA
*--|------------------+----------><----------+--------------*
*  | ESCRIBE EN SYSOUT LA INFORMACIÓN RECUPERADA DE LA TABLA
*************************************************************
  21000-GRABAR-SALIDA.
*

     DISPLAY 'NIF COMPLETO:'WX-NIF-COMPLETO

     .

*
************************************************************
*  |     30000 - FINAL
*--|------------------+----------><----------+-------------*
*  | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
 30000-FINAL.
*
     STOP RUN
     .


En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
  CONFIGURATION SECTION: existirá siempre.
  En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
  En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
  WORKING-STORAGE SECTION: exisitirá siempre.
  En este caso no exisistirá la LINKAGE SECTION pues el programa no es llamado desde otros programas.
PROCEDURE DIVISION: exisitirá siempre.


En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
ACCEPT: esta sentencia recoge la información del campo indicado en el "FROM". En este caso recoge la información almacenada en "SYSIN"; la que nosotros hemos introducido en el JCL.
MOVE/TO: movemos la información de un campo a otro
CALL/USING:es la sentencia que usamos para llamar a una rutina. Después del CALL indicamos el nombre de la rutina que vamos a invocar, y después del USING indicamos las variables de comunicación entre ambos programas.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
IF/ELSE: comprueba si se cumple una condición.
STOP RUN: sentencia de finalización de ejecución.


Descripción del programa:
En el párrafo de inicio, inicializamos las variables que vamos a utilizar a lo largo del programa. Luego mediante un ACCEPT recogemos la información que hemos escrito en la SYSIN de nuestro JOB.

En el párrafo de proceso, informamos el campo WX-NUMERO-NIF del área WX-RUTINA con la información recogida de SYSIN.
Como veis existen dos variables con el mismo nombre. Esto no dará problemas al compilar, mientras las variables pertenezcan a niveles superiores diferentes.
En nuestro caso tenemos un WX-NUMERO-NIF que pertenece a WX-SYSIN, y otro que pertenece a WX-RUTINA. Para utilizar estas variables a lo largo del programa tendremos que indicar a cual de ellas nos referimos, por eso les hemos añadido el "OF WX-XXXXX".

Una vez informada el área de comunicación entre dos programas, procedemos a hacer la llamada en sí con la sentencia CALL/USING.
Se trata de una llamada dinámica, pues el nombre de la rutina está contenido dentro de una variable, así que después de la llamada la rutina será descargada de la memoria.
En las llamadas estáticas el nombre de la rutina se indica entre comillas simpes 'RUTINA1'. En este caso, después de la llamada el módulo queda residente en memoria, porque se integra en el programa objeto.

Para que no haya errores comprobamos que la llamada ha ido bien validando el retorno (informado dentro de la rutina).
Si todo ha ido bien grabamos la información recuperada (NIF con letra) en la SYSOUT mediante un DISPLAY.


RUTINA

 IDENTIFICATION DIVISION.
 PROGRAM-ID. RUTINA1.
*==========================================================*
*     RUTINA QUE CALCULA LA LETRA DE UN NIF
*==========================================================*
*
 ENVIRONMENT DIVISION.
*
 CONFIGURATION SECTION.
*
 SPECIAL-NAMES.
     DECIMAL-POINT IS COMMA.
*
 DATA DIVISION.
*
 WORKING-STORAGE SECTION.
*

 01 WI-INDICES.
    05  WI-IND                            PIC 9(2).
*
 01 WX-VARIABLES.
    05  WX-NIF-DIVID                      PIC 9(8).
    05  WX-NIF-MULTI                      PIC 9(8).
*

 01 WT-TABLAS.
    05 WT-NIF-TABLA                        PIC X(24) 
                              VALUE "TRWAGMYFPDXBNJZSQVHLCKET".
    05 WT-NIF-TABLA-R         REDEFINES    WT-NIF-TABLA.
       10 WT-LETRA-TABLA      OCCURS 24    PIC X.   

*
 LINKAGE SECTION.
*
 01 WX-RUTINA.
    05 WX-NIF-COMPLETO.
       10 WX-NUMERO-NIF  PIC 9(8).
       10 WX-LETRA-NIF   PIC X.
    05 WX-RETORNO        PIC X(2).
*
************************************************************
 PROCEDURE DIVISION USING WX-RUTINA.
************************************************************
*  |     00000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA
************************************************************
 00000-PRINCIPAL.
*
     PERFORM 10000-INICIO
*
     PERFORM 20000-PROCESO
*
     PERFORM 30000-FINAL
     .
************************************************************
*  |     10000 - INICIO
*--|------------+----------><----------+-------------------*
*  | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
************************************************************
 10000-INICIO.
*

     INITIALIZE WX-VARIABLES 
                WI-INDICES
 
     .

*
************************************************************
*  |     20000 - PROCESO
*--|------------------+----------><------------------------*
*  | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el cálculo de la letra del NIF
************************************************************
 20000-PROCESO.
*
     COMPUTE WX-NIF-DIVID = WX-NUMERO-NIF  /  23
     COMPUTE WX-NIF-MULTI = WX-NIF-DIVID  *  23
     COMPUTE WI-IND       = WX-NUMERO-NIF - WX-NIF-MULTI


     ADD 1                       TO WI-IND


     MOVE WT-LETRA-TABLA(WI-IND) TO WX-LETRA-NIF


     MOVE 'OK'                   TO WX-RETORNO
     .

*
************************************************************
*  |     30000 - FINAL
*--|------------------+----------><----------+-------------*
*  | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
 30000-FINAL.
*
     GOBACK
     .



En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
WORKING-STORAGE SECTION: exisitirá siempre.
LINKAGE SECTION: en este caso sí existirá puesto que se trata de una rutina que es llamada por un programa principal.
PROCEDURE DIVISION: exisitirá siempre.


En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
COMPUTE: realiza cálculos numéricos
ADD: operador de adición (suma)
MOVE/TO: movemos la información de un campo a otro
GOBACK: sentencia de finalización de ejecución para rutinas. Devuelve el control al programa llamante.


Descripción del programa:
En la LINKAGE SECTION definimos el área de comunicación con el programa llamante (PRUEBA6), en este caso WX-RUTINA.
En el párrafo de inicio inicializamos las variables que vamos a utilizar a lo largo del programa.

En el párrafo de proceso hacemos los cálculos necesarios para saber qué letra se corresponde al número de NIF que hemos introducido e informamos con un 'OK' el código de retorno.
En caso de que se produzca un error antes de terminar el proceso, el código de retorno irá vacío, y podremos controlar esta diferencia en el programa llamante.

Una vez calculada la letra del NIF devolvemos el control al programa PRUEBA6 haciendo GOBACK.

RESULTADO:

NIF-COMPLETO: 32684930K




Diferencias entre ambos programas:
LINKAGE SECTION: sólo la rutina (programa que es llamado por otro) tiene variables definidas en esta sección.
PROCEDURE DIVISION: sólo la rutina lleva asociada el área de comunicación entre programas en la procedure, añadiéndole la sentencia USING.
30000-FINAL: el programa principal lleva un STOP RUN de finalización de ejecución, mientras que la rutina lleva un GOBACK para devolver el control al programa llamante (programa que ha hecho el CALL).

Si tenéis cualquier duda sobre el uso de rutinas, ya sabéis, preguntad lo que queráis!