Serie ficheros virtuales

 

 

 

 

C Ficheros virtuales

 

 

 

 

 

A.I.7 Extensión. SRRCW como implementación de base de datos virtual

 

 

  A.I.7.1 Introducción. Las nuevas características de SRRCW frente a SRRCM

  A.I.7.2 Generación de un fichero virtual. La función NEW

  A.I.7.3 Arquetipo de las funciones clásicas de SRRCM en SRRCW. La función READ

  A.I.7.4 Funciones específicas por la aparición de lógicos. La función CRTLF

  A.I.7.5 La función DELETE

  A.I.7.6 Las operaciones globales. CPY, DUP, SAVF y RSTF

  A.I.7.7 La emulación de pilas

  A.I.7.8 La emulación de colas

  A.I.7.9 Un ejemplo de descarga de tareas de orden implícito. SRRCS_ACU

  A.I.7.10 Un ejemplo de paso implícito de información

  A.I.7.11 Ampliación de componentes

                                                                                                  _________

 

 

A.I.7.1 Introducción. Las nuevas características de SRRCW frente a SRRCM

 

- Las nuevas características de SRRCW frente a SRRCM

 

SRRCW recoge dos nuevas ideas principales, de un lado permite utilizar nombres para identificar los ficheros virtuales, lo que es más cómodo y natural que las referencias numéricas que usan SRRCM y SRRCN, y de otro permite crear vías de acceso adicionales sobre los mismos datos comunes.

 

Además, SRRCW incluye instrucciones adicionales para la copia y duplicación de bases de datos, así como instrucciones de emulación de pilas y colas.

 

Abusando del lenguaje, a los ficheros virtuales base los llamaré físicos y a las vías de acceso secundarias basadas en ellos los llamaré lógicos, pues es la denominación que se utiliza con los ficheros reales que la aplicación emula.

 

                                                                                                  _________

 

- Notas sobre la implementación

 

Las rutinas de SRRCW son extensiones directas que dotan al motor de base de datos de SRRCM de interfaces útiles y poderosos, con poco desarrollo algorítmico propio pero de una codificación concreta más extensa que la suma de SRRCM y SRRCN juntas.

 

Esta codificación surge de forma natural al implementar los detalles de soporte de nombres y relaciones que constituyen el entramado de la aplicación resultante de base de datos virtual.

 

Para esta implementación, SRRCW usa ficheros virtuales internos (de tipo SRRCM) que soportan los mecanismos de enlace de los nombres y las referencias internas de ficheros virtuales y de la relación entre los ficheros físicos y sus lógicos.

 

Con ello se consigue dotar al producto de capacidades de base de datos, más allá del mero tratamiento de conjuntos de registros.

 

La implementación actual efectúa la emulación generando ficheros en paralelo que replican las operaciones hechas en cualquiera de ellos a cada uno de todos los demás relacionados. Este tipo de respuesta, además de ser algo más fácil de programar que un sistema que comparta datos y sólo replique claves, también tiene un tiempo de respuesta un poco más rápido porque sólo se ejecuta una búsqueda dicotómica en vez de dos, pero por contra ocupa más espacio.

 

En cualquier caso, esta aparente liberalidad en el consumo no tiene una repercusión práctica efectiva, porque para ello se necesitaría acudir a un número significativo de ampliaciones de fichero en una construcción que estuviera basada en una estructura voluminosa y que involucrara varias vías de acceso, cuando lo usual para este tipo de uso en se necesitan lógicos es una implementación aplicada a resolver procesos algorítmicos, en donde prima más la consideración de la rapidez que la del espacio, aspecto que si es de interés en procesos de puro caché, en los que por el contrario no es usual precisar lógicos.

 

Por tanto, normalmente el desarrollo consiste en aplicar la operación solicitada al fichero indicado y duplicarla a los ficheros enlazados. La excepción la constituye el UPDATE.

 

Cuando se detecta que se va aplicar UPDATE a un fichero ligado a otros se ha optado por la opción de sustituir el tratamiento directo de actualización por su alternativa más robusta de emitir una supresión seguida de una escritura de los nuevos datos. Gracias a esta forma de abordar el problema se consigue simplificar la programación, que en su vertiente directa resultaría excesivamente prolija.

 

                                                                                                  _________

 

- SRRCW como interfaz de servicios resultante

 

Este interfaz recoge todos los procedimientos anteriores, tanto de SRRCM como de SRRCN junto con los agregados específicamente en el propio SRRCW, admitiendo sobrecargas para simplificar su uso.

 

Hay que señalar que como la sobrecarga no se permite en C ANSI, en la implementación específica para iSeries hay que emular la sobrecarga creando interfaces de los procedimientos extendidos añadiendo funciones con nombres parecidos que finalmente se utilizarán en un interfaz en otro lenguaje que sí lo permita. Así, para el READ se tiene el prototipo READ “read básico” y el READC “read completo”.

 

En la versión de SRRCW que se presenta aquí se supone un ambiente C++ y por tanto las funciones se introducen con sobrecarga teniéndose, por ejemplo, una sola función READ que se presenta bajo dos prototipos distintos.

 

Un interfaz en ILE RPG lo proporciona SRAGM, programa de servicios del que se presentarán fragmentos relevantes posteriormente en el libro y que permite una sobrecarga parcial al permitir una forma especial de paso de parámetros opcionales, más directa que las listas (...) de ANSI C y que, siguiendo con el ejemplo, consta entonces de una sola función READ con un prototipo que admite el uso básico y el extendido desde los programas de llamada.

 

                                                                                                  _________

 

 

- Un anticipo del resto de epígrafes del capítulo

 

En el resto del capítulo vamos a ver las sentencias principales de los procedimientos de SRRCW que complementan a las presentadas antes bajo SRRCM y SRRCN en el siguiente orden:

 

NEW para crear, READ para leer y como modelo de la extensión de las funciones previas de SRRCM y SRRCN, el CRTLF para generar lógicos y las funciones de soporte de operaciones globales CPY, DUP, SAVF y RSTF.

 

Después veremos las funciones dedicadas al tratamiento de la emulación de pilas y colas.

 

Las demás funciones tienen un carácter auxiliar y una implementación más directa y de fácil seguimiento desde el propio fuente, como por ejemplo las del grupo informativo, y en este momento simplemente presentaremos la lista completa de prototipos externos de SRRCW por grupos funcionales.

 

En el anexo C retomaremos la lista de prototipos de SRRCW con ejemplos de uso, ejemplos que se incluyen en el programa de muestra PruebasW del que ya hemos visto anteriormente algunos fragmentos de codificación de sus botones de prueba.

 

Además, en el apéndice también se presentan ejemplos bajo la versión .net que incorpora una interfaz más detallada y que complementan a los anteriores.

 

También se introducen en los próximos capítulos ejemplos de desarrollo completo basados en la utilización de las funciones de SRRCW que aportan una visión más entretenida de la forma de usarlo que la propia del manual de usuario.

 

                                                                                                  _________

 

 

En el siguiente enlace se presenta la relación completa de prototipos de SRRCW agrupados por grupos funcionales:

 

 

LISTA DE PROTOTIPOS DE SRRCW POR GRUPOS FUNCIONALES

 

 

Tras presentar los prototipos, comencemos ahora a ver los extractos de la codificación principal de las funciones de SRRCW:

 

 

                                                                                                  _________

 

 

 

A.I.7.2 Generación de un fichero virtual. La función NEW

 

La función NEW tiene el siguiente prototipo:

 

//-----------------------------------------------------------------

// FUNCION....: SRRCW_NEW

//

// DESCRIPCION: Genera un identificador interno para el nombre de

// fichero solicitado. Si existe previamente lo limpia

//

//

// PARAMETROS.: Formato completo

//

// (entrada) iDimC: Dimensión claves del nuevo grupo de Ítems

//           lDimD: Dimensión datos del nuevo grupo de Ítems

//        lMaxNreg: Máximo número de ítems a generar

//

//

// PARAMETROS.: Formato compacto

//

// (entrada) iDimC: Dimensión claves del nuevo grupo de Ítems

//           lDimD: Dimensión datos del nuevo grupo de Ítems

//

//

// PARAMETROS.: Formato compacto reducido

//

// (entrada) iDimC: Dimensión claves y datos del nuevo fichero

//

//

// RETORNO....:

//              0: Error de Proceso (Memoria agotada, ...)

//             >0: Nuevo Identificador (NID)

//

//-----------------------------------------------------------------

 

long SRRCW_NEW(const char *cFILE, int iDimC, long lDimD, long lMaxNreg)

 

 

Y su código esencial se resume así:

 

...

 

// Asigna un nuevo identificador de tipo SRRCM

 

lNIDw = SRRCM_NEWC(iDimC, lDimD, lMaxNreg);

 

 

// graba enlace nombre-identificador

 

lresul = SRRCM_WRITE(lNIDf, cFILEw, &lNIDw);

 

return lNIDw;

 

...

                                                                                                   _________

 

 

 

