lunes, 28 de febrero de 2011

Ejemplo 3: leer de fichero y escribir en fichero.

Para este ejemplo crearemos un programa que lea un fichero de entrada, formatee la información y escriba en un fichero de salida.

JCL:

//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.DE.SALIDA
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG3 EXEC PGM=PRUEBA3
//SYSOUT DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.DE.ENTRADA,DISP=SHR
//SALIDA DD DSN=FICHERO.DE.SALIDA,
// DISP=(NEW, CATLG, DELETE),SPACE=(TRK,(50,10))
/*


En este caso volvemos a utilizar el IDCAMS para borrar el fichero de salida que se genera en el segundo paso. Sigue siendo un programa sin DB2, así que utilizamos el EXEC PGM.
Para definir el fichero de entrada "ENTRADA" indicaremos que es un fichero ya existente y compartida al indicar DISP=SHR.
En la SYSOUT veremos los mensajes de error en caso de que los haya.

Fichero de entrada:
----+----1----+
11111AA100A
22222BB100B
33333CC100K
44444DD100M

campo1: número de cliente
campo2: código de empresa
campo3: saldo


PROGRAMA:

 IDENTIFICATION DIVISION.
 PROGRAM-ID. PRUEBA3.
*==========================================================*
*     PROGRAMA QUE LEE DE FICHERO Y ESCRIBE EN FICHERO
*==========================================================*
*
 ENVIRONMENT DIVISION.
*
 CONFIGURATION SECTION.
*
 SPECIAL-NAMES.
     DECIMAL-POINT IS COMMA.
*
 INPUT-OUTPUT SECTION.
*
 FILE-CONTROL.
*
 SELECT ENTRADA ASSIGN TO ENTRADA
                STATUS IS FS-ENTRADA.
 SELECT SALIDA ASSIGN TO SALIDA
                STATUS IS FS-SALIDA.
*
 DATA DIVISION.
*
 FILE SECTION.
*
* Fichero de entrada de longitud fija (F) igual a 11.
 FD ENTRADA RECORDING MODE IS F
            BLOCK CONTAINS 0 RECORDS
            RECORD CONTAINS 11 CHARACTERS.
 01 REG-ENTRADA PIC X(11).
*
* Fichero de salida de longitud fija (F) igual a 28.
 FD SALIDA RECORDING MODE IS F
           BLOCK CONTAINS 0 RECORDS
           RECORD CONTAINS 28 CHARACTERS.
 01 REG-SALIDA PIC X(28).
*
 WORKING-STORAGE SECTION.
* FILE STATUS
 01 FS-STATUS.
    05 FS-ENTRADA        PIC X(2).
       88 FS-ENTRADA-OK          VALUE '00'.
       88 FS-FICHERO1-EOF          VALUE '10'.
    05 FS-SALIDA         PIC X(2).
       88 FS-SALIDA-OK           VALUE '00'.
*
* VARIABLES
 01 WB-FIN-ENTRADA           PIC X(1) VALUE 'N'.
    88 FIN-ENTRADA                    VALUE 'S'.
*
 01 WB-SIGNOS               PIC X.
    88 SIGNO-MAS                      VALUE 'A', 'B',

                                            'C', 'D',
                                            'E', 'F',
                                            'G', 'H',
                                            'I', '{'.
    88 SIGNO-MENOS                    VALUE 'J', 'K',

                                            'L', 'M',
                                            'N', 'O',
                                            'P', 'Q',
                                            'R', '}'.

 01 WX-TABLA-EMPRESAS.
    05                       PIC X(11) VALUE 'AAEMPRESA 1'.
    05                       PIC X(11) VALUE 'BBEMPRESA 2'.
    05                       PIC X(11) VALUE 'CCEMPRESA 3'.
    05                       PIC X(11) VALUE 'DDEMPRESA 4'.
 01 REDEFINES WX-TABLA-EMPRESAS.
    05 WX-ELEMENTOS OCCURS 4 TIMES
                    INDEXED BY WI-ELEM.
       10 WX-CODIGO-EMPRESA  PIC X(2).
       10 WX-NOMBRE-EMPRESA  PIC X(9).
*
 01 WX-REGISTRO-ENTRADA.
    05 WX-ENT-CLIENTE        PIC 9(5).
    05 WX-ENT-COD-EMPRESA    PIC X(2).
    05 WX-ENT-SALDO          PIC S9(4).
*
 01 WX-REGISTRO-SALIDA.
    05 WX-SAL-CLIENTE        PIC 9(5).
    05 WX-SAL-COD-EMPRESA    PIC X(2).
    05 WX-SAL-NOMBRE-EMPRESA PIC X(9).
    05 WX-SAL-SALDO          PIC 9(4).
    05 WX-SAL-SIGNO          PIC X(8).

* CONSTANTES
 01 WK-POSITIVO              PIC X(8)  VALUE 'positivo'.
 01 WK-NEGATIVO              PIC X(8)  VALUE 'negativo'.
*
************************************************************
 PROCEDURE DIVISION.
************************************************************
*  |     0000 - 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
        UNTIL FIN-ENTRADA
*
     PERFORM 30000-FINAL
     .
************************************************************
*  |     10000 - INICIO
*--|------------+----------><----------+-------------------*
*  | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Primera lectura de SYSIN
************************************************************
 10000-INICIO.
*
     INITIALIZE WX-REGISTRO-SALIDA

     PERFORM 11000-ABRIR-FICHERO

     PERFORM LEER-ENTRADA


     IF FIN-ENTRADA
        DISPLAY 'FICHERO DE ENTRADA VACIO'

        PERFORM 30000-FINAL
     END-IF
     .
*
************************************************************
*               11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos los ficheros del programa
************************************************************
 11000-ABRIR-FICHEROS.
*
     OPEN INPUT ENTRADA
         OUTPUT SALIDA
*
     IF NOT FS-ENTRADA-OK
        DISPLAY 'ERROR EN OPEN DEL FICHERO DE ENTRADA:'FS-ENTRADA
     END-IF

     IF NOT FS-SALIDA-OK
        DISPLAY 'ERROR EN OPEN DEL FICHERO DE SALIDA:'FS-SALIDA
     END-IF
     .
*
************************************************************
*  |     20000 - PROCESO
*--|------------------+----------><------------------------*
*  | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
*  | la ENTRADA
************************************************************
 20000-PROCESO.
*
     PERFORM 21000-BUSCAR-NOMBRE-EMPRESA

     PERFORM 22000-BUSCAR-SIGNO-SALDO

     PERFORM 23000-INFORMAR-SALIDA

     PERFORM ESCRIBIR-SALIDA

     PERFORM LEER-ENTRADA
     .
*
************************************************************
*               21000 - BUSCAR NOMBRE EMPRESA
*--|------------------+----------><----------+-------------*
* BUSCAMOS EL CODIGO DE EMPRESA DEL FICHERO DE ENTRADA EN
* NUESTRA TABLA INTERNA PARA RECUPERAR EL NOMBRE
************************************************************
 21000-BUSCAR-NOMBRE-EMPRESA.
*

*Ponemos el índice WI-ELEM a 1 y se irá incrementando de 1 en 1
     SET WI-ELEM TO 1
*Buscamos en WX-TABLA-EMPRESAS el nombre de empresa que tenga

*el mismo código que el del fichero de entrada.
*Si no lo encuentra, movemos espacios al nombre de la empresa
     SEARCH WX-ELEMENTOS
        AT END
           MOVE SPACES TO WX-SAL-NOMBRE-EMPRESA

        WHEN WX-CODIGO-EMPRESA(WI-ELEM) EQUAL WX-ENT-COD-EMPRESA
          MOVE WX-NOMBRE-EMPRESA(WI-ELEM)  

            TO WX-SAL-NOMBRE-EMPRESA

     END-SEARCH

     .

*
************************************************************
*                22000-BUSCAR-SIGNO-SALDO
*--|------------------+----------><----------+-------------*
* COMPROBAMOS EL SIGNO DEL SALDO E INFORMAMOS EL CAMPO:
* WX-SAL-SIGNO
************************************************************
 22000-BUSCAR-SIGNO-SALDO.
*

*El signo viene dado por la última posición. La movemos al
*switch WB-SIGNOS. Según su valor informará positivo o negativo
     MOVE WX-ENT-SALDO(4:1)       TO WB-SIGNOS

     EVALUATE TRUE
        WHEN SIGNO-MAS
             MOVE WK-POSITIVO TO WX-SAL-SIGNO

        WHEN SIGNO-MENOS
             MOVE WK-NEGATIVO TO WX-SAL-SIGNO


        WHEN OTHER
             MOVE SPACES      TO WX-SAL-SIGNO
     END-EVALUATE
     .

*
************************************************************
*                23000-INFORMAR-SALIDA
*--|------------------+----------><----------+-------------*
* INFORMAMOS EL RESTO DE CAMPOS DEL FICHERO DE SALIDA
************************************************************

 23000-INFORMAR-SALIDA.
*
     MOVE WX-ENT-CLIENTE     TO WX-SAL-CLIENTE
     MOVE WX-ENT-COD-EMPRESA TO WX-SAL-COD-EMPRESA
     MOVE WX-ENT-SALDO       TO WX-SAL-SALDO

     .
*
************************************************************
*                LEER ENTRADA
*--|------------------+----------><----------+-------------*
* Leemos del fichero de entrada
************************************************************
 LEER-ENTRADA.
*
     READ ENTRADA INTO WX-REGISTRO-ENTRADA

     EVALUATE TRUE
        WHEN FS-ENTRADA-OK
             CONTINUE

        WHEN FS-ENTRADA-EOF
             SET FIN-ENTRADA TO TRUE

        WHEN OTHER
             DISPLAY 'ERROR EN READ DE ENTRADA:'FS-ENTRADA
     END-EVALUATE
     .

*
************************************************************
*                - ESCRIBIR SALIDA
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
  ESCRIBIR-SALIDA.
*
     WRITE REG-SALIDA FROM WX-REGISTRO-SALIDA

     IF FS-SALIDA-OK
        INITIALIZE WX-REGISTRO-SALIDA
     ELSE
        DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-SALIDA
     END-IF

     .
*
************************************************************
*  |     30000 - FINAL
*--|------------------+----------><----------+-------------*
*  | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
 30000-FINAL.
*
     PERFORM 31000-CERRAR-FICHEROS

     STOP RUN
     .
*
************************************************************
*  |     31000 - CERRAR FICHEROS
*--|------------------+----------><----------+-------------*
*  | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************
 31000-CERRAR-FICHEROS.
*
     CLOSE ENTRADA

           SALIDA

     IF NOT FS-ENTRADA-OK
        DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
     END-IF



     IF NOT FS-SALIDA-OK
        DISPLAY 'ERROR EN CLOSE DE SALIDA:'FS-SALIDA
     END-IF

     .


En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
  CONFIGURATION SECTION: existirá siempre.
  INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de entrada y uno de salida.
DATA DIVISION: existirá siempre.
  FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de entrada y uno de salida.
  WORKING-STORAGE SECTION: exisitirá siempre.
  En este caso no exisistirá la LINKAGE SECTION pues el programa no se comunica con otros programas.
PROCEDURE DIVISION: exisitirá siempre.


En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro.
SEARCH: esta sententencia se utiliza para buscar un dato dentro de una tabla interna, recorriéndola usándo un índice y comparando alguno de sus campos con el campo que buscamos.
PERFORM UNTIL: bucle
SET:Activa los niveles 88 de un campo tipo "switch".
READ: Lee cada registro del fichero de entrada. En el "INTO" le indicamos donde debe guardar la información.
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.

Descripción del programa:
En el párrafo de inicio, inicializamos el registro de salida: WX-REGISTRO-SALIDA
Abriremos los ficheros del programa (OPEN INPUT para la enrtada, y OUTPUT para la salida) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución).

En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), tenemos varias llamadas a párrafos:

21000-BUSCAR-NOMBRE-EMPRESA:
Busca en la taba interna WX-TABLA-EMPRESAS utilizando la sentencia SEARCH.

22000-BUSCAR-SIGNO-SALDO:
La última posición del campo saldo (S9(4)) nos indica el signo. Podéis ver la referencia a los signos en campos numéricos.
Guardaremos esa última posición en el nivel superior de un campo tipo switch (WB-SIGNOS). Si el valor se corresponde con alguno de los indicados en los niveles 88, ese nivel se activará a "TRUE".
En el "EVALUATE TRUE", el programa entrará por la sentencia "WHEN" que esté activa (que sea "TRUE").

23000-INFORMAR-SALIDA:
Informamos el resto de campos.

ESCRIBIR-SALIDA:
Escribimos nuestro registro ya informado en el fichero de salida.

LEER-ENTRADA:
Leemos el siguiente registro del fichero de entrada.

Fichero de salida:
----+----1----+----2----+----3
11111AAEMPRESA 11001positivo
22222BBEMPRESA 21002positivo
33333CCEMPRESA 31002negativo
44444DDEMPRESA 41004negativo

campo1: número de cliente
campo2: código de empresa
campo3: nombre de empresa
campo4: saldo
campo5: signo del saldo

En este programa además de ver como crear un PROCESO que trate todos los registros de un fichero de entrada, hemos visto varias sentencias como el EVALUATE y el SEARCH. Así aprovechamos para ir introduciendo más sentencias útiles del COBOL en forma de ejemplos.
Y si os queda cualquier duda, estamos aquí para resolverlas : )

sábado, 26 de febrero de 2011

Lo más leído: lunes 21 a viernes 25 de Febrero.

Según el señor Google Analytics, estas son las 10 páginas más consultadas durante esta semana:

  1. Errores de JCL.
  2. Errores DB2.
  3. Programas con DB2 I: SELECT, INSERT, UPDATE y DELETE.
  4. JCL básico:IDCAMS, IEFBR14, IEBGENER.
  5. Programas con DB2 III: COUNT, MAX y FOR UPDATE.
  6. Errores de ficheros.
  7. SQL en JCL.
  8. Sort vol.5: SUM.
  9. Programas con DB2 II: CURSOR.
  10. XML PARSE: de XML a fichero plano con COBOL.

Hay que decir que los artículos de errores son siempre lo más consultado, y si los descontamos, aparecerían en los puestos 8, 9 y 10:
  1. CURSOR II: ROWSET.
  2. SORT vol.1: SORT, INCLUDE.
  3. SORT vol.2: OUTREC.

Y hasta aquí la semana. Volvemos el lunes con más, pero no mejor, porque es imposible. Aquí, en el Consultorio Cobol.
: )

P.D.No dejéis de leer las viñetas de La abuela Manuela!!

viernes, 25 de febrero de 2011

BBVA + HP = CPD de máxima seguridad

El nuevo centro de procesamiento de datos de BBVA en Madrid diseñado por HP, garantizará el correcto funcionamiento de la red de oficinas de BBVA en España. El edificio ha sido diseñado para ofrecer servicio ante cualquier incidente que pueda ocurrir y que afecte a sus sistemas eléctricos y/o mecánicos.

Esta tolerancia ante posibles fallos se consigue con múltiples vías de distribución, que consiguen que las labores de mantenimiento en una de las vías no afecten al resto y asegurar así el funcionamiento continuado de la red de oficinas.

Otro detalle a destacar es la flexibilidad de la infraestructura, ya que está preparada para una posible ampliación futura de 500 metros cuadrados de superficie.

Según estimaciones, este nuevo CPD garantiza un tiempo máximo de interrupción del servicio de 52,5 minutos al año.

El centro es el primero de España que recibe la certificación TIER IV del Uptime Institute. Esta certificación establece y promueve los conocimientos y exigencias necesarios que debe cumplir un CPD para garantizar la disponibilidad y continuidad del mismo. El TIER IV es el máximo nivel definido, y algunas de las características que debe cumplir una infraestructura de estas características son los siguientes :

- Componentes redundantes
- Multiples vías de distribución activas y redundantes
- No susceptible a interrupciones por un evento no planeado
- Se puede realizar un mantenimiento de los componentes hardware durante un evento planeado sin generar interrupciones en el sistema

La disponibilidad de estos centros tendría que ser igual o superior al 99.995% y el tiempo de caidas anuales debería ser inferior a 0.8 hrs.

Habrá que seguir de cerca los resultados de este nuevo CPD, pero es una gran noticia que centros de tales características sean la apuesta de futuro de las entidades financieras, y posiblemente futuros lugares de trabajo de profesionales host (en este caso quizás más enfocado a gente de sistemas).

Noticia Networkworld

miércoles, 23 de febrero de 2011

Natural/ADABAS para coboleros: parte I.

¿Qué es Natural? Es un lenguaje de programación de la empresa Software AG que también se utiliza en entornos mainframe. La base de datos que lo acompaña es ADABAS. Del mismo modo que Natural podría tener similitudes con COBOL, ADABAS no las tiene en absoluto con DB2.
Hoy día no tengo conocimiento de que quede algún banco en España que lo utilice, pues el único que conocí de primera mano lo estaba migrando a COBOL. Creo que telefónica todavía tenía una parte en Natural/ADABAS...

En este artículo nos haremos una idea de cómo codificar en Natural asociándolo a un programa COBOL. Tomaremos como ejemplo el "Ejemplo 1" del manual de COBOL para principiantes.

Vamos a construir un programa que recogerá datos de la SYSIN de un JCL, y displayará la inforamción contenida en él.

JCL:

//**************EJECUCION NATURAL*******************
//**************************************************
//PROG1 EXEC NATBAT
//CMPRINT DD SYSOUT=*
//CMSYNIN DD *
LOGON SYSEXBAT
EXECUTE PGMNAT1
FIN
//SYSIN DD *
JOSE LOPEZ VAZQUEZ %
HUGO CASILLAS DIAZ %
JAVIER CARBONERO %
PACO GONZALEZ
/*


Donde EXEC NATBAT indica que vamos a ejecutar un programa natural en modo batch(puede cambiar según instalación).
CMPRINT DD SYSOUT=* indica que la información "displayada" se quedará en la cola CMPRINT (no lo vamos a guardar en un fichero).
En CMSYNIN DD * hace logon a la librería e indicamos el programa q vamos a ejecutar (PGMNAT1).
En SYSIN incluimos la información que va a recibir el programa. El '%' indica que la información continúa en la siguiente línea.


Programa:

DEFINE DATA LOCAL
 1 WI-INDICE(P4)
 1 #WX-NOMBRE1(A20)
 1 #WX-NOMBRE2(A20)
 1 #WX-NOMBRE3(A20)
 1 #WX-NOMBRE4(A20)
 1 WX-TABLA-NOMBRES
   2 WX-NOMBRE(A20/4)
END-DEFINE

INPUT #WX-NOMBRE1 #WX-NOMBRE2 #WX-NOMBRE3 #WX-NOMBRE4

MOVE #WX-NOMBRE1 TO WX-NOMBRE(1)
MOVE #WX-NOMBRE2 TO WX-NOMBRE(2)
MOVE #WX-NOMBRE3 TO WX-NOMBRE(3)
MOVE #WX-NOMBRE4 TO WX-NOMBRE(4)

FOR WI-INDICE = 1 TO 4
   WRITE 'WX-NOMBRE:'WX-NOMBRE(WI-INDICE)

END


Donde el área "DEFINE DATA LOCAL" equivaldría a nuestra WORKING-STORAGE; ahí definiríamos las variables/copys que necesitásemos para el programa. En nuestro caso:

WI-INDICE: empaquetado(P) de 4 posiciones. Equivaldría a un 9(4) COMP-3. Los índices para bucles en natural deben ser siempre numéricos (N) o empaquetados (P).
#WX-NOMBRE1: alfanumérico (A) de 20. Equivaldría a nuestro PIC X(20). La almohadilla indica que es una variable que se recibe por INPUT.
WX-TABLA-NOMBRES: sería una tabla interna donde el campo WX-NOMBRE es un alfanumérico (A) de 20 y se repite 4 veces. Equivaldría a nuestro OCCURS 4 TIMES.

A continuación codificaríamos el "PROCESO" del programa. En nuestro caso:

INPUT: recoge los datos introducidos por SYSIN.
MOVE/TO: para informar variables existen otras opciones:
WX-NOMBRE(1) := #WX-NOMBRE1 ó
ASSIGN WX-NOMBRE(1) = #WX-NOMBRE1

FOR: es uno de los bucles de natural. Las sentencias se repetirán "PARA" WI-INDICE = 1, =2, =3 e =4.
WRITE: escribe en el CMPRINT, que equivaldría a nuestra SYSOUT. La sentencia WRITE equivaldría a nuestro DISPLAY. En natural también existe la sentencia DISPLAY. La diferencia con el WRITE está en que el DISPLAY da formato a los datos, crea columnas con cabeceras por ejemplo.

El resultado usando WRITE sería:

WX-NOMBRE: JOSE LOPEZ VAZQUEZ
WX-NOMBRE: HUGO CASILLAS DIAZ
WX-NOMBRE: JAVIER CARBONERO
WX-NOMBRE: PACO GONZALEZ


Y usando DISPLAY sería:

      WX-NOMBRE:
--------------------

JOSE LOPEZ VAZQUEZ
HUGO CASILLAS DIAZ
JAVIER CARBONERO
PACO GONZALEZ


Esto es un ejemplo muuuy sencillo, lo he probado con la versión de demo que se puede descargar desde la página de Software AG. Eso sí, sólo el código que los JCLs no se puede.
Para la próxima veremos el ejemplo de acceso a ADABAS más sencillo, y así tener una idea de como funciona esta base de datos : )

lunes, 21 de febrero de 2011

Ejemplo 2: leer de SYSIN y escribir en fichero.

En este ejemplo vamos a ejecutar un programa SIN DB2, que recoge información de la SYSIN del JCL del mismo modo que vimos en el ejemplo 1. La diferencia estará en que en esta ocasión vamos a escribir la información en un fichero en lugar de en la SYSOUT.

JCL:

//******************************************************
//**************************** BORRADO *****************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.CON.NOMBRES
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA2 ***********
//PROG1 EXEC PGM=PRUEBA2
//SYSOUT  DD SYSOUT=*
//FICHERO DD DSN=FICHERO.CON.NOMBRES,
//           DISP=(NEW, CATLG, DELETE),

//           SPACE=(TRK,(50,10))
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ JAVIER CARBONERO PACO GONZALEZ
/*


En este ejemplo añadimos un nuevo paso, el IDCAMS. Este programa sirve para borrar ficheros que se crean a lo largo del JCL. En nuestro caso borrará el fichero que se va a crear en el programa. Así podremos ejecutarlo tantas veces como queramos sin generar el mismo fichero una y otra vez.
El paso de ejecución del programa PRUEBA2 es similar al del ejemplo 1, sólo le hemos añadido la definición del fichero de salida "FICHERO".

PROGRAMA:

 IDENTIFICATION DIVISION.
 PROGRAM-ID. PRUEBA2.
*==========================================================*
*     PROGRAMA QUE LEE DE SYSIN Y ESCRIBE EN FICHERO
*==========================================================*
*
 ENVIRONMENT DIVISION.
*
 CONFIGURATION SECTION.
*
 SPECIAL-NAMES.
     DECIMAL-POINT IS COMMA.
*
 INPUT-OUTPUT SECTION.
*
 FILE-CONTROL.
*
 SELECT FICHERO ASSIGN TO FICHERO
                STATUS IS FS-FICHERO.
*
 DATA DIVISION.
*
 FILE SECTION.
*
* Fichero de salida de longitud fija (F) igual a 20.
 FD FICHERO RECORDING MODE IS F
            BLOCK CONTAINS 0 RECORDS
            RECORD CONTAINS 20 CHARACTERS.
 01 REG-FICHERO PIC X(20).
*
 WORKING-STORAGE SECTION.
* FILE STATUS
 01 FS-STATUS.
    05 FS-FICHERO        PIC X(2).
       88 FS-FICHERO-OK          VALUE '00'.
*
* VARIABLES
 01 WI-INDICE            PIC 9(4) COMP.
 01 WX-SYSIN             PIC X(80).
 01 WX-TABLA-NOMBRES.
    05 WX-NOMBRE         PIC X(20) OCCURS 4 TIMES.
 01 WX-REGISTRO-SALIDA   PIC X(20).
*
************************************************************
 PROCEDURE DIVISION.
************************************************************
*  |     0000 - 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| Primera lectura de SYSIN
************************************************************
 10000-INICIO.
*
     INITIALIZE WX-SYSIN

                WX-REGISTRO-SALIDA
                WX-TABLA-NOMBRES

     ACCEPT WX-SYSIN FROM SYSIN

     PERFORM 11000-ABRIR-FICHERO
     .
*
************************************************************
*               11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos el fichero de salida
************************************************************
 11000-ABRIR-FICHERO.
*
     OPEN OUTPUT FICHERO
*
     IF NOT FS-FICHERO-OK
        DISPLAY 'ERROR EN OPEN DEL FICHERO:'FS-FICHERO
     END-IF
     .
*
************************************************************
*  |     20000 - PROCESO
*--|------------------+----------><------------------------*
*  | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
* la SYSIN
************************************************************
 20000-PROCESO.
*
     MOVE WX-SYSIN TO WX-TABLA-NOMBRES
     MOVE 1        TO WI-INDICE

     PERFORM UNTIL WI-INDICE GREATER 4
        PERFORM 21000-INFORMAR-REGISTRO
        PERFORM 22000-ESCRIBIR-FICHERO

        ADD 1 TO WI-INDICE
     END-PERFORM

     .
*
************************************************************
*               21000 - INFORMAR REGISTRO
*--|------------------+----------><----------+-------------*
* MOVEMOS LA INFORMACION DESDE NUESTRA TABLA INTERNA A LA
* VARIABLE WX-REGISTRO-SALIDA
************************************************************
 21000-INFORMAR-REGISTRO.
*
     MOVE WX-NOMBRE(WI-INDICE) TO WX-REGISTRO-SALIDA

     .
*
************************************************************
*               22000 - ESCRIBIR FICHERO
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
 22000-ESCRIBIR-FICHERO.
*
     WRITE REG-FICHERO FROM WX-REGISTRO-SALIDA

     IF NOT FS-FICHERO-OK
        DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-FICHERO
     END-IF

     .
*
************************************************************
*  |     30000 - FINAL
*--|------------------+----------><----------+-------------*
*  | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
 30000-FINAL.
*
     PERFORM 31000-CERRAR-FICHERO

     STOP RUN
     .
*
************************************************************
*  |     31000 - CERRAR FICHERO
*--|------------------+----------><----------+-------------*
*  | CERRAMOS EL FICHERO DE SALIDA
************************************************************
 31000-CERRAR-FICHERO.
*
     CLOSE FICHERO

     IF NOT FS-FICHERO-OK
        DISPLAY 'ERROR EN CLOSE DEL FICHERO:'FS-FICHERO
     END-IF
     .


En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
  CONFIGURATION SECTION: existirá siempre.
  INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de salida.
DATA DIVISION: existirá siempre.
  FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de salida.
  WORKING-STORAGE SECTION: exisitirá siempre.
  En este caso no exisistirá la LINKAGE SECTION pues el programa no se comunica con 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.
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro
PERFORM UNTIL: bucle
ADD:Operador de adición (suma)
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.

Descripción del programa:
En el párrafo de inicio, inicializamos las variables que utilizaremos a lo largo del programa:
WX-SYSIN: donde guardaremos posteriormente mediante un ACCEPT la información que hemos escrito en la SYSIN del JCL.
WX-TABLA-NOMBRES: donde moveremos la información de la SYSIN para dividirla en los diferentes nombres.
WX-REGISTRO-SALIDA: donde guardaremos la información que vamos a escribir en el fichero de salida.
Abriremos el fichero de salida (OPEN OUTPUT) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.

En el párrafo de proceso, informamos la tabla interna WX-TABLA-NOMBRES, donde el campo WX-NOMBRES se repetirá 4 veces (occurs 4 times). Recordad que el tratamiento de las tablas internas está explicado en detalle en el ejemplo 1.
Montaremos un bucle para escribir cada uno de los nombres guardados en la tabla interna en el fichero de salida.
Informaremos la variable WX-REGISTRO-SALIDA con cada uno de los nombres de la tabla.
Escribiremos el fichero de salida. Para ellos indicaremos a continuación del WRITE la variable definida en la FILE-SECTION, y a continuación del FROM la variable donde tenemos guardada la información que queremos escribir.
Controlaremos el file-status.

Nota: es importante controlar el FILE-STATUS en los accesos a ficheros (OPEN, READ, WRITE, CLOSE...) para, en caso de fallo en la ejecución, tener información sobre la causa del error.

viernes, 18 de febrero de 2011

IBM y Bankinter hasta 2020

IBM ha suscrito un contrato de servicios tecnológicos con Bankinter, por el que la Compañía gestionará, hasta el año 2020, la plataforma tecnológica del Banco, lo que incluye el entorno central (mainframe), junto con 800 servidores.

Este contrato, firmado en el mes de septiembre de 2010, renueva y amplía otro anterior suscrito entre ambas compañías en 2002.

A diferencia del contrato anterior, en el que IBM se encargaba solamente de la gestión del mainframe, ahora se encargará también del entorno distribuido.

Noticia Telcommunity

miércoles, 16 de febrero de 2011

JCL Básico I: ¿Qué es JCL?

¿Qué es JCL?
Vamos con un poco de teoría sobre este lenguaje.
JCL (Job Control Language) - Es un lenguaje de programación que permite codificar las instrucciones necesarias para la ejecución de un proceso batch. Estas instrucciones o sentencias son interpretadas y ejecutadas por el Gestor de trabajos (JES).
La codificación de éste, se realiza con el editor ISPF.
Ésta introducción igual es un poco complicada porque solo hace referencia a la definición de términos generales del lenguaje. En los siguientes volúmenes se entra más al detalle, no os preocupéis si no os quedáis con nada de lo que se dice. No me entero ni yo... :-)


Sentencias JOB / EXEC / DD
  • JOB - Identifica el trabajo a realizar. Es procesada por la parte de lectura del JES(Reader). Aporta un registro contable para el trabajo.
  • EXEC - Identifica el programa o procedimiento que se va a ejecutar. Se procesa por parte del JES en el apartado (Converter) destinado a modificar el JCL por rutinas y sentencias estándar del sistema . Se abre un registro contable que identifica el paso de trabajo.
  • DD - Identifica los recursos que va a utilizar el programa (por ejemplo, ficheros). Como la sentencia anterior la ejecución de la sentencia es procesada en primer lugar por el apartado (Converter) del JES. En algunos casos si ello fuese preciso se acude al Gestor de Recursos del Sistema (SRM)

lunes, 14 de febrero de 2011

Sentencia SORT en un programa COBOL

Hemos visto en otros artículos como hacer un SORT en un JCL. En este artículo veremos como hacerlo en un programa cobol.
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no tiene caso hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores puedan darnos una idea de su uso práctico : D

SORT:

La sentencia SORT en cobol sirve para ordenar registros por un campo clave que le indiquemos. Podremos elegir varias claves y definir si el orden será ascendente o descendente.
Para el ejemplo, hemos creado un fichero temporal en nuestro programa que utilizaremos para generar en él la información ordenada. Como registros a ordenar, utilizaremos una tabla interna, de tal forma que no necesitaremos utilizar ficheros en ningún momento.
La información ordenada se cargará desde el fichero temporal al registro de salida, que podría utilizarse a lo largo de la ejecución.
Nosotros nos pararemos cuando tengamos nuestra información ordenada, y la "displayaremos" mostrándola por SYSOUT.

 IDENTIFICATION DIVISION.
 PROGRAM-ID. PRGSORT.
*
 ENVIRONMENT DIVISION.
 CONFIGURATION SECTION.
 SPECIAL-NAMES.
 DECIMAL-POINT IS COMMA.
*
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
* Fichero temporal donde guardaremos la información ordenada
 SELECT TABLA-SORT ASSIGN TO DISK "SORTWORK".
*
 DATA DIVISION.
 FILE SECTION.
* Definición del fichero temporal
 SD TABLA-SORT
 DATA RECORD IS ELEMENTO-SORT.
 01 ELEMENTO-SORT.
    05 SORT-CLAVE1 PIC X.
    05 SORT-CLAVE2 PIC X(3).
    05 SORT-CAMPO PIC X(10).
    05 SORT-INDICADOR PIC X.
*
 WORKING-STORAGE SECTION.
* Formato del registro de salida
 01 VARIABLES.
    05 WA-REGISTRO.
       10 WA-SORT-CLAVE1 PIC X.
       10 WA-SORT-CLAVE2 PIC X(3).
       10 WA-SORT-CAMPO PIC X(10).
       10 WA-SORT-INDICADOR PIC X.
* Switches que utilizaremos en los bucles
 01 SWITCHES.
    05 SW-FIN-TABLA-SORT PIC X(1).
       88 SI-FIN-TABLA-SORT VALUE 'S'.
       88 NO-FIN-TABLA-SORT VALUE 'N'.
* Tabla interna con los datos a ordenar
 01 TABLA.
    05 WT-TBL-LISTA.
       10 PIC X(15) VALUE 'F216CAMPO02802S'.
       10 PIC X(15) VALUE 'M144CAMPO17114N'.
       10 PIC X(15) VALUE 'Q651CAMPO24536S'.
       10 PIC X(15) VALUE 'F217CAMPO03312N'.
       10 PIC X(15) VALUE 'T487CAMPO44914S'.
       10 PIC X(15) VALUE 'O372CAMPO52113N'.
       10 PIC X(15) VALUE 'F457CAMPO61224N'.
       10 PIC X(15) VALUE 'L547CAMPO73985N'.
       10 PIC X(15) VALUE 'L354CAMPO89173N'.
       10 PIC X(15) VALUE 'W516CAMPO92815N'.
    05 REDEFINES WT-TBL-LISTA.
       10 WT-TBL-ELEMENTO OCCURS 15
                           INDEXED BY WI-ELEM.
          15 WT-TBL-CLAVE1 PIC X.
          15 WT-TBL-CLAVE2 PIC X(3).
          15 WT-TBL-CAMPO PIC X(10).
          15 WT-TBL-INDICADOR PIC X.
* Formato del registro a ordenar
 01 WR-ELEMENTO-SORT.
   05 WR-SORT-CLAVE1 PIC X.
   05 WR-SORT-CLAVE2 PIC X(3).
   05 WR-SORT-CAMPO PIC X(10).
   05 WR-SORT-INDICADOR PIC X.
*
 PROCEDURE DIVISION.
*
    PERFORM 1000-INICIO
    PERFORM 2000-PROCESO
    PERFORM 9000-FINAL
    .
*
 1000-INICIO.
*
    INITIALIZE VARIABLES
    .
*
 2000-PROCESO.
* Proceso de SORT
* Indicamos las claves por las que ordenaremos ON ASCENDING ó 

* ON DESCENDING

    SORT TABLA-SORT
     ON ASCENDING KEY SORT-CLAVE1
     ON DESCENDING KEY SORT-CLAVE2
     INPUT PROCEDURE 2100-PROCESO-ENTRADA
     OUTPUT PROCEDURE 2200-PROCESO-SALIDA
* En la INPUT PROCEDURE cargamos los datos a ordenar
* En la OUTPUT PROCEDURE guardamos la información ordenada 

* en variables del programa

* Controlamos el retorno del SORT con SORT-RETURN
    IF SORT-RETURN NOT = ZEROS
       DISPLAY 'ERROR EN EL SORT:' SORT-RETURN
    END-IF
    .
*
 2100-PROCESO-ENTRADA.
* Cargamos los datos a ordenar(los de la tabla interna) en el

* fichero temporal donde se realizará la ordenación
* (ELEMENTO-SORT) con la sentencia RELEASE.
    PERFORM VARYING WI-ELEM
       FROM 1 BY 1
      UNTIL WT-TBL-CLAVE1(WI-ELEM) = LOW-VALUES
            OR WI-ELEM = 16
*
        MOVE WT-TBL-CLAVE1(WI-ELEM) TO WR-SORT-CLAVE1
        MOVE WT-TBL-CLAVE2(WI-ELEM) TO WR-SORT-CLAVE2
        MOVE WT-TBL-CAMPO(WI-ELEM) TO WR-SORT-CAMPO
        MOVE WT-TBL-INDICADOR(WI-ELEM) TO WR-SORT-INDICADOR
*
        RELEASE ELEMENTO-SORT FROM WR-ELEMENTO-SORT
        DISPLAY 'ELEMENTO-SORT:'WR-ELEMENTO-SORT
    END-PERFORM
    .
*
 2200-PROCESO-SALIDA.
* Recuperamos los datos ordenados del fichero temporal con la

* sentencia RETURN y los displayamos
    SET NO-FIN-TABLA-SORT TO TRUE
*
    PERFORM UNTIL SI-FIN-TABLA-SORT

     RETURN TABLA-SORT INTO WR-ELEMENTO-SORT
     AT END
            SET SI-FIN-TABLA-SORT TO TRUE
     NOT AT END
            MOVE WR-SORT-CLAVE1 TO WA-SORT-CLAVE1
            MOVE WR-SORT-CLAVE2 TO WA-SORT-CLAVE2
            MOVE WR-SORT-CAMPO TO WA-SORT-CAMPO
            MOVE WR-SORT-INDICADOR TO WA-SORT-INDICADOR
*
            DISPLAY 'REGISTRO->' WA-REGISTRO
*
     END-RETURN
    END-PERFORM
    .
*
 9000-FINAL.
*
    STOP RUN
    .
*


RESULTADO:

REGISTRO->F457CAMPO61224N
REGISTRO->F217CAMPO03312N
REGISTRO->F216CAMPO02802S
REGISTRO->L547CAMPO73985N
REGISTRO->L354CAMPO89173N
REGISTRO->M144CAMPO17114N
REGISTRO->O372CAMPO52113N
REGISTRO->Q651CAMPO24536S
REGISTRO->T487CAMPO44914S
REGISTRO->W516CAMPO92815N

viernes, 11 de febrero de 2011

Hasta el infinito y más allá

Bueno, hasta el infinito quizás no (¿o si?), pero según Rod Adkins, vicepresidente del grupo de tecnología y sistemas de IBM, el mainframe seguirá dando mucho que hablar hasta el 2018 como mínimo. Según los planes actuales de la compañía, hasta esa fecha el gigante continuará dando soporte a dichas tecnologías.

¿El motivo? Los clientes lo siguen demandando.

Sorprendentemente para muchos, los ingresos provenientes de estos sistemas han crecido un 70% en el último cuatrimestre de 2010 con respecto al mismo periodo del año anterior.

Desde este humilde consultorio, deseamos que la vida del mainframe no tenga fecha de caducidad en 2018, y como diría nuestro admirado Buz Lightyear, dure "hasta el infinito y más allá" :)

Artículo eWEEK Europe

miércoles, 9 de febrero de 2011

Ejemplo 1: Leer de SYSIN y escribir en SYSOUT.

En cobol hay que diferenciar entre los programas que acceden a DB2 y los que no, pues se compilarán de maneras diferentes y se ejecutarán de forma diferente.
Enpezaremos por ver el programa sin DB2 más sencillo:

El programa más sencillo es aquel que recibe datos por SYSIN del JCL y los muestra por SYSOUT.

JCL:

//PROG1 EXEC PGM=PRUEBA1
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ JAVIER CARBONERO PACO GONZALEZ
/*


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

Fijaos en las posiciones de los nombres de la SYSIN, para entender bien el programa:

----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
JOSE LOPEZ VAZQUEZ  HUGO CASILLAS DIAZ  JAVIER CARBONERO    PACO GONZALEZ


Como veis, la longitud máxima que se puede pasar a un programa a través de la SYSIN es de 80. En nuestro caso la hemos dividido en 4 trozos de 20 posiciones, cada uno con un nombre.

PROGRAMA:

 IDENTIFICATION DIVISION.
 PROGRAM-ID. PRUEBA1.
*==========================================================*
*     PROGRAMA QUE LEE DE SYSIN Y ESCRIBE EN SYSOUT
*==========================================================*
*
 ENVIRONMENT DIVISION.
*
 CONFIGURATION SECTION.
*
 SPECIAL-NAMES.
     DECIMAL-POINT IS COMMA.
*
 DATA DIVISION.
*
 WORKING-STORAGE SECTION.
*
 01 WI-INDICE PIC 9(4) COMP.
 01 WX-SYSIN PIC X(80).
 01 WX-TABLA-NOMBRES.
    05 WX-NOMBRE PIC X(20) OCCURS 4 TIMES.
*
************************************************************
 PROCEDURE DIVISION.
************************************************************
*  |     0000 - 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| Primera lectura de SYSIN
************************************************************
 10000-INICIO.
*
     INITIALIZE WX-SYSIN

     ACCEPT WX-SYSIN FROM SYSIN

     .
************************************************************
*  |     20000 - PROCESO
*--|------------------+----------><------------------------*
*  | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro leido
************************************************************
 20000-PROCESO.
*
     MOVE WX-SYSIN TO WX-TABLA-NOMBRES
     MOVE 1        TO WI-INDICE

     PERFORM UNTIL WI-INDICE GREATER 4
        DISPLAY 'WX-NOMBRE:'WX-NOMBRE(WI-INDICE)

        ADD 1 TO WI-INDICE
     END-PERFORM

     .
************************************************************
*  |     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 se comunica con 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
PERFORM UNTIL: bucle
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
ADD:Operador de adición (suma)
STOP RUN: sentencia de finalización de ejecución.


Descripción del programa:
En el párrafo de inicio, inicializamos WX-SYSIN para guardar posteriormente mediante un ACCEPT la información que hemos escrito en la SYSIN del JCL.

En el párrafo de proceso, informamos la tabla interna WX-TABLA-NOMBRES, donde el campo WX-NOMBRES se repetirá 4 veces (occurs 4 times).

Informamos el campo WI-INDICE con un 1, pues vamos a utilizar los campos de la tabla interna:
Para utilizar un campo que pertenezca a una tabla interna (tiene occurs), debemos acompañar el campo de un "índice" entre paréntesis. De tal forma que indiquemos a que "ocurrencia" de la tabla nos estamos refiriendo. Por ejemplo, WX-NOMBRE(1) sería el primer nombre guardado (JOSE LOPEZ VAZQUEZ).
Como queremos displayar todas las ocurrencias de la tabla, haremos que el índice sea una variable que va aumentando.

A continuación montamos un bucle (perform until) con la condición WI-INDICE mayor (greater) de 4, pues la primera vez WI-INDICE valdrá 1, y necesitamos que el bucle se repita 4 veces:
WI-INDICE = 1: WX-NOMBRE(1) = JOSE LOPEZ VAZQUEZ
WI-INDICE = 2: WX-NOMBRE(2) = HUGO CASILLAS DIAZ
WI-INDICE = 3: WX-NOMBRE(3) = JAVIER CARBONERO
WI-INDICE = 4: WX-NOMBRE(4) = PACO GONZALEZ
WI-INDICE = 5: salimos del bucle porque se cumple WI-INDICE GREATER 4


NOTA: el índice de una tabla interna NUNCA puede ser cero, pues no existe la ocurrencia cero. Si no informásemos WI-INDICE con 1, el DISPLAY de WX-NOMBRE(0) nos daría un estupendo casque, de este estilo:

IGZ0006S The reference to table WX-NOMBRE by verb number 01 on line 001099 addressed an area outside the region of the table.
From compile unit R2BCTAN1 at entry point R2BCTAN1 at compile unit offset +00001122 at entry offset +00001122 at address 1965BBAA.

Aunque ahora ya sabemos cómo encontrar la línea que nos da el OFFSET :D

RESULTADO:
WX-NOMBRE: JOSE LOPEZ VAZQUEZ
WX-NOMBRE: HUGO CASILLAS DIAZ
WX-NOMBRE: JAVIER CARBONERO
WX-NOMBRE: PACO GONZALEZ

lunes, 7 de febrero de 2011

Sentencia MERGE en un programa COBOL

Hemos visto en otros artículos como hacer un MERGE en un JCL. En este artículo veremos como hacerlo en un programa cobol.
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no veo la razón de hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores pueda darnos una idea de su uso práctico : D

MERGE:
La sentencia MERGE en cobol sirve para unir dos ficheros teniendo en cuenta la clave por la que están ordenados. Es decir, no podemos hacer un MERGE de ficheros desordenados.
Lo que hará será "colocar" las claves que coincidan, juntas en el fichero de salida.
Para el ejemplo, utilizaremos:
2 ficheros de entrada con los datos a unir.
1 fichero temporal donde se realizará el MERGE.

Los datos de los ficheros de entrada para nuestro ejemplo serán:

FICHERO1:
A
B
C


FICHERO2:
B
C
E


Programa:
 IDENTIFICATION DIVISION.
 PROGRAM-ID.PRGMERGE.
*
 ENVIRONMENT DIVISION.
 CONFIGURATION SECTION.
 SPECIAL-NAMES.
 DECIMAL-POINT IS COMMA.
*
 INPUT-OUTPUT SECTION. 

 FILE-CONTROL.
*Definición de ficheros
 SELECT TABLA-MERGE ASSIGN TO DISK 'SORTWORK'.
 SELECT TABLA-FICH1 ASSIGN TO FICHERO1.
 SELECT TABLA-FICH2 ASSIGN TO FICHERO2.
*
 DATA DIVISION.
 FILE SECTION.
*Ficheros físicos
 FD TABLA-FICH1
    DATA RECORD IS FICHERO1.
 01 FICHERO1.
    05 FILLER PICTURE X.
 FD TABLA-FICH2
    DATA RECORD IS FICHERO2.
 01 FICHERO2.
    05 FILLER PICTURE X.
* Ficheros temporales
 SD TABLA-MERGE
    DATA RECORD IS ELEMENTO-MERGE.
 01 ELEMENTO-MERGE.
    05 MERGE-CLAVE1 PIC X.
*
 WORKING-STORAGE SECTION.
* Variable donde guardaremos el resultado del MERGE
 01 VARIABLES.
    05 WA-REGISTRO.
       10 WA-AUX-CLAVE1 PIC X.
* Switches para el bucle
 01 SWITCHES.
    05 SW-FIN-TABLA-MERGE PIC X(1).
       88 SI-FIN-TABLA-MERGE VALUE 'S'.
       88 NO-FIN-TABLA-MERGE VALUE 'N'.
* Registro para los datos después del MERGE
 01 WR-ELEMENTO-MERGE.
    05 WR-MERGE-CLAVE1 PIC X.
*
 PROCEDURE DIVISION.
*
     PERFORM 1000-INICIO
     PERFORM 2000-PROCESO
     PERFORM 9000-FINAL
     .
*
 1000-INICIO.
*
     INITIALIZE VARIABLES
     .
*
 2000-PROCESO.
* Sentencia MERGE
* Juntamos FICHERO1 y FICHERO2 en TABLA-MERGE por clave 

* MERGE-CLAVE1

     MERGE TABLA-MERGE ASCENDING KEY MERGE-CLAVE1
     USING FICHERO1 FICHERO2
     OUTPUT PROCEDURE 2100-PROCESO-SALIDA
* En la OUTPUT PROCEDURE usaremos la información ya unida
     IF SORT-RETURN NOT = ZEROS
        DISPLAY 'ERROR EN EL MERGE:' SORT-RETURN
     END-IF
     .
*
 2100-PROCESO-SALIDA.
* Movemos del fichero temporal al registro de salida con la

* sentencia RETURN
* Displayamos la información desde una variable definida 
* en el programa
     SET NO-FIN-TABLA-UNION TO TRUE
*
     PERFORM UNTIL SI-FIN-TABLA-UNION
      RETURN TABLA-MERGE INTO WR-ELEMENTO-MERGE
          AT END
             SET SI-FIN-TABLA-MERGE TO TRUE
         NOT AT END
             MOVE WR-MERGE-CLAVE1 TO WA-AUX-CLAVE1

             DISPLAY 'REGISTRO->' WA-REGISTRO

      END-RETURN
     END-PERFORM
     .
*
 9000-FINAL.
*
     STOP RUN
     .
*


RESULTADO:

REGISTRO->A
REGISTRO->B
REGISTRO->B
REGISTRO->C
REGISTRO->C
REGISTRO->E


Nota: aunque no veais nigún párrafo de "abrir fichero", "leer fichero", etc., no significa que se nos haya ido la olla, es que no es necesario!

viernes, 4 de febrero de 2011

¿Cambiaría su casa de sólidos muros de piedra...?

¿Cambiaría su casa de sólidos muros de piedra por otra construida con materiales ligeros prefabricados? Probablemente no, así indica Miguel Fito, en un artículo publicado en infochannel, que opinan la mayoría de los directores de TI cuando se les plantea la opción de cambiar sus aplicaciones Cobol a otras más modernas.

Según estudios recientes, 75% de los datos generados por negocios son procesados por programas creados en Cobol. Hoy por hoy, la programación en Cobol es uno de los negocios más rentables del mundo de la informática.

El principal problema al que se enfrentan todas las grandes empresas que implementan sus procesos de negocio en Cobol es la desaparición de especialistas debido a la falta de profesionales de nueva generación con conocimientos más profundos en Cobol.

Hay dos principales soluciones ante este problema. Una sería la que plantea Miguel Fito, no muy novedosa, soluciones de migración que permitan un ahorro de costos y mayor flexibilidad. "Casualmente", este artículo del Director de Desarrollo de Negocios de Micro Focus para Iberia y Latinoamérica, coindice con el lanzamiento de Visual Cobol R3.

Otra, planteada desde nuestra humilde opinión como trabajadores del sector, sería que el ámbito de la formación profesional (como bien indica su nombre) y el mundo universitario se acercasen a la realidad profesional que espera a sus alumnos, dotando el contenido de carga lectiva de las principales tecnologías existente en el actual mercado, entre las que ocupa un lugar principal Cobol. Por poner cifras, en un portal como Infojobs, hay 423 ofertas en Cobol y 1280 en Java. ¿No sería lógico que dicha proporción se aplicase al contenido de materias educativas en el ámbito de informática? Pues parece que no... Y mientras tanto, diversas compañías intentan vender soluciones de migración amparadas en la falta de profesionales en la materia.

Además nadie usaría su coche para cruzar el Pacífico ni un barco para atravesar España. (por muchos más conductores de coches que capitanes de barco exista)

Artículo Infochannel

miércoles, 2 de febrero de 2011

OFFSET y S0C7, ¿Cómo localizar el error?

Nos casca un jcl por un S0C7, ¡vaya! a ver como sé yo ahora que línea de las mil que tengo me lo ha dado!. Se acabó el proceso engorroso de buscar qué maldito move me está causando el problema. 

En algunas plataformas existen programas REXX que automáticamente te dicen la línea del programa que te ha provocado el offset. Intentaré crear uno común y lo cuelgo en el blog.

Pero para todos los demás lugares donde no existe esta utilidad, aquí os dejo como hacer para saber la línea:

  1. Provocar el offset. (Por ejemplo, mover un alfanumérico con símbolos raros a un comprimido)
  2. Encontrar el númerito del OFFSET. Desde la cola ST, entramos con ‘?’ en el jcl fallido. Entramos con ‘S’ en el apartado SYSOUT:

  3. Pondrá algo parecido a esto:

    CEE3207S The system detected a data exception (System Completion Code=0C7).    
    From compile unit NOMBRE_PGM at entry point NOMBRE_PGM at compile unit offsset +00001460 at entry offset +00001460 at address 1965BBE0.
  4.  Nos apuntamos el número del offset (1460). El + y los ceros podemos olvidarlos. No os asustéis si el número trae letras ya que se trata de un número hexadecimal.
  5. Necesitamos tener la compilación del programa que nos da el offset. Volvemos a la cola ST y entramos con una S en la compilación. Es importante que sea la misma compilación con la que hemos ejecutado o al menos que la compilación no haya cambiado con respecto a la de la ejecución.
  6. Una vez dentro buscamos la palabra “HEXLOC”. (F HEXLOC). 
  7. Aparecerá una lista parecida a esta:
    LINE Ñ
    HEXLOC VERB       LINE Ñ HEXLOC VERB
    000954 000BEC PERFORM    000958 000C04 PERFORM
    000976 000C40 PERFORM    000978 000C60 INITIALIZE
    000984 000D38 DISPLAY    000986 000D42 PERFORM
    001110 001408 PERFORM    001122 00142A INITIALIZE
    001125 001456 MOVE       001126 001468 MOVE
    001128 001478 MOVE       001129 001482 MOVE
    001144 001498 PERFORM    001146 0014B4 PERFORM
    001150 0014EA STOP       001158 00150C CLOSE
    001163 001554 INITIALIZE 001165 001606 SET
    001168 001616 MOVE       001169 001622 MOVE
    001174 001644 IF         001175 001652 INITIALIZE
  8. Nos fijamos en las columnas HEXLOC(generalmente tendremos 3, hubiera puesto la tercera pero no me cabe), y vemos que tiene unos número hexadecimales. Ahora lo que tenemos que hacer es buscar nuestro offset en estas 3 columnas. Están ordenadas de menor a mayor y de izquierda a derecha, si no lo encontramos como en el caso del ejemplo, hemos de buscar el rango donde se encuentra y quedarnos con el primero. En el ejemplo buscamos el 1460, que está entre el 1456 y el 1478. Nos quedamos siempre con el primero (1456) y miramos el valor de la columna LINE de éste. En este caso es 1125. ¡Esta es la línea del programa que nos está dando el offset!

Os dejo el enlace para descargaros el código REXX para localizar el OFFSET:
Descargar OFFSET