¿Cómo abrir y cerrar archivos correctamente "dentro" de una función miembro? [cerrado]

Estoy usando Arduino Uno y Ethernet Shield con una tarjeta SD de 2 GB. Tengo el siguiente código de trabajo destinado a ser utilizado para escribir datos en la tarjeta SD ( nota : la biblioteca SD está correctamente inicializada y solo escondo el código leído para mantener las cosas más pequeñas):

class Logger {
  private:
    File myFile;
    char *myFilename;

  public:
    Logger (char *myFilename = "text.txt") : myFilename(myFilename) {
      myFile = SD.open(myFilename, FILE_WRITE);
    }

    void writeAction () {
      if (myFile) {
        myFile.print("Sample text.");
        myFile.close();
      } else {
        Serial.print("Error opening the file ");
      }
    }
};

void setup() {
  Logger logger;

  ...

  logger.writeAction();
}

Sin embargo, si cambio un poco el código anterior como se hizo en el siguiente, no funcionará como se esperaba: no"Sample text." está escrito/guardado en la tarjeta SD.

class Logger {
  private:
    File myFile;
    char *myFilename;

  public:
    Logger (char *myFilename = "text.txt") : myFilename(myFilename) {
      // Note: Here is the change. File opening statements are moved
      // "inside" the writeAction function.
    }

    void writeAction () {
      myFile = SD.open(myFilename, FILE_WRITE);

      if (myFile) {
        myFile.print("Sample text.");
        myFile.close();
      } else {
        Serial.print("Error opening the file ");
      }
    }
};

void setup() {
  Logger logger;

  ...

  logger.writeAction();
}

¿Por que sucede? ¿Cómo puedo hacer que myFilese abra y cierre correctamente "dentro" de la writeActionfunción?

No recuerdo mis funciones de manejo de archivos C ++ en la parte superior de mi cabeza, pero ¿está seguro de que su configuración es correcta para golpear y sobrescribir text.txtsi ya existe?
No puedo entender por qué su constructor está escrito de esa manera, pero no esperaría que se establezca myFilename global. Intente darle un nombre diferente en el constructor y cópielo en el archivo global.
huele a condición de carrera, que pasa si pones delay(1000); después myFile = SD.open(myFilename, FILE_WRITE);en tu segundo código?
No conozco bien C++ (¡los compiladores de Ada le dicen exactamente qué es lo que está mal la mayor parte del tiempo!) pero sospecho que el segundo constructor no inicializó myFile: ¿puede inicializarlo con un valor conocido de "sin archivo"? ?
Es posible que desee preguntar en StackOverflow, pero el código me parece bien. Tiene que haber algo que no nos estás mostrando...
@vicatcu - Tu truco parece funcionar... ¿puedes decir algo más sobre las condiciones de carrera y cómo y por qué se pueden/deben evitar?
¿Cómo es esto un problema de ingeniería eléctrica? Esto parece un problema de software de alto nivel puro.

Respuestas (1)

Un problema común (y difícil de aislar) que a veces sucede en el desarrollo de sistemas integrados es que algo que funcionó anteriormente deja de funcionar después de algún cambio aparentemente inocuo en el software. Hay muchas razones por las que esto puede suceder, pero las dos más comunes son:

  • Desbordamiento de pila (he escrito sobre esto extensamente )
  • Suposiciones/dependencias de tiempo no documentadas (o no realizadas)

El desbordamiento de la pila generalmente es el resultado de tratar de exprimir demasiado en su chip y no darle al código suficiente espacio para respirar. También puede ser el resultado de errores de software desagradables que involucran punteros que conducen a pisotear la memoria que no esperaba alterar.

Las dependencias de tiempo son mucho más complicadas. Estos a menudo asoman la cabeza cuando está usando "el código de otra persona" y el patrón de uso en el que se basa el código no está bien documentado. Si realmente comprende los periféricos con los que está trabajando, puede averiguarlo investigando el código de esa persona o utilizando un instrumento para realizar mediciones directas en las señales. Enterrado en alguna hoja de datos en algún lugar, probablemente diga que no haga X dentro de Y milisegundos de hacer Z. En casos como este, hay un contrato implícito (bucle abierto) entre el controlador y el periférico para dar tiempo a las cosas para hacer lo que sea antes de interactuar con de nuevo

Una API debería aclarar el tipo de dependencia, pero los mendigos no pueden elegir, como dicen. Este es un desafío cuando se trata de Arduino, y el código funciona bien siempre que no se aleje demasiado de los ejemplos. Pero si no funciona en su contexto, debe estar preparado para comprender realmente lo que está sucediendo y ajustar las cosas en consecuencia.

Idealmente, la interfaz de un periférico debe escribirse de tal manera que sea "insensible a los retrasos" y use algún tipo de mecanismo de sondeo para controlar el progreso. El comportamiento del código como el que está viendo aquí es lo que me gusta llamar desarrollo "basado en la fe". Confiar en los comentarios de un dispositivo siempre es mejor que cruzar los dedos y seguir adelante.

Bien, tal vez entendí lo que quieres decir y estoy pensando que mi caso está relacionado con una "dependencia/suposición de tiempo no documentada (o no realizada)". Entonces, dado que me gustaría "realmente entender lo que está sucediendo y ajustar las cosas en consecuencia", ¿dónde puedo leer el código fuente de la SDbiblioteca Arduino?
En su directorio de instalación de arduino en /libraries/SD