Se genera pues un fichero con SRRCM_NEW de identificador “lNIDw”. Este identificador se guarda en otro fichero virtual de uso interno “lNIDf” que está encargado de archivar las relaciones entre los identificadores numéricos generados y los nombres de usuario.

 

 

El sistema funciona entonces como sigue

 

Al abrir la aplicación se crea el fichero “lNIDf”, pongamos por caso lNIDf = 1, en que las claves serán los nombres asignados por el usuario a los ficheros que se generen, y en donde las claves las compondrán los identificadores de tipo SRRCM correspondientes.

 

Los diversos nombres que se fueran proporcionando se registrarían en el fichero de relaciones de la forma que se indica en la siguiente tabla ejemplo tipo:

 

 

 

 

 

Fichero de relaciones que se graba en el identificador de fichero 1

Nombre dado

Identificador generado

Clave

Datos

 

 

 

 

(Relaciones)

1

-

-

 

 

 

 

EJEMPLO”

2

EJEMPLO

2

ARCHIVO”

3

ARCHIVO

3

TRABAJO”

              4

TRABAJO

   4

. . .

 

. . .

 

NOMBRE”

11

NOMBRE

11

FICHERO”

12

FICHERO

12

TEMPORAL”

            13

TEMPORAL

  13

. . .

 

. . .

 

 

 

                                                                                                  _________

 

 

Entonces cada vez que se vaya a utilizar un fichero, pongamos por caso “NOMBRE”, se accede a esta tabla y se toma su identificador, “11”, que es el que finalmente se utiliza para acceder a los datos grabados con él en la aplicación núcleo SRRCM.

 

                                                                                                  _________

 

 

A.I.7.3 Arquetipo de las funciones clásicas de SRRCM en SRRCW. La función READ

 

Cada una de las funciones de SRRCW homónimas a las de SRRCM consiste básicamente en una llamada a una rutina que recupera el identificador asociado (de tipo SRRCM) a un nombre de usuario (de tipo SRRCW) y a continuación de la emisión de la operación solicitada (traducida a tipo SRRCM) sobre el fichero al que apunte el identificador extraído.

 

Veámoslo con la operación READ, de prototipo:

 

//-----------------------------------------------------------------

// FUNCION....: SRRCW_READ

//

// DESCRIPCION: Recupera un Ítem de Memoria Secuencialmente

// Como los elementos están ordenados por clave, una

// lectura secuencial devuelve los ítems ordenados.

//

//

// PARAMETROS.: Formato completo

//

// (entrada) cFILE : Fichero solicitado

//           iINDIp: Indice secuencial recuperación (IMPORTANTE: formato 1..N)

// (salida ) *vDato: Datos del Ítem

//           *vClav: Clave del Ítem

//

//

// PARAMETROS.: Formato compacto

//

// (entrada) cFILE : Fichero solicitado

//           iINDIp: Indice secuencial recuperación

//                   (IMPORTANTE: formato 1..N)

// (salida ) *vDato: Datos del Ítem

//

//

// RETORNO....: Indicador de error

//              0: Ítem leído satisfactoriamente

//              1: Error de Proceso (No encontrado(EOF), ...)

//

//-----------------------------------------------------------------

 

short int SRRCW_READ(const char *cFILE, long lINDIp, void *vDato, void *vClav)

 

 

Y cuyas sentencias principales se resumen ahora:

 

...

 

// Identificador de tipo SRRCM del fichero solicitado

 

lNIDw = SRRCW_NID(cFILEw);

 

 

// Proceso

 

return SRRCM_READC(lNIDw, lINDIp, vDato, vClav);

 

...

 

 

Este sistema se aplica a un gran conjunto de operaciones de SRRCM y de SRRCN, concretamente, a las lecturas secuenciales, a las lecturas por clave y a los accesos por chain.

 

                                                                                                  _________

 

 

A.I.7.4 Funciones específicas que surgen por la aparición de lógicos. La función CRTLF

 

Las operaciones de actualización y borrado conllevan operaciones adicionales pues deben aplicarse al fichero solicitado y a sus relacionados en caso de pertenecer a una base de datos, esto es, un fichero físico base y su conjunto de lógicos.

 

El prototipo de la nueva función que surge para crear lógicos es:

 

//--------------------------------------------------------------------------------------

//

// Función SRRCW_CRTLF

//

// Descripción: Crea un lógico en base a un fichero físico previo

//

// Importante: La clave del físico debe ocupar las 1ras.posiciones del registro

//

// Parámetros

// (entrada) cLF Nombre del fichero lógico a crear

//           cPF Nombre del fichero físico base

//         iKPOS Posición de comienzo de clave del LF en los datos del registro

//               (IMPORTANTE: Formato 1..N para facilitar uso desde RPG)

//         iKLEN Longitud de la clave del LF que se está creando

//

// Retorno

//         0 Error de proceso

//        >0 NID del fichero en memoria

//

//--------------------------------------------------------------------------------------

long SRRCW_CRTLF(const char *cLF, const char *cPF, int iKPOS, int iKLEN)

 

 

Y las sentencias principales asociadas a la creación de un fichero lógico son

 

. . .

 

// Obtiene el identificador del PF

 

lNIDwf = SRRCW_VERDBR(cPF, cPFb);     // Cuando ya hay dbr asociada

if (!lNIDwf) lNIDwf = SRRCW_NID(cPF); // Cuando es un PF único

 

. . .

 

 

// Genera el identificador del LF a crear

 

lNIDwl = SRRCW_NEW(cLF, iKLEN, lDimD);

 

 

// Genera la vía de acceso asociada, duplicando los datos del físico base al nuevo

// lógico relacionado, en una implementación de tipo replicativo, aplicando la

// definición de posición de comienzo de la clave lógica para poder grabar

// correctamente la clave de acceso del nuevo fichero en la zona marcada

 

for (l=1;;++l)

{

 SRRCM_READ(lNIDwf, l, cDatoW);

 SRRCM_WRITE(lNIDwl, cDatoW + iKPOS - 1, cDatoW);

}

 

 

// Graba enlace físico-lógico

 

memcpy(cDatoW, cPFb, 33);

memcpy(cDatoW+33, cLF, 33);

lresul = SRRCM_WRITE(lNIDpf, cDatoW, cLF);

 

 

// Graba enlace lógico-físico-posición claves

 

memcpy(cDatoW, cPFb, 33);

memcpy(cDatoW+33, &iKPOS, sizeof(int));

lresul = SRRCM_WRITE(lNIDlf, cLF, cDatoW);

 

 

// Devuelve el identificador del lógico construido

 

return lNIDwl;

. . .

 

 

En el CRTLF surgen entonces dos nuevos ficheros virtuales de uso interno de tipo SRRCM, “lNIDpf” que archiva los enlaces de físico-lógicos y “lNIDlf” que guarda los enlaces de lógicos-físicos y la posición de las claves lógicas

 

 

El esquema de funcionamiento de estos ficheros resulta como sigue:

 

Además de los ficheros creados por el usuario tendremos tres ficheros más de uso interno dedicados al archivo de relaciones, cuyos identificadores se guardan en las variables globales del programa SRRCW definidas a continuación

 

static long lNIDf;  // Nid asociado a enlace de nombres y nid’s

static long lNIDpf; // Nid asociado a enlace de físicos y lógicos

static long lNIDlf; // Nid asociado a enlace de lógicos y físicos

 

 

-Por tanto, de un lado tendremos el fichero de relaciones entre nombres e

 identificadores “lNIDf”, cuyos datos se registrarían como en la siguiente tabla

 tipo, que vimos antes al presentar la función NEW y que volvemos a presentar

 ahora en el marco ampliado de relaciones:

 

 

 

 

 

Fichero de relaciones que se graba

Nombre dado

Identificador generado

Clave

Datos

(Relaciones01)

1 [lNIDf]

-

-

(Relaciones02)

2 [lNIDpf]

-

-

(Relaciones03)

3 [lNIDlf]

-

-

EJEMPLO”

2

    EJEMPLO

2

EJEMPLO01”

3

EJEMPLO01

3

EJEMPLO02”

4

EJEMPLO02

4

EJEMPLO03”

5

EJEMPLO03

5

EJEMPLO04”

6

EJEMPLO04

6

EJEMPLO05”

7

EJEMPLO05

7

. . .

 

. . .

 

 

 

Este primer enlace se emplea para acceder a la aplicación núcleo SRRCM

 

 

                                                                                                  _________

 

 

 

-Por otro lado tendremos el fichero de enlace de físico-lógicos “lNIDpf”

 

 

Nº Relativo registro

Clave

Dato

1

EJEMPLO EJEMPLO01

EJEMPLO01

2

EJEMPLO EJEMPLO02

EJEMPLO02

3

EJEMPLO EJEMPLO03

EJEMPLO03

4

EJEMPLO EJEMPLO04

EJEMPLO04

5

EJEMPLO EJEMPLO05

EJEMPLO05

. . .

 

 

 

 

Este segundo fichero de enlaces se usa para duplicar operaciones al conjunto de la base de datos formada por EJEMPLO y sus lógicos EJEMPLO01, 02 ...

 

 

