Gestión e indexación de registros de una tabla en SD.

Jeje, Marcial. Cuando yo estudié informática franco aún era corneta. Efectivamente, entendí perfectamente a qué te referías. Aunque siendo tiquismiquis, el acceso aleatorio/secuencial no se refiere a cómo se trabaja con una base de datos, sino cómo se puede leer o escribir en un archivo; si puedes acceder a leer o escribir cualquier parte directamente o tienes que comerte la vaca entera... ¿te acuerdas de las cintas del zx81 o spectrum?. Por supuesto que sin acceso aleatorio una base de datos sería una pesadilla, pero con índices, sin índices, archivos de imagen o de cualquier tipo, todos los archivos en disco o SD se pueden acceder aleatoriamente.
Mañana te envío un esquema de cómo va organizado el archivo, aunque si miras la cabecera de la librería verás la estructura header que te puede dar alguna pista (agrego comentarios):

struct FDB_Header
{
     unsigned int rec_size; // Posición 0-1, indica qué tamaño va a tener nuestro registro.
     IdReg filledRecords;  // Posición 2-5, número de índices a registros no borrados (a continuación del header)
     IdReg deletedRecords; // Posición 6-9, número de índices a registros borrados (tras los índices no borrados)
     FilePointer dataStart; // Posición 10-13, dirección a partir de donde se encuentran los datos
};

Continuando con el archivo:

  • Pos 14 hasta 14+4*filledRecords Índices ordenados de dirección(unsigned long). Cada index apunta a la posición en la que está el dato que indexa.
  • Pos 14+4filledRecords hasta 14+4(filledRecords+deletedRecords), Índices borrados. El dato sigue en su posición, pero los índices "saltan" a esta zona. Un nuevo registro reutilizará en primer lugar estos índices y su espacio de datos si existen, en lugar de ampliar tamaño de archivo. También cabría la posibilidad de devolver el index a su sitio y deshacer borrados.
  • Pos dataStart hasta final de archivo: bloques de datos. Aquí están los registros propiamente dichos. No están ordenados (los índices dicen qué registro va en qué posición). También encontraremos los datos de los registros eliminados, salvo que la escritura de otros los hayan machacado.

El proceso para obtener un registro en posición ordenada n, sería mover la "cabeza lectora" primero a la dirección 14+4*n, leer dirección (unsigned long), mover a la dirección leída, y cargar la estructura.

No sé si te he aclarado algo o te he liado más, pero cualquier cosa que te pueda aclarar, no dudes en consultar.

Ok, me pongo a ver de a poco como lo hago. Estoy medio apurado con hacer andar esto.
Por ahora lo voy a enviar por serial y depues veo como lo resuelvo.

Lo dicho. Cualquier ayuda que necesites sólo tienes que pedirla.

Noter, la libreria viene andando de maravillas por ahora.
Le hice un cambio usando la libreria SdFat que es mejorada y tiene manejo de memorias mas grandes.
Y lo curioso que en el ejemplo de basededatos me bajo -2224 bytes de programa, pero si consume +12 byte mas de ram.
Y es totalmente compatible, para tenerlo en cuenta.

Está interesante la librería. También permite el uso de nombres largos. Muy importante tener en cuenta los defines de SdFatConfig.h para optimizar recursos/desempeño.

de momento no escribe bien FDB. tengo que revisarla mejor

Avances del proyecto
viedo1

noter:
Lo dicho. Cualquier ayuda que necesites sólo tienes que pedirla.

Sigo usando tu libreria y ahora viene lo bueno, porque le voy a dar caña.
Pero necesito poder buscar para eliminar por otro dato y en el constructor se le pasa la funcion de comparacion.
Puedo modificarla para que reciba otra funcion momentaneamente?

No puedes cambiar de función de comparación, porque esa función establece el indexado y las búsquedas se apoyan en el indexado (de forma similar a como nosotros buscamos algo en el diccionario). Si cambias la función de comparación, dado que los registros no están ordenados según ese nuevo criterio, no vas a encontrar los registros deseados (imagina si quieres buscar algo en un diccionario desordenado). La única opción que te quedaría sería de nuevo hacer una búsqueda secuencial, osea repasar todos los registros, con lo que se pierde la utilidad de la librería.
No sé si en tu caso se podría conseguir algo replanteando el indexado inicial o habría que intentar modificar la librería para que maneje más de un índice, lo que complicaría bastante la cosa; ya que opté por poner el índice en el mismo archivo que los datos, aunque sea un poco más lioso que usar un archivo índice externo, porque creo haber leído (aunque no estoy seguro) que la librería SD sólo puede mantener abierto un archivo al mismo tiempo. Necesitaría que me des más detalles de qué datos implican ambos indexados.

es cierto lo que dices, crei que cambiando la comparacion podias y viendo. Pero como está planteada no se puede.
Yo tengo la el archivo FDB abierto desde el principio y no lo cierro, y ante un evento abro/creo un nuevo archivo con otro objeto SD para el diario ddmmyyy.log.
Esto está bien?

Pues precisamente ayer estuve mirando por ahí, pues pensaba que la librería SD sólo permitía un archivo abierto, y aunque no lo tengo seguro creo que la librería actual permite múltiples archivos, siempre que haya memoria para los objetos FILE, por lo que no debería haber problema en abrir o crear otros archivos mientras tienes abierto el FDB (aunque no es necesario crear otro SD ni otro volumen, sino otro objeto FILE). De todas formas, el movimiento se demuestra andando, así que si te está funcionando llevar ambas cosas será porque se puede :wink:

andar anda por ahora, hay que saber que debemos tener 512byte libres que usa para el buffer de la FAT, cuando estas justo empiezan los problemas, porque es dinamico, pide la memoria cuando se abre el archivo.

aunque no es necesario crear otro SD ni otro volumen, sino otro objeto FILE).

noter:
por lo que no debería haber problema en abrir o crear otros archivos mientras tienes abierto el FDB (aunque no es necesario crear otro SD ni otro volumen, sino otro objeto FILE)

No entendí de no crear otro volumen.
La rutina que uso es esta.

void LogThis(char logmessage[])
{
  char fechahora[18];
  char filename[13];


  //armo el archivo desde la fecha yyyymmdd.log
  dateTimeToString(fechahora,true,false); 
  strcpy(filename,fechahora);
  strcat(filename,ext_log);
	

	File LogsFile = SD.open(filename, FILE_WRITE);
	if (LogsFile) 
	{
            digitalWriteFast(LEDBUSY, HIGH); //informar comienzo de escritura
            //formateo de hora 00:00:00
            dateTimeToString(fechahora,true,true); 
  	    LogsFile.print(logmessage);
  	    LogsFile.print(F(" "));
  	    LogsFile.println(fechahora);
            LogsFile.flush();
            LogsFile.close();
	    digitalWriteFast(LEDBUSY, LOW); //informar fin escritura
 	}
 	else
 	{
 	  Error(ERR_NOSD);
  	}
  	
}

Yo tengo la el archivo FDB abierto desde el principio y no lo cierro, y ante un evento abro/creo un nuevo archivo con otro objeto SD para el diario ddmmyyy.log

No te preocupes. Tu código está bien, lo que pasa es que a veces soy un poco tiquismiquis con los términos. Lo que creas es otro objeto de clase FILE "colgando", eso sí, del mismo objeto SD (que es un objeto de clase SDClass).

noter:
No te preocupes. Tu código está bien, lo que pasa es que a veces soy un poco tiquismiquis con los términos. Lo que creas es otro objeto de clase FILE "colgando", eso sí, del mismo objeto SD (que es un objeto de clase SDClass).

Actualizo.
Te comento Noter y grupo que la libreria es un exito.
Tengo funcionando desde Agosto el dispositivo sin fallas, con 100 usuarios cargados y con excelentes resultados.

Lo malo es que la libreria SD agota la ram de arduino y deja poco espacio para las rutinas de comunicaciones.
Pero para algo autonomo si es muy util.

Muchas gracias por compartir tu experiencia, Maxid. Efectivamente, el escenario en el que podría ser muy útil es para algo autónomo, y supongo que igual que trabaja correctamente con 100 usuarios debería hacerlo con muchos más (si alguien lo prueba con alguna tabla de miles de registros, que comente por aquí), y es cuando realmente valdría mucho la pena. En cuanto a la RAM, efectivamente la librería SD ocupa bastante, y la librería mía supongo que también, dependiendo del tamaño de estructura que define el registro, aunque precisamente hago uso intensivo de referencias para evitar que se anden clonando estructuras en la pila.
Gracias de nuevo.

Estaba revisando esta librería para usarla con un lector RFID y me pareció que el ejemplo que tiene, además que a mi no me funcionó al 100%, como primera impresión me parece poco explicativo, así es que decidí separar cada una de las funciones que se proponen, en un archivo diferente y agregarle un par de comentarios para entenderlo.

He tenido que agregar el código como archivos adjunto debido al límite de caracteres que hay por post.

Esto es solo un pequeño aporte, cualquier error en el código me avisan para corregirlo. Sería buena idea crear un repositorio en github y colocar esta librería allí

ingresar_registro.ino (2.43 KB)

modificar_registro.ino (2.46 KB)

eliminar_registro.ino (1.99 KB)

buscar_registro.ino (2.35 KB)

tengo una tabla de 13250 registro de una base de datos de un control de acceso que estoy realizando , me gustaría probar la librería y mirar en que puedo colaborar

Hola, julio 625.
Precisamente pensando en ese tipo de aplicaciones me embarqué en hacer la librería. La pensé para situaciones en las que no es posible o no queremos comunicar con una base de datos alojada en un servidor, y necesitamos sencillamente poder mantener una tabla con un número elevado de registros ordenados por un determinado criterio.
Si tienes alguna duda o necesitas alguna explicación, exponla por aquí a ver qué se puede hacer. De momento, sería interesante saber qué campos y ordenación va a tener tu base de datos. Basado en eso debes decidir la estructura que va a contener un registro, y la función de ordenación de ese tipo de registro.

Recuerda que el trabajo de noter funciona con la EEPROM (limitado a su tamaño), con EEPROMs externas que albergarán tu base de datos o SDs.
Usa EDB Extended Data Base que alcanza estos 3 grupos de sistemas de almacenamiento.
Usa el más cómodo para tu aplicación.

Nunca quedo satisfecho con una respuesta.
Amplío:
http://playground.arduino.cc/Code/ExtendedDatabaseLibrary

Arduino Extended Database Library

This Arduino Extended Database Library increases the maximum number of records allowed in a database from 256 records (byte) to a theoretical maximum of 4,294,967,295 records (unsigned long). The maximum record size was also increased from 256 bytes (byte) to 65,534 bytes (unsigned int).
You may use this library in conjunction with the standard Arduino EEPROM library, an external EEPROM such as the AT24C1024 EEPROM, or any other platform that supports byte level reading and writing such as an SD card.