-Y además estará el fichero de enlace de lógicos-físicos-posición claves “lNIDlf”

 

 

 

Nº Relativo registro

Clave

Datos

1

EJEMPLO01

EJEMPLO 5 (Según iKPOS recibido)

2

EJEMPLO02

EJEMPLO 9

3

EJEMPLO03

EJEMPLO 20

4

EJEMPLO04

EJEMPLO 24

5

EJEMPLO05

EJEMPLO 25

. . .

 

 

 

 

Este tercer tipo de enlace complementa al anterior en las operaciones de detalle de la replicación.

 

 

Podemos apreciar la conjunción de estas relaciones en la función DELETE que se expone a continuación

 

 

                                                                                                  _________

 

 

A.I.7.5 La función DELETE

 

A partir de los ficheros de relaciones entre físicos y lógicos, se pueden codificar las operaciones de actualización para una base de datos constituida por un fichero físico y sus lógicos relacionados.

 

Veámoslo en detalle con DELETE, de prototipo:

 

//-------------------------------------------------------------------------------------

// Función SRRCW_DELETE

//

// Descripción Elimina un ítem de memoria

//

// Parámetros

// (entrada) cFILE Nombre del fichero asociado

//           vClav Clave del ítem

//           lLong Longitud de acceso parcial, para borrado por clave

//                 parcial igual. Este parámetro es opcional

//

// Retorno: Posición índice del registro suprimido

//          0 Error de proceso (Clave inexistente ...)

//         >0 Posición en memoria del ítem suprimido

//

//-------------------------------------------------------------------------------------

long SRRCW_DELETE(const char *cFILE, const void *vClav)

 

 

Cuya codificación se extracta a continuación

 

. . .

 

// Recupera el identificador correspondiente al fichero solicitado

 

lNIDw = SRRCW_NID(cFILE);

 

 

// Proceso:

//

// Recupera datos, elimina registro base y extiende la operación con DUPLIDELETE a toda la dbr asociada

 

 

// Recupera los datos del registro solicitado para borrar

 

lResul = SRRCM_CHAIN(lNIDw, vClav, cDatoW);

 

 

// Borra el registro del fichero indicado y lo aplica a la dbr asociada

 

lResul = SRRCM_DELETE(lNIDw, vClav);

if (lResul) er = SRRCW_DUPLIDELETE(cFILEw, vClav, cDatoW);

 

return lResul;

. . .

                                                                                                  _________

 

 

La replicación de la operación en DUPLIDELETE al conjunto de ficheros relacionados se codifica en la función interna auxiliar de prototipo:

 

//-------------------------------------------------------------------------------------

// Función SRRCW_DUPLIDELETE

//

// Descripción Duplica un DELETE a la dbr asociada

//

// Parámetros

//

// (entrada) cFILE: Nombre de fichero solicitado

//           vClav: Clave del ítem

//          cDatDB: Datos que se suprimen, que incluyen por construcción todas las

//                  claves de la dbr

//

// Retorno 0 Proceso satisfactorio

//        >0 Error de proceso

//-------------------------------------------------------------------------------------

short int SRRCW_DUPLIDELETE(const char *cFILE, const void *vClav, const char *cDatDB)

 

 

Donde en esencia se aplicará la operación de borrado al registro correspondiente del fichero físico base y a continuación al resto de los ficheros relacionados, eludiendo el fichero de partida que ya se trató en la rutina de llamada.

 

Sigue un extracto del código

 

. . .

 

// Evalúa si cFILE es un fichero con dbr asociada

 

lNIDw = SRRCW_VERDBR(cFILE, cPFw); // Además extrae el nombre del físico base asociado

if (!lNIDw) return NO_ERRO;

 

 

// Duplica operación al fichero físico base (eludiendo el propio fichero ya tratado)

 

iComp = memcmp(cPFw, cFILE, 33*sizeof(char));

if (iComp) lresul = SRRCM_DELETE(lNIDw, cDatDB);

 

 

// Ciclo de duplicación de la operación solicitada a lógicos dependientes

 

lresul = SRRCN_SETLL(lNIDpf,cPFw,33); //Sitúa para leer el relactor de físicos-lógicos

 

for(;;)

{

 lresul = SRRCN_READE(lNIDpf, cPFw, 33, cDatoW, cClavW); // Lee la relación

 if (!lresul) break;

 

 

 // Extrae el siguiente nombre de lógico y su identificación (Tipo SRRCM)

 

 memcpy(cLFw, cDatoW, 33*sizeof(char));

 lNIDw = SRRCW_NID(cLFw);

 

 

 // Elude el propio fichero ya tratado

 

 iComp = memcmp(cFILE, cLFw, 33*sizeof(char));

 if (!iComp) continue;

 

 

 // Extrae la posición de clave del lógico en los datos del físico

 

 lresul = SRRCM_CHAIN(lNIDlf, cLFw, cDatoW);

 memcpy(&iKPOS, cDatoW+33, sizeof(int));

 

 

 // Duplica la operación

 

 lresul = SRRCM_DELETE(lNIDw, cDatDB + iKPOS - 1);

}

 

. . .

 

 

A diferencia de lo que veremos en los ejemplos, en estos bucles de lectura no se utiliza SRRCAL_Lexi, pues lo que interesa aquí es que se recorra el propio bucle al completo, siendo indiferente para el resultado el orden real en se extraen los datos.

 

                                                                                                  _________

 

DUPLIDELETE, como el resto de mecanismos replicadores, utiliza una función auxiliar para determinar si un fichero está asociado a una base de datos, VERDBR, de prototipo

//-------------------------------------------------------------------------------------

// Función SRRCW_VERDBR

//

// Descripción Evalúa si un nombre de fichero tiene dbr asociada

//

// Parámetros

//

// (entrada) cFILE: Nombre de fichero solicitado

// (salida)   cPF : Nombre del PF base asociado

//

// Retorno 0 No pertenece

//        >0 Nid del PF asociado

//-------------------------------------------------------------------------------------

long SRRCW_VERDBR(const char *cFILE, char *cPF)

 

 

Rutina que va examinando los ficheros de relaciones.

 

Primero examina el fichero que relaciona los físicos y sus lógicos, y a continuación recorre la relación recíproca de lógicos-físicos.

 

Exponemos su código principal a continuación:

 

. . .

 

// Evalúa si cFILE es un físico con dbr asociada, examinando el fichero de

// relaciones entre físicos y lógicos

 

lresul = SRRCN_SETLL(lNIDpf, cFILE, 33);

lresul = SRRCN_READE(lNIDpf, cFILE, 33, cDatoW, cClavW);

if (lresul)

{

 memcpy(cPF, cClavW, 33*sizeof(char));

 lNIDw = SRRCW_NID(cPF);

 return lNIDw;

}

 

 

// Evalúa si cFILE es un lógico de una cierta dbr, examinando el fichero de

// relaciones entre lógicos y físicos

 

lresul = SRRCM_CHAIN(lNIDlf, cFILE, cPF);

if (!lresul) return lresul;

 

lNIDw = SRRCW_NID(cPF);

return lNIDw;

 

. . .

                                                                                                  _________

 

 

Con los mecanismos vistos en DELETE se cubre el repertorio de operaciones individuales, pues el funcionamiento es similar para las demás. Pasamos entonces a examinar las operaciones globales.

 

                                                                                                  _________

 

 

A.I.7.6 Las operaciones globales. CPY, DUP, SAVF y RSTF

 

Se exponen a continuación las operaciones específicas de carácter global añadidas en SRRCW: La copia de ficheros, la duplicación de bases de datos y el salvado y restauración de datos desde fichero real de respaldo en disco.

 

El prototipo de la función de duplicado de una base de datos, que replica un fichero y sus lógicos en otra base de datos es

 

//-------------------------------------------------------------------------------

//

// Función: SRRCW_DUP

//

// Descripción: Duplica una base de datos virtual

//

// Parámetros:

// (entrada) cORG: Nombre del fichero (“físico” principal) origen

//           cDES: Nombre del fichero (“físico” principal) destino

//

// Retorno: Identificador principal de la nueva base de datos

//         0: Error de proceso

//        >0: NID de cDES

//

//--------------------------------------------------------------------------------------

 

long SRRCW_DUP(const char *cORG, const char *cDES)

 

 

La codificación central de la función de duplicación es

 

...

 

 

// Recupera diseño origen

 

er = SRRCM_INF(lNIDw, &iDimC, &lDimD, &lNÍTEM, &lBAJAS, &lNIDD);

 

 

// Crea el nuevo fichero

 

lNID_DES = SRRCW_NEW(cDESw, iDimC, lDimD);

 

 

// Copia datos desde el fichero origen

 

lNCPY = SRRCW_CP(cORG, cDES, 0);

 

 

// Extiende la duplicación a los lógicos dependientes

//

// Manteniendo la relación NOMBREORIGEN como NOMBREDESTINO

//                         NOMBREORIGEN01    NOMBREDESTINO01

//                         NOMBREORIGEN02    NOMBREDESTINO02

//                         NOMBREORIGEN03    NOMBREDESTINO03

// . . .

 

er = SRRCW_DUPLIDUP(cORGw, cDESw);

 

...

 

La codificación de la copia es similar, y la retomaremos más adelante de una forma específica en el epígrafe “A.I.7.11 Ampliación de componentes de SRRCW” al abordar su ampliación y en donde veremos también la rutina común SRRCW_CP

 

                                                                                                  _________

 

 

En cuanto a las operaciones de salvado y restauración, las veremos en los ejemplos dando soporte a las pantallas de consulta de resultados históricos.

 

Estos resultados se guardan en el formato binario estándar de C, por ello su cambio directo no resulta aconsejable y para facilitar minimamente su protección se guardan con el nombre suministrado, pero extendiéndolo con blancos a la derecha hasta completar la longitud máxima para nombre de fichero que utiliza SRRCW [Longitud 33 = Cadenas de 32 posiciones + Terminador], con este mecanismo importar por error un fichero generado por otros medios resulta imposible sin manipulación expresa.

 

 

Presentamos ahora el prototipo de la función de salvado a disco:

 

//--------------------------------------------------------------------------------------

//

// Función: SRRCW_SAVF

//

// Descripción: Salva registros a soporte físico cFILE.DAT

//

//

// PARAMETROS.: Formato completo

// (entrada) cFILE: Nombre del fichero virtual origen a salvar como cFILE.DAT

//            iDES: 0/1 Salvar desglosando clave y datos. 1 Es la opción por defecto

//                  (0<=> Salva de forma compacta clave y datos solapados)

//

// PARAMETROS.: Formato compacto

// (entrada) cFILE: Nombre del fichero virtual origen a salvar como cFILE.DAT

//

// Retorno:

//          >=0: Contador de registros copiados

//           <0: Error de proceso

//

//--------------------------------------------------------------------------------------

long SRRCW_SAVF(const char *cFILE, [short int iDES])

 

 

Y su codificación central, que resulta de interés porque es el único punto donde esta aplicación sobre ficheros virtuales toma contacto con los ficheros físicos, es:

 

...

 

FILE * pFILE_DAT; // Manejador de archivo cFILE.DAT

 

 

// Recupera estadísticos del fichero virtual

 

er = SRRCM_INF(lNIDw, &iDimC, &lDimD, &lNÍTEM, &lBAJAS, &lNIDD);

lNTOT = lNÍTEM - lBAJAS;

 

 

// Abre archivo de salida para grabar

 

if ((pFILE_DAT = fopen(cFILEd, "wb")) == NULL) return -1;

 

 

// Bucle de proceso

 

if (lNTOT)

{

 for (l=1;l<=lNTOT;++l)

 {

 

  // Lectura del fichero a salvar

 

  er = SRRCM_READC(lNIDw, l, cDatoW, cClavW);

  if (er) break;

 

 

  // Salva clave y datos

 

  fwrite(cClavW, iDimC, 1, pFILE_DAT);

  fwrite(cDatoW, lDimD, 1, pFILE_DAT);

 

  ++lNCPY;

 }

}

 

 

// Cierra archivo grabado

 

fclose(pFILE_DAT);

 

 

// Sale nº registros copiados

 

return lNCPY;

 

. . .

                                                                                                  _________

 

 

En cuanto al prototipo de su recíproco de restauración es:

 

//--------------------------------------------------------------------------------------

//

// Función: SRRCW_RSTF

//

// Descripción: Restaura el salva de registros desde soporte físico cFILE.DAT

//

// PARAMETROS.: Formato completo

// (entrada) cFILE: Nombre del fichero virtual destino de la restauración

//            iDES: 0/1 Restaurar desde salva desglosado en clave y datos. 1 = *Dft

//                  0<=> Restaura desde forma compacta con clave y datos solapados

//

// PARAMETROS.: Formato compacto

// (entrada) cFILE: Nombre del fichero virtual destino de la restauración

//

// Retorno:

//          >=0: Contador de registros copiados

//           <0: Error de proceso

//

//-------------------------------------------------------------------------------------

 

long SRRCW_RSTF(const char *cFILE, [short int iDES])

 

 

y su codificación puede extractarse como sigue:

 

...

 

// Borra destino

 

er = SRRCW_CLRF(cFILE);

 

 

// Recupera estadísticos del fichero virtual

 

er = SRRCW_INF(cFILE, &iDimC, &lDimD, &lNÍTEM, &lBAJAS, &lNIDD);

if (er) return -1;

 

 

// Abre archivo de entrada

 

if ((pFILE_DAT = fopen(cFILEd, "rb")) == NULL) return -1;

 

 

// Bucle de proceso

 

for(;;)

{

 

 // Recupera clave y datos

 fread(cClavW, iDimC, 1, pFILE_DAT);

 fread(cDatoW, lDimD, 1, pFILE_DAT);

 

 if (feof(pFILE_DAT)) break;

 

 

 // Graba en fichero virtual que se restaura

 

 lresul = SRRCW_WRITE(cFILE, cClavW, cDatoW);

 

 ++lNCPY;

}

 

 

// Cierra archivo leído

 

fclose(pFILE_DAT);

 

 

// Sale nº de registros grabados

 

return lNCPY;

 

. . .

                                                                                                  _________

 

 

En la versión iSeries, en vez de utilizar este recurso de C, el interfaz en RPG asociado (SRAGM) utiliza una codificación de salva/restauración a un fichero DB2 que puede consultarse en el enlace http://iseries.ficherosvirtuales.com/Documentos/Persistencia_de_datos_RSTF_SAVF.html

 

                                                                                                  _________

 

A.I.7.7 La emulación de pilas

 

SRRCW también incorpora funciones de emulación de pilas y colas.

Aunque la teoría no aconseja para grandes volúmenes utilizar la implementación de inserción que implícitamente se emplea aquí para las pilas, la respuesta es satisfactoria para tratamientos usuales prácticos, como por ejemplo en el análisis de expresiones de la calculadora en aritmética extendida que se presentará en los ejemplos.

Veamos ahora las funciones correspondientes del grupo de emulación de pilas.

 

Las pilas funcionan como las pilas de platos, el último que se coloca es el primero que se retira. Por eso en inglés se dice que siguen el sistema LIFO “last input first output”. En SRRCW se crean con la función SRRCW_LIFO_NEW:

 

//-------------------------------------------------------------------------------

//

// Función SRRCW_LIFO_NEW

//

// Descripción Genera una pila (LIFO)

//

//

// Parámetros Formato completo

//

// (entrada) cLIFO Nombre de pila asociada

//           lDimD Dimensión de datos a guardar en la pila

//        lMaxNreg NºMáximo de registros a guardar

//

//

// Parámetros Formato compacto

//

// (entrada) cLIFO Nombre de pila asociada

//           lDimD Dimensión de datos a guardar en la pila

//

// Retorno

//         0 Error de proceso

//        >0 Nid asociado

//

//--------------------------------------------------------------------------------

long SRRCW_LIFO_NEW(const char *cLIFO, long lDimD, long lMaxNreg)

{

 return SRRCW_NEW(cLIFO, 25*sizeof(char), lDimD, lMaxNreg);

}

 

Se trata de un interfaz a la función SRRCW_NEW estándar en el que no hay que proporcionar la longitud de clave, que se asignará automáticamente como se indica más adelante.

 

Su gestión se realiza con dos funciones, la dedicada a su escritura y su recíproca de lectura

 

Los ítems de la pila se graban utilizando la función SRRCW_LIFO_WRITE que sigue:

 

//-----------------------------------------------------------------------------------

// Función SRRCW_LIFO_WRIT

//

// Descripción Guarda un ítem en la pila

//

// Parámetros

// (entrada) cLIFO Nombre de la pila

//           vDato Datos a guardar

//

// Retorno

//         0 Error de proceso

//        >0 Indice del ítem guardado

//

//-----------------------------------------------------------------------------------

long SRRCW_LIFO_WRIT(const char *cLIFO, const void *vDato)

{

 static char cClav[25];        // Aux.paso de clave

 static long lCONTI=214783647; // Contador inverso desde el máximo long signed

 

 

 // Clave: STAM inverso + contador inverso

 

 

 // Compone la porción del estampillado horario inverso

 // (El estampillado es práctico a efectos de seguimiento de detalle cuando los datos

 // se salvan a un archivo físico de respaldo)

 

 cSTAMNUMINV = SRFECHA_STAMNUMINV();

 strcpy(cClav, cSTAMNUMINV);


 

 

 // Compone la porción del contador inverso

 

 --lCONTI;

 cLTOA = SRRCAL_ltoal(lCONTI);

 memcpy(cClav+14, cLTOA, 11);

 

 

 // Graba ítem (estilo LIFO debido al contador inverso)

 

 return SRRCW_WRITE(cLIFO, cClav, vDato);

}

                                                                                                  _________

 

 

En esencia se trata de un interfaz al SRRCW_WRITE estándar que incorpora una clave automática inversa que permite que la emisión de lecturas, que veremos ahora, recupere en primer lugar los últimos registros grabados:

 

//-----------------------------------------------------------------------------------

//

// Función SRRCW_LIFO_READ

//

// Descripción Lee un ítem de la pila

//

//

// Parámetros Formato completo

//

// (entrada) cLIFO Nombre de pila

// (salida)  vDato Datos del ítem

// (entrada) cOpci Opción 0/1 borrar elemento leído (1 es la opción por defecto)

//

//

// Parámetros Formato compacto (En donde asume “borrar tras leer”)

//

// (entrada) cLIFO Nombre de pila

// (salida)  vDato Datos del ítem

//

// Retorno

//         0 OK

//        >0 Error de proceso

//-----------------------------------------------------------------------------------

short int SRRCW_LIFO_READ(const char *cLIFO, void *vDato, char cOpci)

{

 static char cClav[25]; // Aux.paso de clave

 long lResul = 0;       // Control resultado invocaciones

 

 

 // Lee primer ítem. Por la natureleza de la clave es el último grabado

 

 er = SRRCW_READ(cLIFO, 1, vDato, cClav);

 if (er) return er;

 

 if (cOpci == '0') return NO_ERRO;

 

 

 // Elimina ítem si se solicita

 

 lResul = SRRCW_DELETE(cLIFO, cClav);

 if (!lResul) return 1;

 

 return NO_ERRO;

}

 

Como en los casos anteriores, también nos encontramos ante un interfaz del SRRCW_READ, en donde se recupera el primer ítem disponible que por la naturaleza inversa de la clave se tratará del último grabado. Además el ítem leído puede o no ser depurado de la pila a voluntad. Esta característica resultará ser vital para el analizador de expresiones de la calculadora en aritmética extendida que se presenta en los ejemplos y que constituye precisamente un ejemplo completo del uso de pilas.

 

                                                                                                  _________

 

 

Vemos ahora la codificación de la rutina auxiliar de estampillado inverso utilizada al grabar los ítems de las pilas:

 

En la cabecera del fuente del programa de servicio SRFECHA se incluye la siguiente definición de variable global, de permanencia estática durante la ejecución del programa:

 

 

// Salida de SRFECHA_STAMNUM | SRFECHA_STAMNUMINV

static char cStamNum[] = " ";

 

 

Y que da soporte a la salida de las funciones de estampillado como sigue:

 

//---------------------------------------------------------------------------

// Función: SRFECHA_STAMNUMINV

//

// Propósito: Devuelve estampillado tipo

//

// "99999999999999 - AAAAMMDDHHMMSS" en cStamNum[15]

//

// Parámetros: Ninguno

//

// Retorno:

//         Estampillado inverso solicitado solicitado

//---------------------------------------------------------------------------

char *SRFECHA_STAMNUMINV(void)

{

 long lDate = 0; // Paso de *date AAAAMMDD

 long lTime = 0; // Valor de retorno *time HHMMSS

 

 

 // Construye el estampillado inverso

 

 lDate = SRFECHA_DATE();

 lDate = 99999999 - lDate;

 

 lTime = SRFECHA_TIME();

 lTime = 999999 - lTime;

 

 

 // Construye variable de salida y termina proceso

 

 cLTOA = SRRCAL_ltoal(lDate);

 memcpy(cStamNumInv, cLTOA + 2, 8);

 

 cLTOA = SRRCAL_ltoal(lTime);

 memcpy(cStamNumInv + 8, cLTOA + 4, 7);

 

 return cStamNumInv;

}

 

 

Que invoca a su vez a las rutinas DATE y TIME siguientes:

 

//---------------------------------------------------------------------------

// Función: SRFECHA_DATE

//

// Propósito: Devuelve fecha en curso en formato AAAAMMDD

//

// Parámetros: Ninguno

//

// Retorno: DATE solicitado

//---------------------------------------------------------------------------

long SRFECHA_DATE(void)

{

 long lDate = 0; // Valor de retorno *date AAAAMMDD

 

 

 // *Time & date

 

 struct tm *local;

 time_t tim;

 

 tim = time(NULL) ;

 local = localtime(&tim);

 

 

 // Fecha en curso a devolver

 

 lDate = (local->tm_year + 1900) * 10000 + local->tm_mon * 100 + local->tm_mday;

 

 return lDate;

}

 

 

//---------------------------------------------------------------------------

// Función: SRFECHA_TIME

//

// Propósito: Devuelve time en curso en formato HHMMSS

//

// Parámetros: Ninguno

//

// Retorno: TIME solicitado

//---------------------------------------------------------------------------

long SRFECHA_TIME(void)

{

 long lTime = 0; // Valor de retorno *time HHMMSS

 

 

 // *Time & date

 

 struct tm *local;

 time_t tim;

 

 tim = time(NULL) ;

 local = localtime(&tim);

 

 

 // Time en curso a devolver

 

 lTime = local->tm_hour * 10000 + local->tm_min * 100 + local->tm_sec;

 

 return lTime;

}

                                                                                                  _________

 

Con estos recursos el funcionamiento concreto de las pilas sería como en el ejemplo que sigue.

Si estuviéramos en el analizador de expresiones de la calculadora de la sección de ejemplos y nos encontráramos con que tuviésemos que apilar la secuencia de operadores + * - / ^ estos se archivarían en una forma similar a la que se expone a continuación:

 

Número de Secuencia

Datos

799296977884650214783642

^

799296977885730214783643

/

799296977885860214783644

-

799296977885960214783645

*

799296977886550214783646

+

 

de forma que al leer secuencialmente, los registros se recuperarían en orden inverso al grabado, esto es, como ^ / - * + , al estilo de una pila de platos, cada uno rotulado con el símbolo + * - / ^

                                                                                                  _________

 

A.I.7.8 La emulación de colas

 

Las colas se emulan de una forma similar a como se hace con las pilas.

 

Las colas funcionan como las colas de los cines, el primero que se coloca es el primero que se atiende. Por eso en inglés se dice que siguen el sistema FIFO “first input first output”. En SRRCW se crean con la función SRRCW_FIFO_NEW:

 

//----------------------------------------------------------------------------------

//

// Función SRRCW_FIFO_NEW

//

// Descripción Genera una cola (FIFO)

//

//

// Parámetros Formato completo

//

// (entrada) cFIFO Nombre de cola asociada

//           lDimD Dimensión de datos a guardar en la cola

//        lMaxNreg NºMáximo de registros a guardar

//

//

// Parámetros Formato compacto

//

// (entrada) cFIFO Nombre de cola asociada

//           lDimD Dimensión de datos a guardar en la cola

//

// Retorno

//        0 Error de proceso

//       >0 Nid asociado

//

//-----------------------------------------------------------------------------------

long SRRCW_FIFO_NEW(const char *cFIFO, long lDimD, long lMaxNreg)

{

 return SRRCW_NEW(cFIFO, 25*sizeof(char), lDimD, lMaxNreg);

}

 

 

Como en las pilas, también nos encontramos con un interfaz a la función SRRCW_NEW estándar en el que no hay que proporcionar la longitud de clave, que se asignará automáticamente como se indica más adelante, aunque aquí de valor creciente.

 

Su gestión se realiza también con dos funciones, la dedicada a la escritura y su recíproca de lectura en la cola.

 

Los ítems de la cola se graban utilizando la función SRRCW_FIFO_WRITE que sigue:

 

//----------------------------------------------------------------------------------

// Función SRRCW_FIFO_WRIT

//

// Descripción Guarda un ítem en la cola

//

// Parámetros

// (entrada) cFIFO Nombre de la cola

//           vDato Datos a guardar

//

// Retorno

//         0 Error de proceso

//        >0 Idice del ítem guardado

//

//-----------------------------------------------------------------------------------

long SRRCW_FIFO_WRIT(const char *cFIFO, const void *vDato)

{

 static char cClav[25]; // Aux.paso de clave

 static long lCONTD=0; // Contador directo

 

 

 // Clave: STAM directo + contador creciente

 

 // Compone la porción del estampillado horario directo

 // (El estampillado es práctico a efectos de seguimiento de detalle cuando los datos

 // se salvan a un archivo físico de respaldo)

 

 cSTAMNUM = SRFECHA_STAMNUM();

 strcpy(cClav, cSTAMNUM);

 

 

 // Compone la porción del contador creciente

 

 ++lCONTD;

 cLTOA = SRRCAL_ltoal(lCONTD);

 memcpy(cClav+14, cLTOA, 11);

 

 

 // Graba ítem (estilo FIFO debido a la clave creciente)

 

 return SRRCW_WRITE(cFIFO, cClav, vDato);

}

                                                                                                  _________

 

 

En esencia se trata de un interfaz al SRRCW_WRITE estándar que incorpora una clave automática creciente que permite que la emisión de lecturas, que veremos ahora, recupere en primer lugar los primeros registros grabados:

 

//-----------------------------------------------------------------------------------

//

// Función SRRCW_FIFO_READ

//

// Descripción Lee un ítem de la cola

//

//

// Parámetros Formato completo

//

// (entrada) cFIFO Nombre de cola

// (salida)  vDato Datos del ítem

// (entrada) cOpci Opción 0/1 borrar elemento leído (1 es el valor por defecto)

//

//

// Parámetros Formato compacto (En donde asume “borrar tras leer”)

//

// (entrada) cFIFO Nombre de cola

// (salida)  vDato Datos del ítem

//

// Retorno

//         0 OK

//        >0 Error de proceso

//-----------------------------------------------------------------------------------

short int SRRCW_FIFO_READ(const char *cFIFO, void *vDato, char cOpci)

{

 static char cClav[25]; // Aux.paso de clave

 long lResul = 0;       // Control resultado invocaciones

 

 

 // Lee primer ítem. Por la naturaleza de la clave es el grabado en primer lugar

 

 er = SRRCW_READ(cFIFO, 1, vDato, cClav);

 if (er) return er;

 

 if (cOpci == '0') return NO_ERRO;

 

 

 // Elimina ítem si se solicita

 

 lResul = SRRCW_DELETE(cFIFO, cClav);

 if (!lResul) return 1;

 

 return NO_ERRO;

}

                                                                                                  _________

 

 

Como en los casos anteriores, también nos encontramos ante un interfaz del SRRCW_READ, en donde se recupera el primer ítem disponible que por la naturaleza inversa de la clave se tratará del último grabado. Además el ítem leído puede o no ser depurado de la cola a voluntad.

 

Vemos ahora la codificación de la rutina auxiliar de estampillado directo utilizada al grabar los ítems de las colas:

 

//---------------------------------------------------------------------------

// Función: SRFECHA_STAMNUM

//

// Propósito: Devuelve estampillado tipo

//

// "AAAAMMDDHHMMSS" en cStamNum[15]

//

// Parámetros: Ninguno

//

// Retorno:

//           Estampillado solicitado

//---------------------------------------------------------------------------

char *SRFECHA_STAMNUM(void)

{

 long lDate = 0; // Paso de *date AAAAMMDD

 long lTime = 0; // Valor de retorno *time HHMMSS

 

 

 // Construye el estampillado directo

 

 lDate = SRFECHA_DATE();

 lTime = SRFECHA_TIME();

 

 

 // Construye variable de salida y termina proceso

 

 cLTOA = SRRCAL_ltoal(lDate);

 memcpy(cStamNum, cLTOA + 2, 8);

 

 cLTOA = SRRCAL_ltoal(lTime);

 memcpy(cStamNum + 8, cLTOA + 4, 7);

 

 return cStamNum;

}

                                                                                                  _________

 

Con estos recursos el funcionamiento concreto de las colas sería como se muestra ahora.

 

Si en el ejemplo anterior deseáramos encolar en vez de apilar la secuencia de operadores + * - / ^ , estos se archivarían en una forma similar a la siguiente:

 

 

Número de secuencia

Datos

200512012210300000000001

+

200512012210350000000002

*

200512012210380000000003

-

200512012210450000000004

/

200512012210550000000005

^

 

de forma que al leer en orden estos registros que se han estampillado de forma directa al guardarlos, se recuperarían consecutivamente de la misma forma en la que se grabaron, esto es + * - / ^ , como es de desear que se responda en una cola.

                                                                                                  _________

 

 

Con lo que cerramos la visión de los extractos de la codificación principal de las funciones de SRRCW pues el resto de funciones son auxiliares y su código puede seguirse fácilmente en las carpetas de proyecto, o acudiendo a los ejemplos que se encuentran en el libro, así como al propio anexo dedicado al manual de usuario.

 

                                                                                                  _________

 

 

A.I.7.9 Un ejemplo de descarga de tareas de orden implícito. SRRCS_ACU

 

Disponer de una herramienta de emulación de ficheros permite desarrollar rutinas más sencillas, descargándolas de tareas de orden implícito que suelen complicar los desarrollos llegando a ocultar la lógica de los algoritmos implementados con los detalles de su codificación.

 

 

Veamos un ejemplo concreto, en el que vamos a tomar una rutina y le vamos a descargar de una tarea de orden implícito pasándola a un fichero virtual:

 

//---------------------------------------------------------------------------

//

// DESCRIPCION: Acumula importes por fechas (Codificación de partida)

//

// - Rutina auxiliar que prepara flujos financieros para proceso

// posterior, acumulándolos por fechas

//

// - Ésta versión original de la rutina asume implícitamente que los datos están

// ordenados en orden creciente según los plazos de lSf

//

// Parámetros:

//

// (I) inSf: Dimensión efectiva de series (base 0..n-1)

// (IO) *lSf: Puntero a serie de plazos

// (IO) *dSi: Puntero a serie importes a acumular

//

// Retorno: Dimensión efectiva tras la agrupación

//

//---------------------------------------------------------------------------

 

short int SRRCFS_ACU(int inSf, long *lSf, double *dSi)

{

 register int i;      // Contador de DO

 int inSfO = -1;      // Dimensión efectiva de salida

 long lFecA = *(lSf); // Plazo anterior tratado

 double dImpA = 0.0;  // Importe anterior tratado

 

 

 // Filtro

 

 if (inSf <= 1) return inSf;

 

 

 

 // Bucle de proceso

 

 for(i=0;i<inSf;++i)

 {

 

  // Cambio de plazo

 

  if( *(lSf + i) != lFecA)

  {

   ++inSfO;

   *(lSf + inSfO) = lFecA;

   *(dSi + inSfO) = dImpA;

   lFecA = *(lSf + i);

   dImpA = 0.0;

  }

 

 

  // Acumulación efectiva

 

  dImpA += *(dSi + i);

 }

 

 

 // Sale último registro

 

 ++inSfO;

 *(lSf + inSfO) = lFecA;

 *(dSi + inSfO) = dImpA;

 

 

 // Sale nueva dimensión

 

 return inSfO;

}

                                                                                                  _________

 

 

La anterior codificación del algoritmo de agregación, aún sin ser difícil, obliga a uno a repensarla con cierto detenimiento cada vez que se la encuentra para recordar cómo funciona.

 

 

Por el contrario, consideremos la siguiente implementación donde usamos un fichero virtual para resolver la acumulación:

 

 

short int SRRCFS_ACU(int inSf, long *lSf, double *dSi)

{

 register int i;   // Contador de DO

 int inSfO = -1;   // Dimensión efectiva de salida

 

 short int er = 0; // Control de errores en SRRCW

 long lResul = 0;  // Control de resultados en SRRCW

 long lNid = 0;    // Identificador fichero SRRCW

 

 

 

// Zona de definición de la estructura de soporte del fichero virtual utilizado

 

 

 // Ds claves del fichero virtual soporte del proceso

 

 struct sDsK {long lSf;}; // Los resultados se ordenarán por plazo

 

 

 // Ds datos del fichero virtual soporte de proceso

 

 struct sDsD {double dSi;}; // Importe

 

 

 // Ds del fichero virtual soporte de proceso

 

 struct sDsAcum {struct sDsK sDk; sDsD sDd;} sDsAcu;

 

 

 

 // Comienzo de proceso

 

 

 // Filtro

 

 if (inSf <= 1) return inSf;

 

 

 

 // Crea el fichero virtual a utilizar. Si ya existe previamente se limpia

 

 lNid = SRRCW_NEW("DSACU", sizeof(sDsAcu.sDk), sizeof(sDsAcu));

 if(!lNid) return inSf;

 

 

 

 // Bucle de proceso

 

 for(i=0;i<inSf;++i)

 {

 

  // Pasa campos al formato de registro del fichero virtual de proceso

 

  sDsAcu.sDk.lSf = *(lSf + i);

  sDsAcu.sDd.dSi = *(dSi + i);

 

 

  // Graba o actualiza el fichero virtual de soporte

 

  lResul = SRRCW_WRITE("DSACU", &sDsAcu);

 

 

  // Si graba, progresa el contador de salida

 

  if (lResul) ++inSfO;

 

 

  // En caso contrario, accede al contenido anterior y lo actualiza

 

 else

 {

 

   // Accede

 

   lResul = SRRCW_CHAIN("DSACU", &sDsAcu.sDk, &sDsAcu);

 

 

   // Acumula y actualiza

 

   sDsAcu.sDd.dSi += *(dSi + i);

 

 

   lResul = SRRCW_UPDATE("DSACU", &sDsAcu);

  }

 }

 


 

 // Bucle de volcado de resultados

 

 for(i=0;i<inSfO;++i)

 {

  er = SRRCW_READ("DSACU", i+1, &sDsAcu);

  if (er) return inSf;

 

  *(lSf + i) = sDsAcu.sDk.lSf;

  *(dSi + i) = sDsAcu.sDd.dSi;

 }

 

 

 // Sale dimensión obtenida

 

 return inSfO;

}

 

[Nota: Se trata de una codificación para iSeries, en MsC++ habría que añadir SRRCAL_lLexi para garantizar el orden de la clave en el sentido memcmp]

 

                                                                                                  _________

 

 

En ésta implementación el algoritmo empleado es evidente, se graban los datos de partida en el fichero virtual, y si ya existe el registro de destino, se acumulan en él los importes; por último, se vuelcan los resultados a las series de entrada/salida.

 

Se podría probar la existencia antes de grabar en SRRW_WRITE con la operación SRRCW_SETEQ, siguiendo la idea de minimizar el impacto en base de datos, pero no hace falta pues estamos tratando con operaciones virtuales, el sistema no sufre manipulación de objetos físicos y no hay tal “impacto en base de datos”.

 

Por el contrario, si de verdad nos encontráramos ante una base de datos física, sería muy conveniente tal verificación, a efectos de mejorar el rendimiento y minimizar la generación de avisos del sistema en el log del trabajo.

 

Además no se precisa que los datos leídos estén preordenados, pero se garantiza que saldrán ordenados por construcción.

 

La claridad de la rutina resultante compensa el mayor nº de sentencias, ésta claridad se aprecia aún más con el paso del tiempo por parte del personal responsable del mantenimiento.

 

Este aspecto es vital en la implementación "industrial" tal y como enseña la experiencia, y debe cuidarse al extremo. El día a día lamentablemente puede no dejar mucho tiempo para ello, pero si no se hace alguien lo acaba pagando.

 

En cuanto al rendimiento, no se aprecian diferencias en la práctica ya que aunque la segunda rutina es más larga e invoca un procedimiento externo, elude la exigencia de que los datos de entrada estén ordenados, integrando implícitamente la rutina de clasificación que se ahorra como paso previo, lo que es una razón más para simplificar la lógica y descargar complejidad en programas de servicio.

 

                                                                                                  _________

 

 

A.I.7.10 Un ejemplo de paso implícito de información

 

Además de conocer el uso ortodoxo de los procedimientos, también conviene conocer recursos alternativos para situaciones provisionales de emergencia.

 

En esta línea vamos a ver cómo pasar información de forma implícita entre funciones en la misma pila de invocación pero no contiguas, como un procedimiento no recomendado para aplicación permanente pero válido como salvaguarda puntual, particularmente en situaciones heredadas.

 

 

Basta definir la estructura DSPASO:

 

 

 

// Ds de clave de acceso a la información extendida a pasar

 

struct sDsK {char cId[21];}; // Identificador de acceso

 

 

// Ds término asociado

 

struct sDsD {void *pDS;}; // Puntero a estructura soporte de paso de

                                         // información extendida DSPARM

 

 

// Ds del fichero virtual de soporte de paso de información extendida

 

static struct sDSPASO

{

 struct sDsK DSK;

 struct sDsD DSD;

} DSPASO;

 

 

 

Y serializarla asociándole un fichero virtual, grabar la información precisa en el procedimiento más interno y recuperarla luego en el más externo tras el retorno de invocación principal:

 

Procedimiento01 <–> Procedimiento 02 <-> ... <–> Procedimiento11

 

 

Incluso puede plantearse en otras situaciones similares de más difícil planteamiento a priori, en que estos procedimientos sean componentes de DLL’s distintas, en un esquema como el siguiente:

 

(DLL01) Procedimiento10 <-> Procedimiento11 (En donde se grabarían los datos)

 

(DLL02) Procedimiento01 (En donde se leerían los datos desde DSPASO)

 

 

Sea cual sea la situación que se plantee, en ambos procedimientos podemos tener definida una estructura de soporte de información extendida DSPARM, como por ejemplo:

 

struct sDSPARM // Estructura de información ampliada en Procedimiento01

{

 long li;      // Parámetro i

 long lj;      // Parámetro j

 double dImpo; // Parámetro importe

 long lEnte;   // Parámetro parte entera

 long lDeci;   // Parámetro parte decimal

};

 

static struct sDSPARM //Estructura de información ampliada en Procedimiento11

{

 long li;      // Parámetro i

 long lj;      // Parámetro j

 double dImpo; // Parámetro importe

 long lEnte;   // Parámetro parte entera

 long lDeci;   // Parámetro parte decimal

} DSPARM;

 

 

que complemente el paso ordinario de parámetros que, por las razones que sean, no establezcan comunicación directa del contenido que se define en DSPARM entre Procedimento01 y Procedimiento11.

 

                                                                                                  _________

 

 

Entonces, se podría implementar el siguiente juego de proceso tal como se hace en el programa ejemplo PruebasW:

 

void Procedimiento01 (void)

{

 long lParteEntera = 0;           // Receptor de parte entera

 long lParteDecimal= 0;           // Receptor de parte decimal

 char cId[] = "Procedimiento11 "; // Identificador de enlace

 long lNID = 0;                   // Identificador fichero DSPASO

 long lResul = 0;                 // Control de resultados SRRCW

 

 

 

 

// Ds de clave de acceso a la información extendida a pasar

 

struct sDsK {char cId[21];}; // Identificador de acceso

 

 

// Ds término asociado

 

struct sDsD {void *pDS;}; // Puntero a estructura soporte de paso de

                                         // información extendida

 

// Ds del fichero virtual de soporte de paso de información extendida

 

struct sDSPASO

{

 struct sDsK DSK;

 struct sDsD DSD;

} DSPASO;

 

 

 

 

 

struct sDSPARM // Estructura de información ampliada a recibir

{

 long li;      // Parámetro i

 long lj;      // Parámetro j

 double dImpo; // Parámetro importe

 long lEnte;   // Parámetro parte entera

 long lDeci;   // Parámetro parte decimal

};

 

struct sDSPARM *p_DSPARM; // Soporte paso información extendida

 

 

 

  

// (Re)genera el fichero virtual de paso de información extendida

 

lNID = SRRCW_NOW("DSPASO", sizeof(DSPASO.DSK), sizeof(DSPASO));

 

 

 

// Ejecuta proceso con paso de información limitada

 

Procedimiento02(1, 2, 1.2);

 

 

 

// Codificación añadida para la recuperación de información extendida:

 

 

 

// Recepción de la información extendida adicional

 

lResul = SRRCW_CHAIN("DSPASO", cId, &DSPASO);

 

 

// Formateo según la estructura de soporte definida

 

p_DSPARM = (sDSPARM *) DSPASO.DSD.pDS;

 

lParteEntera = p_DSPARM -> lEnte;

lParteDecimal = p_DSPARM -> lDeci;

 

 

[ Tras la ejecución se tiene lParteEntera = 1 y lParteDecimal = 2]

 

 

 return;

}

 

                                                                                                  _________

 

 

 

[ El procedimiento con paso limitado de información puede ser origen de una pila de invocación multinivel, como en este caso: ]

 

 

void Procedimiento02(long i, long j, double importe)

 

{return Procedimiento11(i, j, importe);}

 

. . .

 

 

 

[ Finalmente, en un procedimiento interior se genera la información deseada: ]

 

 

void Procedimiento11(long i, long j, double importe)

{

 long lResul = 0;                 // Control de resultados SRRCW

 char cId[] = "Procedimiento11 "; // Identificador de enlace

 

 

 

 

// Ds de clave de acceso a la información extendida a pasar

 

struct sDsK {char cId[21];}; // Identificador de acceso

 

 

// Ds término asociado (

 

struct sDsD {void *pDS;}; // Puntero a estructura de datos a pasar

 

 

// Ds del fichero virtual de soporte de paso de información extendida

 

static struct sDSPASO

{struct sDsK DSK;

 struct sDsD DSD;

} DSPASO;

 

 

 

 

 

static struct sDSPARM // Estructura de información ampliada a pasar

{

 long li;      // Parámetro i

 long lj;      // Parámetro j

 double dImpo; // Parámetro importe

 long lEnte;   // Parámetro parte entera

 long lDeci;   // Parámetro parte decimal

} DSPARM;

 

 

 

// ... Proceso que genera la información deseada

 

 

 

// Codificación añadida para el paso implícito de información:

 

 

 

// Pasa información extendida implícita tras el proceso ordinario

 

DSPARM.li = i;

DSPARM.lj = j;

DSPARM.dImpo = importe;

DSPARM.lEnte = i;

DSPARM.lDeci = j;

 

 

// Formatea la información según la estructura de paso

 

memcpy(DSPASO.DSK.cId, cId, 21);

DSPASO.DSD.pDS = (void *) &DSPARM;

 

 

// Graba la información extendida para su utilización externa indirecta

 

lResul = SRRCW_WRITE("DSPASO", &DSPASO);

 

 

 

 return;

}

 

                                                                                                  _________

 

 

A.I.7.11 Ampliación de componentes

 

Veamos ahora como se ampliaría un componente para agregar una nueva funcionalidad.

 

Para ello nos vamos a centrar en SRRCW_CPY, función que permite copiar contenido entre ficheros virtuales con el mismo diseño, particularmente para reservar datos.

 

No la hemos presentado en detalle anteriormente al centrarnos más bien en las rutinas que comparte con su alter ego SRRCW_DUP que duplica ficheros.

 

SRRCW_DUP es una función similar pero orientada a nivel de objeto global y que por tanto no está diseñada para alcanzar la versatilidad potencial del CPY al manipular registros.

 

Esta versatilidad permite, como vamos a ver ahora, agregar diversas funcionalidades a nivel de detalle de registros.

 

 

Para SRRCW_CPY el prototipo original es

 

//--------------------------------------------------------------------------------------

//

// Función: SRRCW_CPY

//

// Descripción: Copia de registros. Deben proporcionarse nombres de ficheros "físicos"

// base, aunque el proceso duplicará el contenido a toda la BD relacionada

//

// Parámetros:

// (entrada) cORG: Nombre del fichero físico origen

//           cDES: Nombre del fichero físico destino

//           iREP: 0/1 Borrar destino (*Dft 1)

//

// Retorno:

//          >=0: Contador de registros copiados

//           <0: Error de proceso

//--------------------------------------------------------------------------------------

long SRRCW_CPY(const char *cORG, const char *cDES, short int iREP);

long SRRCW_CPY(const char *cORG, const char *cDES); [iREP = 1]

 

 

La implementación responde al equivalente CPYF *ADD y CPYF *REPLACE del iSeries.

 

Si ahora se presenta la necesidad de añadir una funcionalidad equivalente a un *MERGE, de forma que podamos mantener una copia de respaldo del último contenido anterior de un fichero que vayamos actualizando por zonas, de forma que las nuevas copias añadan o actualicen el contenido anterior, debemos cambiar el prototipo y la implementación como se indica a continuación, donde se ha remarcado la codificación añadida:

 

//--------------------------------------------------------------------------------------

//

// Función: SRRCW_CPY

//

// Descripción: Copia de registros. Deben proporcionarse nombres de ficheros "físicos"

// base, aunque el proceso duplicará las operaciones a la BD relacionada

//

// Parámetros:

// (entrada) cORG: Nombre del fichero físico origen

//           cDES: Nombre del fichero físico destino

//           iREP: 0=Copia con adicción simple (COPY *ADD)

//                 1=Copia con borrado previo (COPY *REPLACE) [*DFT]

//                 2=Copia con eliminación previa de registros duplicados en destino (COPY *MERGE)

//

// Retorno:

//         >=0: Contador de registros copiados

//          <0: Error de proceso

//--------------------------------------------------------------------------------------

long SRRCW_CPY(const char *cORG, const char *cDES, short int iREP)

 

{

 static char cORGw[33]; // Aux.paso de nombre origen

 static char cDESw[33]; // Aux.paso de nombre destino

 long lNCPY = 0;        // Contador de registros copiados

 long lNDEL = 0;        // Contador de registros duplicados borrados en *MERGE

 

 

 // Normaliza nombres

 memcpy(cDESw, SRRCW_NAME(cDES), 33 * sizeof(char));

 memcpy(cORGw, SRRCW_NAME(cORG), 33 * sizeof(char));

 

 

 // Bajo *MERGE, elimina registros duplicados en destino previamente

 if (iREP == 2)

 {

  // Eliminación previa

  lNDEL = SRRCW_DELCPY(cORG, cDES);

 

  // Copia *ADD posterior

  lNCPY = SRRCW_CP(cORG, cDES, 0);

 }

 

 // Copia ordinaria

 else lNCPY = SRRCW_CP(cORG, cDES, iREP);

 

 

 // Filtro

 if (!lNCPY) return lNCPY;

 

 

 // Extiende la copia a los lógicos asociados

 er = SRRCW_DUPLICPY(cORGw, cDESw);

 

 

 return lNCPY;

}

 

// Formato compacto

 

long SRRCW_CPY(const char *cORG, const char *cDES)

{

 return SRRCW_CPY(cORG, cDES, 1);

}

 

 

La implementación utiliza dos funciones auxiliares, SRRCW_CP módulo común de copia que se comparte con SRRCW_DUP y la función auxiliar específica SRRCW_DELCPY, variante particular de SRRCW_CP que se encarga de eliminar los registros duplicados en destino.

 

                                                                                                  _________

 

Veamos ahora la codificación principal de ambas

 

//--------------------------------------------------------------------------------------

//

// Función: SRRCW_CP

//

// Descripción: Copia de registros. Parte común a CPY y a DUP

//

// Parámetros:

// (entrada) cORG: Nombre del fichero origen

//           cDES: Nombre del fichero destino

//           iREP: 0/1 Reemplazar. 1=Borra destino antes de copiar

//

// Retorno:

//          >=0: Contador de registros copiados

//           <0: Error de proceso

//--------------------------------------------------------------------------------------

 

long SRRCW_CP(const char *cORG, const char *cDES, short int iREP)

{

 static char cClavW[iSIZEMAX]; // Aux.cadena de clave

 static char cDatoW[lSIZEMAX]; // Aux.cadena de datos

 

 static char cPF[33]; // Nombre del fichero físico de una DB virtual

 

 int iDes = 0;        // 0/1 Fichero desglosado K||D s/comparación memcmp

 long l = 0;          // Contador de for

 long lNID_ORG = 0;   // NID origen

 long lNID_DES = 0;   // NID destino

 long lNCPY = 0;      // Valor de retorno. Contador de registros copiados

 

 

// Recupera NID origen

 

lNID_ORG = SRRCW_NID(cORG);

 

 

// Recupera diseño asociado para poder desglosar claves de datos

 

er = SRRCM_INF(lNID_ORG, &iDimC, &lDimD, &lNÍTEM, &lBAJAS, &lNIDD);

 

 

// Recupera NID destino

 

lNID_DES = SRRCW_NID(cDES);

 

 

 

// Inicializa si procede

 

if (iREP) er = SRRCW_CLRF(cDES);

 

 

 

// Núcleo de copia de datos

 

for (l=1;;++l)

{

  er = SRRCM_READC(lNID_ORG, l, cDatoW, cClavW);

  if (er) break;

 

 

  // Determina si K||D examinando K&D del 1º

 

  if (l==1) iDes = memcmp(cClavW, cDatoW, iDimC);

 

 

  // Graba destino desgajando clave si procede

 

  if (!iDes) lresul = SRRCM_WRITE(lNID_DES, cDatoW, cDatoW); // =: K & D se solapan

  else lresul = SRRCM_WRITE(lNID_DES, cClavW, cDatoW);

 

  if (lresul) ++lNCPY;

 }

 

 

 return lNCPY;

}

                                                                                                  _________

 

 

 

Basándonos en esta rutina se codifica SRRCW_DELCPY, que resulta más sencilla:

 

//--------------------------------------------------------------------------------------

//

// Función: SRRCW_DELCPY

//

// Descripción: Rutina auxiliar de borrado de registros en cpy(2=Merge)

//

// Parámetros:

// (entrada) cORG: Nombre del fichero origen

//           cDES: Nombre del fichero destino

//

// Retorno:

//          >=0: Contador de registros eliminados

//           <0: Error de proceso

//--------------------------------------------------------------------------------------

long SRRCW_DELCPY(const char *cORG, const char *cDES)

{

 static char cClavW[iSIZEMAX]; // Aux.cadena de clave

 static char cDatoW[lSIZEMAX]; // Aux.cadena de datos

 

 long l = 0;     // Contador de for

 long lNDEL = 0; // Valor de retorno. Contador de registros borrados

 

 

 // Depura destino de datos origen

 

 for (l=1;;++l)

 {

  er = SRRCW_READ(cORG, l, cDatoW, cClavW);

  if (er) break;

 

  lresul = SRRCW_DELETE(cDES, cClavW);

  if (lresul) ++lNDEL;

 }

 

 return lNDEL;

}

                                                                                                  _________

 

Para introducir y compilar el código lo más rápido es emplear el proyecto mínimo asociado a SRRCW que se encuentra en su carpeta asociada.

 

Sin embargo, para poder utilizar un debug sobre la DLL actualizada resultante es necesario recompilar toda la pila de llamadas desde SRRCW hasta el interfaz que se utilice, como pueda ser PruebasW.

 

Esta situación es propia del MS C++, en el iSeries no se requiere este reenlace para la disponibilidad del depurador sobre el componente cambiado, esto se debe a que las peculiaridades de verificación de integridad de objetos y signatura de componentes son distintas en ambos sistemas.

 

Una vez introducido y compilado el código, para su aplicación deben tenerse en cuenta las observaciones que se indican en el epígrafe

 

"A.I.10.2 Estrategia del cambio"

 

Aún cuando el cambio es pequeño, por metodología debe seguirse el proceso que allí se indica paso a paso. Entonces, debe cambiarse y probarse en el PC, ampliando PruebasW, luego debe llevarse al sistema de desarrollo iSeries, verificar la batería de pruebas estándar (que debe ampliarse a su vez para integrar la nueva funcionalidad) y aplicarlo luego al sistema de explotación.

 

 

                                                                                                  _________

 

 

 

Ahora en el capítulo siguiente presentaremos una versión de SRRCW orientada a su uso como clase, invitando previamente, como en los capítulos anteriores, a consultar el fuente completo del módulo en SRRCW.cpp, así como a visitar también los anexos dedicados a los fuentes, y muy particularmente el capítulo "C3 Detalle de relación de programas de servicio (DLL) soporte de de los ficheros virtuales"  que recoge los fuentes de los programas de servicio que constituyen el soporte principal del sistema de ficheros virtuales.

 

 

                                                                                                  _________