<commit_before>/*

 SD - a slightly more friendly wrapper for sdfatlib

 This library aims to expose a subset of SD card functionality
 in the form of a higher level "wrapper" object.

 License: GNU General Public License V3
          (Because sdfatlib is licensed with this.)

 (C) Copyright 2010 SparkFun Electronics

 This library provides four key benefits:

   * Including `SD.h` automatically creates a global
     `SD` object which can be interacted with in a similar
     manner to other standard global objects like `Serial` and `Ethernet`.

   * Boilerplate initialisation code is contained in one method named
     `begin` and no further objects need to be created in order to access
     the SD card.

   * Calls to `open` can supply a full path name including parent
     directories which simplifies interacting with files in subdirectories.

   * Utility methods are provided to determine whether a file exists
     and to create a directory heirarchy.

  Note however that not all functionality provided by the underlying
  sdfatlib library is exposed.

 */

/*

  Implementation Notes

  In order to handle multi-directory path traversal, functionality that
  requires this ability is implemented as callback functions.

  Individual methods call the `walkPath` function which performs the actual
  directory traversal (swapping between two different directory/file handles
  along the way) and at each level calls the supplied callback function.

  Some types of functionality will take an action at each level (e.g. exists
  or make directory) which others will only take an action at the bottom
  level (e.g. open).

 */

#include "SD.h"

// Used by `getNextPathComponent`
#define MAX_COMPONENT_LEN 12 // What is max length?
#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1

bool getNextPathComponent(char *path, unsigned int *p_offset,
			  char *buffer) {
  /*

    Parse individual path components from a path.

      e.g. after repeated calls '/foo/bar/baz' will be split
           into 'foo', 'bar', 'baz'.

    This is similar to `strtok()` but copies the component into the
    supplied buffer rather than modifying the original string.

    `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.

    `p_offset` needs to point to an integer of the offset at
    which the previous path component finished.

    Returns `true` if more components remain.

    Returns `false` if this is the last component.
      (This means path ended with 'foo' or 'foo/'.)

   */

  // TODO: Have buffer local to this function, so we know it's the
  //       correct length?

  int bufferOffset = 0;

  int offset = *p_offset;

  // Skip root or other separator
  if (path[offset] == '/') {
    offset++;
  }

  // Copy the next next path segment
  while (bufferOffset < MAX_COMPONENT_LEN
	 && (path[offset] != '/')
	 && (path[offset] != '\0')) {
    buffer[bufferOffset++] = path[offset++];
  }

  buffer[bufferOffset] = '\0';

  // Skip trailing separator so we can determine if this
  // is the last component in the path or not.
  if (path[offset] == '/') {
    offset++;
  }

  *p_offset = offset;

  return (path[offset] != '\0');
}

boolean walkPath(char *filepath, SdFile& parentDir,
		 boolean (*callback)(SdFile& parentDir,
				     char *filePathComponent,
				     boolean isLastComponent,
				     void *object),
		 void *object = NULL) {
  /*

     When given a file path (and parent directory--normally root),
     this function traverses the directories in the path and at each
     level calls the supplied callback function while also providing
     the supplied object for context if required.

       e.g. given the path '/foo/bar/baz'
            the callback would be called at the equivalent of
	    '/foo', '/foo/bar' and '/foo/bar/baz'.

     The implementation swaps between two different directory/file
     handles as it traverses the directories and does not use recursion
     in an attempt to use memory efficiently.

     If a callback wishes to stop the directory traversal it should
     return false--in this case the function will stop the traversal,
     tidy up and return false.

     If a directory path doesn't exist at some point this function will
     also return false and not subsequently call the callback.

     If a directory path specified is complete, valid and the callback
     did not indicate the traversal should be interrupted then this
     function will return true.

   */

  SdFile subfile1;
  SdFile subfile2;

  char buffer[PATH_COMPONENT_BUFFER_LEN];

  unsigned int offset = 0;

  SdFile *p_parent;
  SdFile *p_child;

  SdFile *p_tmp_sdfile;

  p_child = &subfile1;

  p_parent = &parentDir;

  while (true) {

    boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);

    boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);

    if (!shouldContinue) {
      // TODO: Don't repeat this code?
      // If it's one we've created then we
      // don't need the parent handle anymore.
      if (p_parent != &parentDir) {
        (*p_parent).close();
      }
      return false;
    }

    if (!moreComponents) {
      break;
    }

    boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);

    // If it's one we've created then we
    // don't need the parent handle anymore.
    if (p_parent != &parentDir) {
      (*p_parent).close();
    }

    // Handle case when it doesn't exist and we can't continue...
    if (exists) {
      // We alternate between two file handles as we go down
      // the path.
      if (p_parent == &parentDir) {
        p_parent = &subfile2;
      }

      p_tmp_sdfile = p_parent;
      p_parent = p_child;
      p_child = p_tmp_sdfile;
    } else {
      return false;
    }
  }

  if (p_parent != &parentDir) {
    (*p_parent).close(); // TODO: Return/ handle different?
  }

  return true;
}

/*

   The callbacks used to implement various functionality follow.

   Each callback is supplied with a parent directory handle,
   character string with the name of the current file path component,
   a flag indicating if this component is the last in the path and
   a pointer to an arbitrary object used for context.

 */

boolean callback_pathExists(SdFile& parentDir, char *filePathComponent,
			    boolean isLastComponent, void *object) {
  /*

    Callback used to determine if a file/directory exists in parent
    directory.

    Returns true if file path exists.

  */
  SdFile child;

  boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);

  if (exists) {
     child.close();
  }

  return exists;
}

boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
			     boolean isLastComponent, void *object) {
  /*

    Callback used to create a directory in the parent directory if
    it does not already exist.

    Returns true if a directory was created or it already existed.

  */
  boolean result = false;
  SdFile child;

  result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
  if (!result) {
    result = child.makeDir(parentDir, filePathComponent);
  }

  return result;
}

  /*

boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
			  boolean isLastComponent, void *object) {

    Callback used to open a file specified by a filepath that may
    specify one or more directories above it.

    Expects the context object to be an instance of `SDClass` and
    will use the `file` property of the instance to open the requested
    file/directory with the associated file open mode property.

    Always returns true if the directory traversal hasn't reached the
    bottom of the directory heirarchy.

    Returns false once the file has been opened--to prevent the traversal
    from descending further. (This may be unnecessary.)

  if (isLastComponent) {
    SDClass *p_SD = static_cast<SDClass*>(object);
    p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
    if (p_SD->fileOpenMode == FILE_WRITE) {
      p_SD->file.seekSet(p_SD->file.fileSize());
    }
    // TODO: Return file open result?
    return false;
  }
  return true;
}
  */

boolean callback_remove(SdFile& parentDir, char *filePathComponent,
			boolean isLastComponent, void *object) {
  if (isLastComponent) {
    return SdFile::remove(parentDir, filePathComponent);
  }
  return true;
}

boolean callback_rmdir(SdFile& parentDir, char *filePathComponent,
			boolean isLastComponent, void *object) {
  if (isLastComponent) {
    SdFile f;
    if (!f.open(parentDir, filePathComponent, O_READ)) return false;
    return f.rmDir();
  }
  return true;
}

/* Implementation of class used to create `SDCard` object. */

boolean SDClass::begin(uint8_t csPin) {
  /*

    Performs the initialisation required by the sdfatlib library.

    Return true if initialization succeeds, false otherwise.

   */
  return card.init(SPI_HALF_SPEED, csPin) &&
         volume.init(card) &&
         root.openRoot(volume);
}

// this little helper is used to traverse paths
SdFile SDClass::getParentDir(char *filepath, int *index) {
  // get parent directory
  SdFile d1 = root; // start with the mostparent, root!
  SdFile d2;

  // we'll use the pointers to swap between the two objects
  SdFile *parent = &d1;
  SdFile *subdir = &d2;

  char *origpath = filepath;

  while (strchr(filepath, '/')) {

    // get rid of leading /'s
    if (filepath[0] == '/') {
      filepath++;
      continue;
    }

    if (! strchr(filepath, '/')) {
      // it was in the root directory, so leave now
      break;
    }

    // extract just the name of the next subdirectory
    uint8_t idx = strchr(filepath, '/') - filepath;
    if (idx > 12)
      idx = 12;    // dont let them specify long names
    char subdirname[13];
    strncpy(subdirname, filepath, idx);
    subdirname[idx] = 0;

    // close the subdir (we reuse them) if open
    subdir->close();
    if (! subdir->open(parent, subdirname, O_READ)) {
      // failed to open one of the subdirectories
      return SdFile();
    }
    // move forward to the next subdirectory
    filepath += idx;

    // we reuse the objects, close it.
    parent->close();

    // swap the pointers
    SdFile *t = parent;
    parent = subdir;
    subdir = t;
  }

  *index = (int)(filepath - origpath);
  // parent is now the parent diretory of the file!
  return *parent;
}

File SDClass::open(char *filepath, uint8_t mode) {
  /*

     Open the supplied file path for reading or writing.

     The file content can be accessed via the `file` property of
     the `SDClass` object--this property is currently
     a standard `SdFile` object from `sdfatlib`.

     Defaults to read only.

     If `write` is true, default action (when `append` is true) is to
     append data to the end of the file.

     If `append` is false then the file will be truncated first.

     If the file does not exist and it is opened for writing the file
     will be created.

     An attempt to open a file for reading that does not exist is an
     error.

   */

  int pathidx;

  // do the interative search
  SdFile parentdir = getParentDir(filepath, &pathidx);
  // no more subdirs!

  filepath += pathidx;

  if (! filepath[0]) {
    // it was the directory itself!
    return File(parentdir, "/");
  }

  // Open the file itself
  SdFile file;

  if (!parentdir.isOpen() || ! file.open(parentdir, filepath, mode)) {
    // failed to open the final file itself or one of the subdirs
    return File();
  }
  parentdir.close();

  if (mode & (O_APPEND | O_WRITE))
    file.seekSet(file.fileSize());
  return File(file, filepath);
}

/*
File SDClass::open(char *filepath, uint8_t mode) {
  //

     Open the supplied file path for reading or writing.

     The file content can be accessed via the `file` property of
     the `SDClass` object--this property is currently
     a standard `SdFile` object from `sdfatlib`.

     Defaults to read only.

     If `write` is true, default action (when `append` is true) is to
     append data to the end of the file.

     If `append` is false then the file will be truncated first.

     If the file does not exist and it is opened for writing the file
     will be created.

     An attempt to open a file for reading that does not exist is an
     error.

   //

  // TODO: Allow for read&write? (Possibly not, as it requires seek.)

  fileOpenMode = mode;
  walkPath(filepath, root, callback_openPath, this);

  return File();

}
*/

//boolean SDClass::close() {
//  /*
//
//    Closes the file opened by the `open` method.
//
//   */
//  file.close();
//}

boolean SDClass::exists(char *filepath) {
  /*

     Returns true if the supplied file path exists.

   */
  return walkPath(filepath, root, callback_pathExists);
}

//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
//  /*
//
//     Returns true if the supplied file path rooted at `parentDir`
//     exists.
//
//   */
//  return walkPath(filepath, parentDir, callback_pathExists);
//}

boolean SDClass::mkdir(char *filepath) {
  /*

    Makes a single directory or a heirarchy of directories.

    A rough equivalent to `mkdir -p`.

   */
  return walkPath(filepath, root, callback_makeDirPath);
}

boolean SDClass::rmdir(char *filepath) {
  /*

    Makes a single directory or a heirarchy of directories.

    A rough equivalent to `mkdir -p`.

   */
  return walkPath(filepath, root, callback_rmdir);
}

boolean SDClass::remove(char *filepath) {
  return walkPath(filepath, root, callback_remove);
}

// allows you to recurse into a directory
File File::openNextFile(uint8_t mode) {
  dir_t p;

  //Serial.print("\t\treading dir...");
  while (_file->readDir(&p) > 0) {

    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) {
      //Serial.println("end");
      return File();
    }

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
      //Serial.println("dots");
      continue;
    }

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
      //Serial.println("notafile");
      continue;
    }

    // print file name with possible blank fill
    SdFile f;
    char name[12];
    _file->dirName(p, name);
    //Serial.print("try to open file ");
    //Serial.println(name);

    if (f.open(_file, name, mode)) {
      //Serial.println("OK!");
      return File(f, name);
    } else {
      //Serial.println("ugh");
      return File();
    }
  }

  //Serial.println("nothing");
  return File();
}

void File::rewindDirectory(void) {
  if (isDirectory())
    _file->rewind();
}

SDClass SD;
<commit_msg>Fixed possibly buggy case of opened file being in root<commit_after>/*

 SD - a slightly more friendly wrapper for sdfatlib

 This library aims to expose a subset of SD card functionality
 in the form of a higher level "wrapper" object.

 License: GNU General Public License V3
          (Because sdfatlib is licensed with this.)

 (C) Copyright 2010 SparkFun Electronics

 This library provides four key benefits:

   * Including `SD.h` automatically creates a global
     `SD` object which can be interacted with in a similar
     manner to other standard global objects like `Serial` and `Ethernet`.

   * Boilerplate initialisation code is contained in one method named
     `begin` and no further objects need to be created in order to access
     the SD card.

   * Calls to `open` can supply a full path name including parent
     directories which simplifies interacting with files in subdirectories.

   * Utility methods are provided to determine whether a file exists
     and to create a directory heirarchy.

  Note however that not all functionality provided by the underlying
  sdfatlib library is exposed.

 */

/*

  Implementation Notes

  In order to handle multi-directory path traversal, functionality that
  requires this ability is implemented as callback functions.

  Individual methods call the `walkPath` function which performs the actual
  directory traversal (swapping between two different directory/file handles
  along the way) and at each level calls the supplied callback function.

  Some types of functionality will take an action at each level (e.g. exists
  or make directory) which others will only take an action at the bottom
  level (e.g. open).

 */

#include "SD.h"

// Used by `getNextPathComponent`
#define MAX_COMPONENT_LEN 12 // What is max length?
#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1

bool getNextPathComponent(char *path, unsigned int *p_offset,
			  char *buffer) {
  /*

    Parse individual path components from a path.

      e.g. after repeated calls '/foo/bar/baz' will be split
           into 'foo', 'bar', 'baz'.

    This is similar to `strtok()` but copies the component into the
    supplied buffer rather than modifying the original string.

    `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.

    `p_offset` needs to point to an integer of the offset at
    which the previous path component finished.

    Returns `true` if more components remain.

    Returns `false` if this is the last component.
      (This means path ended with 'foo' or 'foo/'.)

   */

  // TODO: Have buffer local to this function, so we know it's the
  //       correct length?

  int bufferOffset = 0;

  int offset = *p_offset;

  // Skip root or other separator
  if (path[offset] == '/') {
    offset++;
  }

  // Copy the next next path segment
  while (bufferOffset < MAX_COMPONENT_LEN
	 && (path[offset] != '/')
	 && (path[offset] != '\0')) {
    buffer[bufferOffset++] = path[offset++];
  }

  buffer[bufferOffset] = '\0';

  // Skip trailing separator so we can determine if this
  // is the last component in the path or not.
  if (path[offset] == '/') {
    offset++;
  }

  *p_offset = offset;

  return (path[offset] != '\0');
}

boolean walkPath(char *filepath, SdFile& parentDir,
		 boolean (*callback)(SdFile& parentDir,
				     char *filePathComponent,
				     boolean isLastComponent,
				     void *object),
		 void *object = NULL) {
  /*

     When given a file path (and parent directory--normally root),
     this function traverses the directories in the path and at each
     level calls the supplied callback function while also providing
     the supplied object for context if required.

       e.g. given the path '/foo/bar/baz'
            the callback would be called at the equivalent of
	    '/foo', '/foo/bar' and '/foo/bar/baz'.

     The implementation swaps between two different directory/file
     handles as it traverses the directories and does not use recursion
     in an attempt to use memory efficiently.

     If a callback wishes to stop the directory traversal it should
     return false--in this case the function will stop the traversal,
     tidy up and return false.

     If a directory path doesn't exist at some point this function will
     also return false and not subsequently call the callback.

     If a directory path specified is complete, valid and the callback
     did not indicate the traversal should be interrupted then this
     function will return true.

   */

  SdFile subfile1;
  SdFile subfile2;

  char buffer[PATH_COMPONENT_BUFFER_LEN];

  unsigned int offset = 0;

  SdFile *p_parent;
  SdFile *p_child;

  SdFile *p_tmp_sdfile;

  p_child = &subfile1;

  p_parent = &parentDir;

  while (true) {

    boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);

    boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);

    if (!shouldContinue) {
      // TODO: Don't repeat this code?
      // If it's one we've created then we
      // don't need the parent handle anymore.
      if (p_parent != &parentDir) {
        (*p_parent).close();
      }
      return false;
    }

    if (!moreComponents) {
      break;
    }

    boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);

    // If it's one we've created then we
    // don't need the parent handle anymore.
    if (p_parent != &parentDir) {
      (*p_parent).close();
    }

    // Handle case when it doesn't exist and we can't continue...
    if (exists) {
      // We alternate between two file handles as we go down
      // the path.
      if (p_parent == &parentDir) {
        p_parent = &subfile2;
      }

      p_tmp_sdfile = p_parent;
      p_parent = p_child;
      p_child = p_tmp_sdfile;
    } else {
      return false;
    }
  }

  if (p_parent != &parentDir) {
    (*p_parent).close(); // TODO: Return/ handle different?
  }

  return true;
}

/*

   The callbacks used to implement various functionality follow.

   Each callback is supplied with a parent directory handle,
   character string with the name of the current file path component,
   a flag indicating if this component is the last in the path and
   a pointer to an arbitrary object used for context.

 */

boolean callback_pathExists(SdFile& parentDir, char *filePathComponent,
			    boolean isLastComponent, void *object) {
  /*

    Callback used to determine if a file/directory exists in parent
    directory.

    Returns true if file path exists.

  */
  SdFile child;

  boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);

  if (exists) {
     child.close();
  }

  return exists;
}

boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
			     boolean isLastComponent, void *object) {
  /*

    Callback used to create a directory in the parent directory if
    it does not already exist.

    Returns true if a directory was created or it already existed.

  */
  boolean result = false;
  SdFile child;

  result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
  if (!result) {
    result = child.makeDir(parentDir, filePathComponent);
  }

  return result;
}

  /*

boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
			  boolean isLastComponent, void *object) {

    Callback used to open a file specified by a filepath that may
    specify one or more directories above it.

    Expects the context object to be an instance of `SDClass` and
    will use the `file` property of the instance to open the requested
    file/directory with the associated file open mode property.

    Always returns true if the directory traversal hasn't reached the
    bottom of the directory heirarchy.

    Returns false once the file has been opened--to prevent the traversal
    from descending further. (This may be unnecessary.)

  if (isLastComponent) {
    SDClass *p_SD = static_cast<SDClass*>(object);
    p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
    if (p_SD->fileOpenMode == FILE_WRITE) {
      p_SD->file.seekSet(p_SD->file.fileSize());
    }
    // TODO: Return file open result?
    return false;
  }
  return true;
}
  */

boolean callback_remove(SdFile& parentDir, char *filePathComponent,
			boolean isLastComponent, void *object) {
  if (isLastComponent) {
    return SdFile::remove(parentDir, filePathComponent);
  }
  return true;
}

boolean callback_rmdir(SdFile& parentDir, char *filePathComponent,
			boolean isLastComponent, void *object) {
  if (isLastComponent) {
    SdFile f;
    if (!f.open(parentDir, filePathComponent, O_READ)) return false;
    return f.rmDir();
  }
  return true;
}

/* Implementation of class used to create `SDCard` object. */

boolean SDClass::begin(uint8_t csPin) {
  /*

    Performs the initialisation required by the sdfatlib library.

    Return true if initialization succeeds, false otherwise.

   */
  return card.init(SPI_HALF_SPEED, csPin) &&
         volume.init(card) &&
         root.openRoot(volume);
}

// this little helper is used to traverse paths
SdFile SDClass::getParentDir(char *filepath, int *index) {
  // get parent directory
  SdFile d1 = root; // start with the mostparent, root!
  SdFile d2;

  // we'll use the pointers to swap between the two objects
  SdFile *parent = &d1;
  SdFile *subdir = &d2;

  char *origpath = filepath;

  while (strchr(filepath, '/')) {

    // get rid of leading /'s
    if (filepath[0] == '/') {
      filepath++;
      continue;
    }

    if (! strchr(filepath, '/')) {
      // it was in the root directory, so leave now
      break;
    }

    // extract just the name of the next subdirectory
    uint8_t idx = strchr(filepath, '/') - filepath;
    if (idx > 12)
      idx = 12;    // dont let them specify long names
    char subdirname[13];
    strncpy(subdirname, filepath, idx);
    subdirname[idx] = 0;

    // close the subdir (we reuse them) if open
    subdir->close();
    if (! subdir->open(parent, subdirname, O_READ)) {
      // failed to open one of the subdirectories
      return SdFile();
    }
    // move forward to the next subdirectory
    filepath += idx;

    // we reuse the objects, close it.
    parent->close();

    // swap the pointers
    SdFile *t = parent;
    parent = subdir;
    subdir = t;
  }

  *index = (int)(filepath - origpath);
  // parent is now the parent diretory of the file!
  return *parent;
}

File SDClass::open(char *filepath, uint8_t mode) {
  /*

     Open the supplied file path for reading or writing.

     The file content can be accessed via the `file` property of
     the `SDClass` object--this property is currently
     a standard `SdFile` object from `sdfatlib`.

     Defaults to read only.

     If `write` is true, default action (when `append` is true) is to
     append data to the end of the file.

     If `append` is false then the file will be truncated first.

     If the file does not exist and it is opened for writing the file
     will be created.

     An attempt to open a file for reading that does not exist is an
     error.

   */

  int pathidx;

  // do the interative search
  SdFile parentdir = getParentDir(filepath, &pathidx);
  // no more subdirs!

  filepath += pathidx;

  if (! filepath[0]) {
    // it was the directory itself!
    return File(parentdir, "/");
  }

  // Open the file itself
  SdFile file;

  // failed to open a subdir!
  if (!parentdir.isOpen())
    return File();

  // there is a special case for the Root directory since its a static dir
  if (parentdir.isRoot()) {
    if ( ! file.open(SD.root, filepath, mode)) {
      // failed to open the file :(
      return File();
    }
    // dont close the root!
  } else {
    if ( ! file.open(parentdir, filepath, mode)) {
      return File();
    }
    // close the parent
    parentdir.close();
  }

  if (mode & (O_APPEND | O_WRITE))
    file.seekSet(file.fileSize());
  return File(file, filepath);
}

/*
File SDClass::open(char *filepath, uint8_t mode) {
  //

     Open the supplied file path for reading or writing.

     The file content can be accessed via the `file` property of
     the `SDClass` object--this property is currently
     a standard `SdFile` object from `sdfatlib`.

     Defaults to read only.

     If `write` is true, default action (when `append` is true) is to
     append data to the end of the file.

     If `append` is false then the file will be truncated first.

     If the file does not exist and it is opened for writing the file
     will be created.

     An attempt to open a file for reading that does not exist is an
     error.

   //

  // TODO: Allow for read&write? (Possibly not, as it requires seek.)

  fileOpenMode = mode;
  walkPath(filepath, root, callback_openPath, this);

  return File();

}
*/

//boolean SDClass::close() {
//  /*
//
//    Closes the file opened by the `open` method.
//
//   */
//  file.close();
//}

boolean SDClass::exists(char *filepath) {
  /*

     Returns true if the supplied file path exists.

   */
  return walkPath(filepath, root, callback_pathExists);
}

//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
//  /*
//
//     Returns true if the supplied file path rooted at `parentDir`
//     exists.
//
//   */
//  return walkPath(filepath, parentDir, callback_pathExists);
//}

boolean SDClass::mkdir(char *filepath) {
  /*

    Makes a single directory or a heirarchy of directories.

    A rough equivalent to `mkdir -p`.

   */
  return walkPath(filepath, root, callback_makeDirPath);
}

boolean SDClass::rmdir(char *filepath) {
  /*

    Makes a single directory or a heirarchy of directories.

    A rough equivalent to `mkdir -p`.

   */
  return walkPath(filepath, root, callback_rmdir);
}

boolean SDClass::remove(char *filepath) {
  return walkPath(filepath, root, callback_remove);
}

// allows you to recurse into a directory
File File::openNextFile(uint8_t mode) {
  dir_t p;

  //Serial.print("\t\treading dir...");
  while (_file->readDir(&p) > 0) {

    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) {
      //Serial.println("end");
      return File();
    }

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
      //Serial.println("dots");
      continue;
    }

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
      //Serial.println("notafile");
      continue;
    }

    // print file name with possible blank fill
    SdFile f;
    char name[13];
    _file->dirName(p, name);
    //Serial.print("try to open file ");
    //Serial.println(name);

    if (f.open(_file, name, mode)) {
      //Serial.println("OK!");
      return File(f, name);
    } else {
      //Serial.println("ugh");
      return File();
    }
  }

  //Serial.println("nothing");
  return File();
}

void File::rewindDirectory(void) {
  if (isDirectory())
    _file->rewind();
}

SDClass SD;
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include "../src/awg.hpp"

#include "../src/pulseTrain.hpp"

TEST(AwgClass, ComPort) {
	awg testAwg;

	EXPECT_EQ(std::string("COM1"),testAwg.comPort());

	testAwg.comPort(2);
	EXPECT_EQ(std::string("COM2"),testAwg.comPort());

	testAwg.comPort("com1");
	EXPECT_EQ(std::string("com1"),testAwg.comPort());
}

#define S(s) s, sizeof s - 1

TEST(AwgClass, ProgrammingString) {
	freqPulse	testPulse(100, 0.0, 5.0, 0, true);
	pulseTrain	testTrain(0.0);
	testTrain.pushPulse(testPulse);

	awg testAwg(testTrain, 19200, 1000.0, "COM42", "testName.txt");
	testAwg.startPulseLength(1.0);
	const std::string expectedProgramming(S("DATA:DESTINATION \"testName.txt\"\nDATA:WIDTH 1\nCURVE #15\x7F\x7F\x7F\x7F\x7F\nMARKER:DATA #15\x2\0\0\0\0\nCLOCK:FREQUENCY 1000.000000MHz\nCH1:WAVEFORM \"testName.txt\"\n"));
	EXPECT_EQ(expectedProgramming, testAwg.programmingString());

	const std::string expectedProgramming2(S("DATA:DESTINATION \"testName.txt\"\nDATA:WIDTH 1\nCURVE #15\x7F\x7F\x7F\x7F\x7F\nMARKER:DATA #15\x2\2\2\0\0\nCLOCK:FREQUENCY 1000.000000MHz\nCH1:WAVEFORM \"testName.txt\"\n"));
	testAwg.startPulseLength(2.7);
	EXPECT_EQ(expectedProgramming2, testAwg.programmingString());

}

int main(int argc, char * argv[] ) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
<commit_msg>Test: expose not padding to 32 sample multiples<commit_after>#include <gtest/gtest.h>

#include "../src/awg.hpp"

#include "../src/pulseTrain.hpp"
#include <sstream>
unsigned int getReportedSampleCount(std::string theString) {
	unsigned int firstDigit = theString.find_first_of('#')+2;
	std::string subString(theString.substr(firstDigit, 10));
	std::istringstream toRead(subString);
	unsigned int theCount;
	toRead >> theCount;
	return theCount;
}

TEST(AwgClass, ComPort) {
	awg testAwg;

	EXPECT_EQ(std::string("COM1"),testAwg.comPort());

	testAwg.comPort(2);
	EXPECT_EQ(std::string("COM2"),testAwg.comPort());

	testAwg.comPort("com1");
	EXPECT_EQ(std::string("com1"),testAwg.comPort());
}

#define S(s) s, sizeof s - 1

TEST(AwgClass, ProgrammingString) {
	freqPulse	testPulse(62.5, 0.0, 16.0, 0, true);
	pulseTrain	testTrain(0.0);
	testTrain.pushPulse(testPulse);

	awg testAwg(testTrain, 19200, 1000.0, "COM42", "testName.txt");
	testAwg.startPulseLength(1.0);
	const std::string expectedProgramming(S("DATA:DESTINATION \"testName.txt\"\nDATA:WIDTH 1\nCURVE #232\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\nMARKER:DATA #232\x2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\nCLOCK:FREQUENCY 1000.000000MHz\nCH1:WAVEFORM \"testName.txt\"\n"));
	EXPECT_EQ(expectedProgramming, testAwg.programmingString());

	EXPECT_EQ(0, getReportedSampleCount(testAwg.programmingString())%32);

	const std::string expectedProgramming2(S("DATA:DESTINATION \"testName.txt\"\nDATA:WIDTH 1\nCURVE #232\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F\nMARKER:DATA #232\x2\2\2\0\0\0\0\0\0\0\0\0\0\0\0\0\x2\2\2\0\0\0\0\0\0\0\0\0\0\0\0\0\nCLOCK:FREQUENCY 1000.000000MHz\nCH1:WAVEFORM \"testName.txt\"\n"));
	testAwg.startPulseLength(2.7);
	EXPECT_EQ(expectedProgramming2, testAwg.programmingString());
	EXPECT_EQ(0, getReportedSampleCount(testAwg.programmingString())%32);

}

int main(int argc, char * argv[] ) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/* mbed Microcontroller Library
 * Copyright (c) 2017 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include <limits.h>
#include "mbed.h"
#include "mbed_lp_ticker_wrapper.h"
#include "../sleep/sleep_test_utils.h"
#include "sleep_manager_api_tests.h"

#if !DEVICE_SLEEP
#error [NOT_SUPPORTED] test not supported
#endif

#define SLEEP_DURATION_US 20000ULL
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 500

using utest::v1::Case;
using utest::v1::Specification;
using utest::v1::Harness;

#if DEVICE_LPTICKER
/* Make sure there are enough ticks to cope with more than SLEEP_DURATION_US sleep
 * without hitting the wrap-around.
 */
void wraparound_lp_protect(void)
{
    const uint32_t ticks_now = get_lp_ticker_data()->interface->read();
    const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();

    const uint32_t max_count = ((1 << p_ticker_info->bits) - 1);
    const uint32_t delta_ticks = us_to_ticks(SLEEP_DURATION_US * 1.5, p_ticker_info->frequency);

    if (ticks_now < (max_count - delta_ticks)) {
        return;
    }

    while (get_lp_ticker_data()->interface->read() > (max_count - delta_ticks));
}
#endif

void test_lock_unlock()
{
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());

    sleep_manager_lock_deep_sleep();
    TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());

    sleep_manager_unlock_deep_sleep();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
}

void test_lock_eq_ushrt_max()
{
    uint32_t lock_count = 0;
    while (lock_count < USHRT_MAX) {
        sleep_manager_lock_deep_sleep();
        lock_count++;
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
    }
    while (lock_count > 1) {
        sleep_manager_unlock_deep_sleep();
        lock_count--;
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
    }
    sleep_manager_unlock_deep_sleep();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
}

utest::v1::status_t testcase_setup(const Case *const source, const size_t index_of_case)
{
    // Suspend the RTOS kernel scheduler to prevent interference with duration of sleep.
    osKernelSuspend();

#if DEVICE_LPTICKER
#if (LPTICKER_DELAY_TICKS > 0)
    // Suspend the low power ticker wrapper to prevent interference with deep sleep lock.
    lp_ticker_wrapper_suspend();
#endif
    lp_ticker_init();
#endif
#if DEVICE_USTICKER
    // Make sure HAL tickers are initialized.
    us_ticker_init();
#endif

    return utest::v1::greentea_case_setup_handler(source, index_of_case);
}

utest::v1::status_t testcase_teardown(const Case *const source, const size_t passed, const size_t failed,
                                      const utest::v1::failure_t failure)
{
#if DEVICE_LPTICKER
#if (LPTICKER_DELAY_TICKS > 0)
    lp_ticker_wrapper_resume();
#endif
#endif
    osKernelResume(0);
    return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
}

#if DEVICE_LPTICKER
#if DEVICE_USTICKER
/* This test is based on the fact that the high-speed clocks are turned off
 * in deep sleep mode but remain on in the ordinary sleep mode. Low-speed
 * clocks stay on for both sleep and deep sleep modes.
 *
 * The type of sleep that was actually used by sleep_manager_sleep_auto()
 * can be detected by comparing times measured by us and lp tickers.
 */
void test_sleep_auto()
{
    const ticker_info_t *us_ticker_info = get_us_ticker_data()->interface->get_info();
    const unsigned us_ticker_mask = ((1 << us_ticker_info->bits) - 1);
    const ticker_irq_handler_type us_ticker_irq_handler_org = set_us_ticker_irq_handler(us_ticker_isr);
    const ticker_info_t *lp_ticker_info = get_lp_ticker_data()->interface->get_info();
    const unsigned lp_ticker_mask = ((1 << lp_ticker_info->bits) - 1);
    const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr);
    us_timestamp_t us_ts1, us_ts2, lp_ts1, lp_ts2, us_diff1, us_diff2, lp_diff1, lp_diff2;

    /*  Let's avoid the Lp ticker wrap-around case */
    wraparound_lp_protect();
    uint32_t lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
    timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
    lp_ticker_set_interrupt(lp_wakeup_ts);

    /* Some targets may need an interrupt short time after LPTIM interrupt is
     * set and forbid deep_sleep during that period. Let this period pass  */
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());

    sleep_manager_lock_deep_sleep();

    us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);

    sleep_manager_sleep_auto();
    us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    us_diff1 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
    lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
    lp_diff1 = (lp_ts1 <= lp_ts2) ? (lp_ts2 - lp_ts1) : (lp_ticker_mask - lp_ts1 + lp_ts2 + 1);

    // Deep sleep locked -- ordinary sleep mode used:
    // * us_ticker powered ON,
    // * lp_ticker powered ON,
    // so both should increment equally.

    // Verify us and lp tickers incremented equally, with 10% tolerance.
    TEST_ASSERT_UINT64_WITHIN_MESSAGE(
        SLEEP_DURATION_US / 10ULL, lp_diff1, us_diff1,
        "Deep sleep mode locked, but still used");

    sleep_manager_unlock_deep_sleep();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());

    // Wait for hardware serial buffers to flush.
    busy_wait_ms(SERIAL_FLUSH_TIME_MS);

    /*  Let's avoid the Lp ticker wrap-around case */
    wraparound_lp_protect();
    lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
    lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
    lp_ticker_set_interrupt(lp_wakeup_ts);

    /* Some targets may need an interrupt short time after LPTIM interrupt is
     * set and forbid deep_sleep during that period. Let this period pass  */
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());

    us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);

    sleep_manager_sleep_auto();

    us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    us_diff2 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
    lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
    lp_diff2 = (lp_ts1 <= lp_ts2) ? (lp_ts2 - lp_ts1) : (lp_ticker_mask - lp_ts1 + lp_ts2 + 1);

    // Deep sleep unlocked -- deep sleep mode used:
    // * us_ticker powered OFF,
    // * lp_ticker powered ON.
    // The us_ticker increment represents only the code execution time
    // and should be much shorter than both:
    // 1. current lp_ticker increment,
    // 2. previous us_ticker increment (locked sleep test above)

    // Verify that the current us_ticker increment:
    // 1. is at most 10% of lp_ticker increment
    // 2. is at most 10% of previous us_ticker increment.
    TEST_ASSERT_MESSAGE(us_diff2 < lp_diff2 / 10ULL, "Deep sleep mode unlocked, but not used");
    TEST_ASSERT_MESSAGE(us_diff2 < us_diff1 / 10ULL, "Deep sleep mode unlocked, but not used");

    set_us_ticker_irq_handler(us_ticker_irq_handler_org);
    set_lp_ticker_irq_handler(lp_ticker_irq_handler_org);
}
#endif

void test_lock_unlock_test_check()
{
    // Make sure HAL tickers are initialized.
    ticker_read(get_us_ticker_data());
    ticker_read(get_lp_ticker_data());

    // Use LowPowerTimer instead of Timer to prevent deep sleep lock.
    LowPowerTimer lp_timer;
    us_timestamp_t exec_time_unlocked, exec_time_locked;
    LowPowerTimeout lp_timeout;

    // Deep sleep unlocked:
    // * sleep_manager_can_deep_sleep() returns true,
    // * sleep_manager_can_deep_sleep_test_check() returns true instantly.
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
    lp_timer.start();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
    lp_timer.stop();
    exec_time_unlocked = lp_timer.read_high_resolution_us();

    // Deep sleep locked:
    // * sleep_manager_can_deep_sleep() returns false,
    // * sleep_manager_can_deep_sleep_test_check() returns false with 2 ms delay.
    sleep_manager_lock_deep_sleep();
    TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
    lp_timer.reset();
    lp_timer.start();
    TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep_test_check());
    lp_timer.stop();
    exec_time_locked = lp_timer.read_high_resolution_us();
    TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US,
                              exec_time_locked - exec_time_unlocked);

    // Deep sleep unlocked with a 1 ms delay:
    // * sleep_manager_can_deep_sleep() returns false,
    // * sleep_manager_can_deep_sleep_test_check() returns true with a 1 ms delay,
    // * sleep_manager_can_deep_sleep() returns true when checked again.
    lp_timer.reset();
    lp_timeout.attach_us(mbed::callback(sleep_manager_unlock_deep_sleep_internal),
                         DEEP_SLEEP_TEST_CHECK_WAIT_US / 2);
    lp_timer.start();
    TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
    lp_timer.stop();
    TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US / 2,
                              lp_timer.read_high_resolution_us());
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
}
#endif

utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
    GREENTEA_SETUP(10, "default_auto");
    return utest::v1::greentea_test_setup_handler(number_of_cases);
}

Case cases[] = {
    Case("deep sleep lock/unlock",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_lock_unlock,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
    Case("deep sleep locked USHRT_MAX times",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_lock_eq_ushrt_max,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
#if DEVICE_LPTICKER
#if DEVICE_USTICKER
    Case("sleep_auto calls sleep/deep sleep based on lock",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_sleep_auto,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
#endif
    Case("deep sleep lock/unlock test_check",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_lock_unlock_test_check,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
#endif
};

Specification specification(testsuite_setup, cases);

int main()
{
    return !Harness::run(specification);
}
<commit_msg>Further modifications in Sleep Manager test.<commit_after>/* mbed Microcontroller Library
 * Copyright (c) 2017 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include <limits.h>
#include "mbed.h"
#include "mbed_lp_ticker_wrapper.h"
#include "../sleep/sleep_test_utils.h"
#include "sleep_manager_api_tests.h"

#if !DEVICE_SLEEP
#error [NOT_SUPPORTED] test not supported
#endif

#define SLEEP_DURATION_US 20000ULL
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 600

using utest::v1::Case;
using utest::v1::Specification;
using utest::v1::Harness;

#if DEVICE_LPTICKER
/* Make sure there are enough ticks to cope with more than SLEEP_DURATION_US sleep
 * without hitting the wrap-around.
 */
void wraparound_lp_protect(void)
{
    const uint32_t ticks_now = get_lp_ticker_data()->interface->read();
    const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();

    const uint32_t max_count = ((1 << p_ticker_info->bits) - 1);
    const uint32_t delta_ticks = us_to_ticks(SLEEP_DURATION_US * 1.5, p_ticker_info->frequency);

    if (ticks_now < (max_count - delta_ticks)) {
        return;
    }

    while (get_lp_ticker_data()->interface->read() > (max_count - delta_ticks));
}
#endif

void test_lock_unlock()
{
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());

    sleep_manager_lock_deep_sleep();
    TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());

    sleep_manager_unlock_deep_sleep();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
}

void test_lock_eq_ushrt_max()
{
    uint32_t lock_count = 0;
    while (lock_count < USHRT_MAX) {
        sleep_manager_lock_deep_sleep();
        lock_count++;
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
    }
    while (lock_count > 1) {
        sleep_manager_unlock_deep_sleep();
        lock_count--;
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
    }
    sleep_manager_unlock_deep_sleep();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
}

utest::v1::status_t testcase_setup(const Case *const source, const size_t index_of_case)
{
    // Suspend the RTOS kernel scheduler to prevent interference with duration of sleep.
    osKernelSuspend();
#if DEVICE_LPTICKER
    ticker_suspend(get_lp_ticker_data());
#if (LPTICKER_DELAY_TICKS > 0)
    // Suspend the low power ticker wrapper to prevent interference with deep sleep lock.
    lp_ticker_wrapper_suspend();
#endif
#endif
    ticker_suspend(get_us_ticker_data());
    // Make sure HAL tickers are initialized.
    us_ticker_init();
#if DEVICE_LPTICKER
    lp_ticker_init();
#endif
    return utest::v1::greentea_case_setup_handler(source, index_of_case);
}

utest::v1::status_t testcase_teardown(const Case *const source, const size_t passed, const size_t failed,
                                      const utest::v1::failure_t failure)
{
    ticker_resume(get_us_ticker_data());
#if DEVICE_LPTICKER
#if (LPTICKER_DELAY_TICKS > 0)
    lp_ticker_wrapper_resume();
#endif
    ticker_resume(get_lp_ticker_data());
#endif
    osKernelResume(0);
    return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
}

#if DEVICE_LPTICKER
#if DEVICE_USTICKER
/* This test is based on the fact that the high-speed clocks are turned off
 * in deep sleep mode but remain on in the ordinary sleep mode. Low-speed
 * clocks stay on for both sleep and deep sleep modes.
 *
 * The type of sleep that was actually used by sleep_manager_sleep_auto()
 * can be detected by comparing times measured by us and lp tickers.
 */
void test_sleep_auto()
{
    const ticker_info_t *us_ticker_info = get_us_ticker_data()->interface->get_info();
    const unsigned us_ticker_mask = ((1 << us_ticker_info->bits) - 1);
    const ticker_irq_handler_type us_ticker_irq_handler_org = set_us_ticker_irq_handler(us_ticker_isr);
    const ticker_info_t *lp_ticker_info = get_lp_ticker_data()->interface->get_info();
    const unsigned lp_ticker_mask = ((1 << lp_ticker_info->bits) - 1);
    const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr);
    us_timestamp_t us_ts1, us_ts2, lp_ts1, lp_ts2, us_diff1, us_diff2, lp_diff1, lp_diff2;

    /*  Let's avoid the Lp ticker wrap-around case */
    wraparound_lp_protect();
    uint32_t lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
    timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
    lp_ticker_set_interrupt(lp_wakeup_ts);

    /* Some targets may need an interrupt short time after LPTIM interrupt is
     * set and forbid deep_sleep during that period. Let this period pass  */
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());

    sleep_manager_lock_deep_sleep();

    us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);

    sleep_manager_sleep_auto();
    us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    us_diff1 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
    lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
    lp_diff1 = (lp_ts1 <= lp_ts2) ? (lp_ts2 - lp_ts1) : (lp_ticker_mask - lp_ts1 + lp_ts2 + 1);

    // Deep sleep locked -- ordinary sleep mode used:
    // * us_ticker powered ON,
    // * lp_ticker powered ON,
    // so both should increment equally.

    // Verify us and lp tickers incremented equally, with 10% tolerance.
    TEST_ASSERT_UINT64_WITHIN_MESSAGE(
        SLEEP_DURATION_US / 10ULL, lp_diff1, us_diff1,
        "Deep sleep mode locked, but still used");

    sleep_manager_unlock_deep_sleep();
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());

    // Wait for hardware serial buffers to flush.
    busy_wait_ms(SERIAL_FLUSH_TIME_MS);

    /*  Let's avoid the Lp ticker wrap-around case */
    wraparound_lp_protect();
    lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
    lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
    lp_ticker_set_interrupt(lp_wakeup_ts);

    /* Some targets may need an interrupt short time after LPTIM interrupt is
     * set and forbid deep_sleep during that period. Let this period pass  */
    TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());

    us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);

    sleep_manager_sleep_auto();

    us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
    us_diff2 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
    lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
    lp_diff2 = (lp_ts1 <= lp_ts2) ? (lp_ts2 - lp_ts1) : (lp_ticker_mask - lp_ts1 + lp_ts2 + 1);

    // Deep sleep unlocked -- deep sleep mode used:
    // * us_ticker powered OFF,
    // * lp_ticker powered ON.
    // The us_ticker increment represents only the code execution time
    // and should be much shorter than both:
    // 1. current lp_ticker increment,
    // 2. previous us_ticker increment (locked sleep test above)

    // Verify that the current us_ticker increment:
    // 1. is at most 10% of lp_ticker increment
    // 2. is at most 10% of previous us_ticker increment.
    TEST_ASSERT_MESSAGE(us_diff2 < lp_diff2 / 10ULL, "Deep sleep mode unlocked, but not used");
    TEST_ASSERT_MESSAGE(us_diff2 < us_diff1 / 10ULL, "Deep sleep mode unlocked, but not used");

    set_us_ticker_irq_handler(us_ticker_irq_handler_org);
    set_lp_ticker_irq_handler(lp_ticker_irq_handler_org);
}
#endif

#define US_PER_S 1000000

uint32_t diff_us(uint32_t start_ticks, uint32_t stop_ticks, const ticker_info_t *info)
{
    uint32_t counter_mask = ((1 << info->bits) - 1);

    uint32_t diff_ticks = ((stop_ticks - start_ticks) & counter_mask);

    return (uint32_t)((uint64_t) diff_ticks * US_PER_S / info->frequency);
}

uint32_t us_to_ticks(uint32_t us, const ticker_info_t *info)
{
    return (uint32_t)((uint32_t) us * info->frequency / US_PER_S);
}

volatile bool unlock_deep_sleep = false;

void ticker_event_handler_stub(const ticker_data_t *const ticker)
{
    if (unlock_deep_sleep) {
        sleep_manager_unlock_deep_sleep_internal();
        unlock_deep_sleep = false;
    }
}

ticker_irq_handler_type prev_irq_handler;

void test_lock_unlock_test_check()
{
    prev_irq_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub);

    for (int i = 0; i < 1000; i++) {

        wraparound_lp_protect();

        const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();

        // Use LowPowerTimer instead of Timer to prevent deep sleep lock.
        us_timestamp_t exec_time_unlocked, exec_time_locked;
        uint32_t start, stop;

        // Deep sleep unlocked:
        // * sleep_manager_can_deep_sleep() returns true,
        // * sleep_manager_can_deep_sleep_test_check() returns true instantly.
        TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
        start = lp_ticker_read();
        TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
        stop = lp_ticker_read();
        exec_time_unlocked = diff_us(start, stop, p_ticker_info);

        // Deep sleep locked:
        // * sleep_manager_can_deep_sleep() returns false,
        // * sleep_manager_can_deep_sleep_test_check() returns false with 2 ms delay.
        sleep_manager_lock_deep_sleep();
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
        start = lp_ticker_read();
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep_test_check());
        stop = lp_ticker_read();
        exec_time_locked = diff_us(start, stop, p_ticker_info);
        TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US,
                                  exec_time_locked - exec_time_unlocked);

        // Deep sleep unlocked with a 1 ms delay:
        // * sleep_manager_can_deep_sleep() returns false,
        // * sleep_manager_can_deep_sleep_test_check() returns true with a 1 ms delay,
        // * sleep_manager_can_deep_sleep() returns true when checked again.
        unlock_deep_sleep = true;
        lp_ticker_set_interrupt(lp_ticker_read() + us_to_ticks(DEEP_SLEEP_TEST_CHECK_WAIT_US / 2, p_ticker_info));

        start = lp_ticker_read();
        // Extra wait after setting interrupt to handle CMPOK
        wait_ns(100000);
        TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
        TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
        stop = lp_ticker_read();
        TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US / 2,
                                  diff_us(start, stop, p_ticker_info));
        TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
    }

    set_lp_ticker_irq_handler(prev_irq_handler);
}
#endif

utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
    GREENTEA_SETUP(10, "default_auto");
    return utest::v1::greentea_test_setup_handler(number_of_cases);
}

Case cases[] = {
    Case("deep sleep lock/unlock",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_lock_unlock,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
    Case("deep sleep locked USHRT_MAX times",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_lock_eq_ushrt_max,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
#if DEVICE_LPTICKER
#if DEVICE_USTICKER
    Case("sleep_auto calls sleep/deep sleep based on lock",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_sleep_auto,
         (utest::v1::case_teardown_handler_t) testcase_teardown),
#endif
    Case("deep sleep lock/unlock test_check",
         (utest::v1::case_setup_handler_t) testcase_setup,
         test_lock_unlock_test_check,
         (utest::v1::case_teardown_handler_t) testcase_teardown)
#endif
};

Specification specification(testsuite_setup, cases);

int main()
{
    return !Harness::run(specification);
}
<|endoftext|>


<commit_before>#include "TURIPclientSPI.h"
#include <SPI.h>

cl_TURIPclientSPI TURIPclientSPI;

cl_TURIPclientSPI::cl_TURIPclientSPI(){
  // TURIPclient.addPeripheral(this);
  numOfSsPin = 0;
}

void cl_TURIPclientSPI::begin(){
  #ifdef ESP8266
  SPI.setFrequency(200000);
  SPI.setDataMode(SPI_MODE0);
  #else
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  #endif
  SPI.begin();
  TURIPclient.addTuripClientInterface(this);
}

void cl_TURIPclientSPI::addPin(int ssPin){
  pinMode(ssPin, OUTPUT);
  digitalWrite(ssPin, HIGH);
  ssPinList[numOfSsPin++] = ssPin;
}

void cl_TURIPclientSPI::scan(){
  uint64_t id;
  uint32_t model, serial;
  for(int i = 0; i < numOfSsPin; i++){
    if(!readByUseSS(ssPinList[i], TURIP_PORT_MODEL, TURIP_TYPE_INT32, &model)){
      readByUseSS(ssPinList[i], TURIP_PORT_SERIAL, TURIP_TYPE_INT32, &serial);
      id = (uint64_t)model << 32 | (uint64_t)serial;
      TURIPclient.devices[TURIPclient.numofDevices].id = id;
      TURIPclient.devices[TURIPclient.numofDevices].interface = this;
      TURIPclient.devices[TURIPclient.numofDevices].localAddr = ssPinList[i];
      TURIPclient.numofDevices++;
    }
  }
}

int cl_TURIPclientSPI::read(uint64_t id, uint8_t port, TURIPdataType type, void* data){
  int ss = -1;
  for(int i = 0; i < TURIPclient.numofDevices; i++){
    if(TURIPclient.devices[i].id == id){
      ss = TURIPclient.devices[i].localAddr;
    }
  }
  if(ss == -1){
    return TURIP_ERR;
  }
  return readByUseSS(ss, port, type, data);
}

int cl_TURIPclientSPI::readByUseSS(int ss, uint8_t port, TURIPdataType type, void* data){
  // Serial.println("cl_TURIPclientSPI::readByUseSS()");
  uint8_t status;
  uint16_t timer;
  int datasize;
  switch (type) {
    case TURIP_TYPE_BOOL:
    case TURIP_TYPE_INT8:
    case TURIP_TYPE_UINT8:
      datasize = 1;
      break;
    case TURIP_TYPE_INT16:
    case TURIP_TYPE_UINT16:
      datasize = 2;
      break;
    case TURIP_TYPE_INT32:
    case TURIP_TYPE_UINT32:
    case TURIP_TYPE_FLOAT:
      datasize = 4;
      break;
    case TURIP_TYPE_INT64:
    case TURIP_TYPE_UINT64:
    case TURIP_TYPE_DOUBLE:
      datasize = 8;
      break;
    default:
      datasize = 0;
  }

  digitalWrite(ss, LOW);

  delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);

  // 通信安定化処理
  for(int i = 0; 1; i++){
    if(SPI.transfer(0xff) == 0xff) break;
    if(i == TURIP_ON_SPI_CLIENT_TRANS_RETRY) return -1;
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
  }

  // データ要求コマンド送信
  SPI.transfer((uint8_t)port & 0x7f);

  // スレーブ側の応答を待機
  timer = 0;
  do {
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    status = (uint8_t)SPI.transfer(0xff);
    if(++timer == TURIP_ON_SPI_CLIENT_TRANS_RETRY){
      digitalWrite(ss, HIGH);
      return TURIP_ERR;
    }
  } while (status == 0xff);
  if((status & 0xf0) != 0x00) {
    digitalWrite(ss, HIGH);
    return TURIP_ERR;
  }

  for(unsigned int i = 0; i < datasize; i++){
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    *((char*)data + i) = SPI.transfer(0xff);
  }
  digitalWrite(ss, HIGH);
  return TURIP_OK;
}

int cl_TURIPclientSPI::write(uint64_t id, uint8_t port, TURIPdataType type, void* data){
  int ss = -1;
  for(int i = 0; i < TURIPclient.numofDevices; i++){
    if(TURIPclient.devices[i].id == id){
      ss = TURIPclient.devices[i].localAddr;
    }
  }
  if(ss == -1){
    return TURIP_ERR;
  }
  int datasize;
  uint8_t status;
  uint16_t timer;

  switch (type) {
    case TURIP_TYPE_BOOL:
    case TURIP_TYPE_INT8:
    case TURIP_TYPE_UINT8:
      datasize = 1;
      break;
    case TURIP_TYPE_INT16:
    case TURIP_TYPE_UINT16:
      datasize = 2;
      break;
    case TURIP_TYPE_INT32:
    case TURIP_TYPE_UINT32:
    case TURIP_TYPE_FLOAT:
      datasize = 4;
      break;
    case TURIP_TYPE_INT64:
    case TURIP_TYPE_UINT64:
    case TURIP_TYPE_DOUBLE:
      datasize = 8;
      break;
    default:
      datasize = 0;
  }

  if(ss == -1) return -1;

  digitalWrite(ss, LOW);

  delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);

  // 通信安定化処理
  for(int i = 0; 1; i++){
    if(SPI.transfer(0xff) == 0xff) break;
    if(i == TURIP_ON_SPI_CLIENT_TRANS_RETRY) return -1;
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
  }

  // コマンド送信
  SPI.transfer((uint8_t)port | 0x80);

  // データ送信
  for(unsigned int i = 0; i < datasize; i++){
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    SPI.transfer(*((char*)data + i));
  }

  // スレーブ側の応答を待機
  timer = 0;
  do {
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    status = (uint8_t)SPI.transfer(0xff);
    if(++timer == TURIP_ON_SPI_CLIENT_TRANS_RETRY){
      digitalWrite(ss, HIGH);
      return TURIP_ERR;
    }
  } while (status == 0xff);
  if((status & 0xf0) != 0x00) {
    digitalWrite(ss, HIGH);
    return TURIP_ERR;
  }

  digitalWrite(ss, HIGH);
  return TURIP_OK;
}

TURIPdataType cl_TURIPclientSPI::getType(uint64_t id, uint8_t port){
  int ss = -1;
  for(int i = 0; i < TURIPclient.numofDevices; i++){
    if(TURIPclient.devices[i].id == id){
      ss = TURIPclient.devices[i].localAddr;
    }
  }
  if(ss == -1){
    return TURIP_TYPE_UNKNOWN;
  }

  uint8_t status;
  uint16_t timer;

  digitalWrite(ss, LOW);

  // 通信安定化処理
  for(int i = 0; 1; i++){
    if(SPI.transfer(0xff) == 0xff) break;
    if(i == TURIP_ON_SPI_CLIENT_TRANS_RETRY) return -1;
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
  }

  // データ要求コマンド送信
  SPI.transfer((uint8_t)port & 0x7f);

  // スレーブ側の応答を待機
  timer = 0;
  do {
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    status = (uint8_t)SPI.transfer(0xff);
    if(++timer == TURIP_ON_SPI_CLIENT_TRANS_RETRY){
      digitalWrite(ss, HIGH);
      return TURIP_TYPE_UNKNOWN;
    }
  } while (status == 0xff);

  if((status & 0xf0) != 0x00) {
    digitalWrite(ss, HIGH);
    return TURIP_TYPE_UNKNOWN;
  }

  digitalWrite(ss, HIGH);

  return (TURIPdataType)(status & 0x0f);
}
<commit_msg>Change TURIPclientSPI.begin() for esp32 modules<commit_after>#include "TURIPclientSPI.h"
#include <SPI.h>

cl_TURIPclientSPI TURIPclientSPI;

cl_TURIPclientSPI::cl_TURIPclientSPI(){
  // TURIPclient.addPeripheral(this);
  numOfSsPin = 0;
}

void cl_TURIPclientSPI::begin(){
  #ifdef ESP8266
  SPI.setFrequency(200000);
  SPI.setDataMode(SPI_MODE0);
  SPI.begin();
  #elif ESP32
  SPI.setFrequency(200000);
  SPI.setDataMode(SPI_MODE0);
  SPI.begin(17, 18, 16, 19); // Temporal setting for test device.
  #else
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  SPI.begin();
  #endif
  TURIPclient.addTuripClientInterface(this);
}

void cl_TURIPclientSPI::addPin(int ssPin){
  pinMode(ssPin, OUTPUT);
  digitalWrite(ssPin, HIGH);
  ssPinList[numOfSsPin++] = ssPin;
}

void cl_TURIPclientSPI::scan(){
  uint64_t id;
  uint32_t model, serial;
  for(int i = 0; i < numOfSsPin; i++){
    if(!readByUseSS(ssPinList[i], TURIP_PORT_MODEL, TURIP_TYPE_INT32, &model)){
      readByUseSS(ssPinList[i], TURIP_PORT_SERIAL, TURIP_TYPE_INT32, &serial);
      id = (uint64_t)model << 32 | (uint64_t)serial;
      TURIPclient.devices[TURIPclient.numofDevices].id = id;
      TURIPclient.devices[TURIPclient.numofDevices].interface = this;
      TURIPclient.devices[TURIPclient.numofDevices].localAddr = ssPinList[i];
      TURIPclient.numofDevices++;
    }
  }
}

int cl_TURIPclientSPI::read(uint64_t id, uint8_t port, TURIPdataType type, void* data){
  int ss = -1;
  for(int i = 0; i < TURIPclient.numofDevices; i++){
    if(TURIPclient.devices[i].id == id){
      ss = TURIPclient.devices[i].localAddr;
    }
  }
  if(ss == -1){
    return TURIP_ERR;
  }
  return readByUseSS(ss, port, type, data);
}

int cl_TURIPclientSPI::readByUseSS(int ss, uint8_t port, TURIPdataType type, void* data){
  // Serial.println("cl_TURIPclientSPI::readByUseSS()");
  uint8_t status;
  uint16_t timer;
  int datasize;
  switch (type) {
    case TURIP_TYPE_BOOL:
    case TURIP_TYPE_INT8:
    case TURIP_TYPE_UINT8:
      datasize = 1;
      break;
    case TURIP_TYPE_INT16:
    case TURIP_TYPE_UINT16:
      datasize = 2;
      break;
    case TURIP_TYPE_INT32:
    case TURIP_TYPE_UINT32:
    case TURIP_TYPE_FLOAT:
      datasize = 4;
      break;
    case TURIP_TYPE_INT64:
    case TURIP_TYPE_UINT64:
    case TURIP_TYPE_DOUBLE:
      datasize = 8;
      break;
    default:
      datasize = 0;
  }

  digitalWrite(ss, LOW);

  delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);

  // 通信安定化処理
  for(int i = 0; 1; i++){
    if(SPI.transfer(0xff) == 0xff) break;
    if(i == TURIP_ON_SPI_CLIENT_TRANS_RETRY) return -1;
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
  }

  // データ要求コマンド送信
  SPI.transfer((uint8_t)port & 0x7f);

  // スレーブ側の応答を待機
  timer = 0;
  do {
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    status = (uint8_t)SPI.transfer(0xff);
    if(++timer == TURIP_ON_SPI_CLIENT_TRANS_RETRY){
      digitalWrite(ss, HIGH);
      return TURIP_ERR;
    }
  } while (status == 0xff);
  if((status & 0xf0) != 0x00) {
    digitalWrite(ss, HIGH);
    return TURIP_ERR;
  }

  for(unsigned int i = 0; i < datasize; i++){
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    *((char*)data + i) = SPI.transfer(0xff);
  }
  digitalWrite(ss, HIGH);
  return TURIP_OK;
}

int cl_TURIPclientSPI::write(uint64_t id, uint8_t port, TURIPdataType type, void* data){
  int ss = -1;
  for(int i = 0; i < TURIPclient.numofDevices; i++){
    if(TURIPclient.devices[i].id == id){
      ss = TURIPclient.devices[i].localAddr;
    }
  }
  if(ss == -1){
    return TURIP_ERR;
  }
  int datasize;
  uint8_t status;
  uint16_t timer;

  switch (type) {
    case TURIP_TYPE_BOOL:
    case TURIP_TYPE_INT8:
    case TURIP_TYPE_UINT8:
      datasize = 1;
      break;
    case TURIP_TYPE_INT16:
    case TURIP_TYPE_UINT16:
      datasize = 2;
      break;
    case TURIP_TYPE_INT32:
    case TURIP_TYPE_UINT32:
    case TURIP_TYPE_FLOAT:
      datasize = 4;
      break;
    case TURIP_TYPE_INT64:
    case TURIP_TYPE_UINT64:
    case TURIP_TYPE_DOUBLE:
      datasize = 8;
      break;
    default:
      datasize = 0;
  }

  if(ss == -1) return -1;

  digitalWrite(ss, LOW);

  delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);

  // 通信安定化処理
  for(int i = 0; 1; i++){
    if(SPI.transfer(0xff) == 0xff) break;
    if(i == TURIP_ON_SPI_CLIENT_TRANS_RETRY) return -1;
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
  }

  // コマンド送信
  SPI.transfer((uint8_t)port | 0x80);

  // データ送信
  for(unsigned int i = 0; i < datasize; i++){
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    SPI.transfer(*((char*)data + i));
  }

  // スレーブ側の応答を待機
  timer = 0;
  do {
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    status = (uint8_t)SPI.transfer(0xff);
    if(++timer == TURIP_ON_SPI_CLIENT_TRANS_RETRY){
      digitalWrite(ss, HIGH);
      return TURIP_ERR;
    }
  } while (status == 0xff);
  if((status & 0xf0) != 0x00) {
    digitalWrite(ss, HIGH);
    return TURIP_ERR;
  }

  digitalWrite(ss, HIGH);
  return TURIP_OK;
}

TURIPdataType cl_TURIPclientSPI::getType(uint64_t id, uint8_t port){
  int ss = -1;
  for(int i = 0; i < TURIPclient.numofDevices; i++){
    if(TURIPclient.devices[i].id == id){
      ss = TURIPclient.devices[i].localAddr;
    }
  }
  if(ss == -1){
    return TURIP_TYPE_UNKNOWN;
  }

  uint8_t status;
  uint16_t timer;

  digitalWrite(ss, LOW);

  // 通信安定化処理
  for(int i = 0; 1; i++){
    if(SPI.transfer(0xff) == 0xff) break;
    if(i == TURIP_ON_SPI_CLIENT_TRANS_RETRY) return -1;
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
  }

  // データ要求コマンド送信
  SPI.transfer((uint8_t)port & 0x7f);

  // スレーブ側の応答を待機
  timer = 0;
  do {
    delayMicroseconds(TURIP_ON_SPI_CLIENT_TRANS_DELAY);
    status = (uint8_t)SPI.transfer(0xff);
    if(++timer == TURIP_ON_SPI_CLIENT_TRANS_RETRY){
      digitalWrite(ss, HIGH);
      return TURIP_TYPE_UNKNOWN;
    }
  } while (status == 0xff);

  if((status & 0xf0) != 0x00) {
    digitalWrite(ss, HIGH);
    return TURIP_TYPE_UNKNOWN;
  }

  digitalWrite(ss, HIGH);

  return (TURIPdataType)(status & 0x0f);
}
<|endoftext|>


<commit_before>// C++ program for implementation of Aho Corasick algorithm
// for string matching
#include <queue>
#include <string>
#include <cstring>
#include <iostream>

using namespace std;

// Max number of states in the matching machine.
// Should be equal to the sum of the length of all keywords.
const int MAXS = 100000;

// Maximum number of characters in input alphabet
const int MAXC = 26;

// OUTPUT FUNCTION IS IMPLEMENTED USING out[]
// Bit i in this mask is one if the word with index i
// appears when the machine enters this state.
int out[MAXS];

// FAILURE FUNCTION IS IMPLEMENTED USING f[]
int f[MAXS];

// GOTO FUNCTION (OR TRIE) IS IMPLEMENTED USING g[][]
int g[MAXS][MAXC];

// Builds the string matching machine.
// arr -   array of words. The index of each keyword is important:
//         "out[state] & (1 << i)" is > 0 if we just found word[i]
//         in the text.
// Returns the number of states that the built machine has.
// States are numbered 0 up to the return value - 1, inclusive.
int buildMatchingMachine(vector<string> arr, int k)
{
    // Initialize all values in output function as 0.
    memset(out, 0, sizeof out);

    // Initialize all values in goto function as -1.
    memset(g, -1, sizeof g);

    // Initially, we just have the 0 state
    int states = 1;

    // Construct values for goto function, i.e., fill g[][]
    // This is same as building a Trie for arr[]
    for (int i = 0; i < k; ++i)
    {
        const string &word = arr[i];
        int currentState = 0;

        // Insert all characters of current word in arr[]
        for (int j = 0; j < word.size(); ++j)
        {
            int ch = word[j] - 'a';

            // Allocate a new node (create a new state) if a
            // node for ch doesn't exist.
            if (g[currentState][ch] == -1)
                g[currentState][ch] = states++;

            currentState = g[currentState][ch];
        }

        // Add current word in output function
        out[currentState] |= (1 << i);
    }

    // For all characters which don't have an edge from
    // root (or state 0) in Trie, add a goto edge to state
    // 0 itself
    for (int ch = 0; ch < MAXC; ++ch)
        if (g[0][ch] == -1)
            g[0][ch] = 0;

    // Now, let's build the failure function

    // Initialize values in fail function
    memset(f, -1, sizeof f);

    // Failure function is computed in breadth first order
    // using a queue
    queue<int> q;

     // Iterate over every possible input
    for (int ch = 0; ch < MAXC; ++ch)
    {
        // All nodes of depth 1 have failure function value
        // as 0. For example, in above diagram we move to 0
        // from states 1 and 3.
        if (g[0][ch] != 0)
        {
            f[g[0][ch]] = 0;
            q.push(g[0][ch]);
        }
    }

    // Now queue has states 1 and 3
    while (q.size())
    {
        // Remove the front state from queue
        int state = q.front();
        q.pop();

        // For the removed state, find failure function for
        // all those characters for which goto function is
        // not defined.
        for (int ch = 0; ch <= MAXC; ++ch)
        {
            // If goto function is defined for character 'ch'
            // and 'state'
            if (g[state][ch] != -1)
            {
                // Find failure state of removed state
                int failure = f[state];

                // Find the deepest node labeled by proper
                // suffix of string from root to current
                // state.
                while (g[failure][ch] == -1)
                      failure = f[failure];

                failure = g[failure][ch];
                f[g[state][ch]] = failure;

                // Merge output values
                out[g[state][ch]] |= out[failure];

                // Insert the next level node (of Trie) in Queue
                q.push(g[state][ch]);
            }
        }
    }

    return states;
}

// Returns the next state the machine will transition to using goto
// and failure functions.
// currentState - The current state of the machine. Must be between
//                0 and the number of states - 1, inclusive.
// nextInput - The next character that enters into the machine.
int findNextState(int currentState, char nextInput)
{
    int answer = currentState;
    int ch = nextInput - 'a';

    // If goto is not defined, use failure function
    while (g[answer][ch] == -1)
        answer = f[answer];

    return g[answer][ch];
}

// This function finds all occurrences of all array words
// in text.
unsigned long long searchWords(vector<string> arr, int k, string text, int start, int end, vector<int> health)
{
    unsigned long long ret = 0;
    // Preprocess patterns.
    // Build machine with goto, failure and output functions

    // Initialize current state
    int currentState = 0;

    // Traverse the text through the nuilt machine to find
    // all occurrences of words in arr[]
    for (int i = 0; i < text.size(); ++i)
    {
        currentState = findNextState(currentState, text[i]);

        // If match not found, move to next state
        if (out[currentState] == 0)
             continue;

        // Match found, print all matching words of arr[]
        // using output function.
        for (int j = start; j <= end; ++j)
        {
            if (out[currentState] & (1 << j))
            {
                ret += health[j];
/*
                cout << "Word " << arr[j] << " appears from "
                     << i - arr[j].size() + 1 << " to " << i << endl;
*/
            }
        }
    }

    return ret;
}

// Driver program to test above
int main()
{
    long long min = -1;
    long long max = 0;

    int n;
    cin >> n;
    vector<string> genes(n);
    for(int genes_i = 0; genes_i < n; genes_i++){
        cin >> genes[genes_i];
    }
    vector<int> health(n);
    for(int health_i = 0; health_i < n; health_i++){
        cin >> health[health_i];
    }

    buildMatchingMachine(genes, n);

    int s;
    cin >> s;
    for(int a0 = 0; a0 < s; a0++){
        int first;
        int last;
        string d;
        cin >> first >> last >> d;
        // your code goes here

        unsigned long long ret = searchWords(genes, n, d, first, last, health);
        if (ret > max) {
            max = ret;
        }

        if (0 > min || ret < min) {
            min = ret;
        }
    }

    printf("%lld %lld\n", min, max);

    return 0;
}
<commit_msg>debug<commit_after>// C++ program for implementation of Aho Corasick algorithm
// for string matching
#include <queue>
#include <string>
#include <cstring>
#include <iostream>
#include <fstream>

#define DEBUG       1

using namespace std;

// Max number of states in the matching machine.
// Should be equal to the sum of the length of all keywords.
const int MAXS = 100000;

// Maximum number of characters in input alphabet
const int MAXC = 26;

// OUTPUT FUNCTION IS IMPLEMENTED USING out[]
// Bit i in this mask is one if the word with index i
// appears when the machine enters this state.
int out[MAXS];

// FAILURE FUNCTION IS IMPLEMENTED USING f[]
int f[MAXS];

// GOTO FUNCTION (OR TRIE) IS IMPLEMENTED USING g[][]
int g[MAXS][MAXC];

// Builds the string matching machine.
// arr -   array of words. The index of each keyword is important:
//         "out[state] & (1 << i)" is > 0 if we just found word[i]
//         in the text.
// Returns the number of states that the built machine has.
// States are numbered 0 up to the return value - 1, inclusive.
int buildMatchingMachine(vector<string> arr, int k)
{
    // Initialize all values in output function as 0.
    memset(out, 0, sizeof out);

    // Initialize all values in goto function as -1.
    memset(g, -1, sizeof g);

    // Initially, we just have the 0 state
    int states = 1;

    // Construct values for goto function, i.e., fill g[][]
    // This is same as building a Trie for arr[]
    for (int i = 0; i < k; ++i)
    {
        const string &word = arr[i];
        int currentState = 0;

        // Insert all characters of current word in arr[]
        for (int j = 0; j < word.size(); ++j)
        {
            int ch = word[j] - 'a';

            // Allocate a new node (create a new state) if a
            // node for ch doesn't exist.
            if (g[currentState][ch] == -1)
                g[currentState][ch] = states++;

            currentState = g[currentState][ch];
        }

        // Add current word in output function
        out[currentState] |= (1 << i);
    }

    // For all characters which don't have an edge from
    // root (or state 0) in Trie, add a goto edge to state
    // 0 itself
    for (int ch = 0; ch < MAXC; ++ch)
        if (g[0][ch] == -1)
            g[0][ch] = 0;

    // Now, let's build the failure function

    // Initialize values in fail function
    memset(f, -1, sizeof f);

    // Failure function is computed in breadth first order
    // using a queue
    queue<int> q;

     // Iterate over every possible input
    for (int ch = 0; ch < MAXC; ++ch)
    {
        // All nodes of depth 1 have failure function value
        // as 0. For example, in above diagram we move to 0
        // from states 1 and 3.
        if (g[0][ch] != 0)
        {
            f[g[0][ch]] = 0;
            q.push(g[0][ch]);
        }
    }

    // Now queue has states 1 and 3
    while (q.size())
    {
        // Remove the front state from queue
        int state = q.front();
        q.pop();

        // For the removed state, find failure function for
        // all those characters for which goto function is
        // not defined.
        for (int ch = 0; ch <= MAXC; ++ch)
        {
            // If goto function is defined for character 'ch'
            // and 'state'
            if (g[state][ch] != -1)
            {
                // Find failure state of removed state
                int failure = f[state];

                // Find the deepest node labeled by proper
                // suffix of string from root to current
                // state.
                while (g[failure][ch] == -1)
                      failure = f[failure];

                failure = g[failure][ch];
                f[g[state][ch]] = failure;

                // Merge output values
                out[g[state][ch]] |= out[failure];

                // Insert the next level node (of Trie) in Queue
                q.push(g[state][ch]);
            }
        }
    }

    return states;
}

// Returns the next state the machine will transition to using goto
// and failure functions.
// currentState - The current state of the machine. Must be between
//                0 and the number of states - 1, inclusive.
// nextInput - The next character that enters into the machine.
int findNextState(int currentState, char nextInput)
{
    int answer = currentState;
    int ch = nextInput - 'a';

    // If goto is not defined, use failure function
    while (g[answer][ch] == -1)
        answer = f[answer];

    return g[answer][ch];
}

// This function finds all occurrences of all array words
// in text.
unsigned long long searchWords(vector<string> arr, int k, string text, int start, int end, vector<int> health)
{
    unsigned long long ret = 0;
    // Preprocess patterns.
    // Build machine with goto, failure and output functions

    // Initialize current state
    int currentState = 0;

    // Traverse the text through the nuilt machine to find
    // all occurrences of words in arr[]
    for (int i = 0; i < text.size(); ++i)
    {
        currentState = findNextState(currentState, text[i]);

        // If match not found, move to next state
        if (out[currentState] == 0)
             continue;

        // Match found, print all matching words of arr[]
        // using output function.
        for (int j = start; j <= end; ++j)
        {
            if (out[currentState] & (1 << j))
            {
                ret += health[j];
/*
                cout << "Word " << arr[j] << " appears from "
                     << i - arr[j].size() + 1 << " to " << i << endl;
*/
            }
        }
    }

    return ret;
}

// Driver program to test above
int main()
{
    long long min = -1;
    long long max = 0;

    int n;

#if DEBUG
    ifstream fin("input.txt");
    if (!fin.is_open()) {
        printf("open file error!\n");
        return -1;
    }
    fin >> n;
#else
    cin >> n;
#endif

    vector<string> genes(n);
    for(int genes_i = 0; genes_i < n; genes_i++){
#if DEBUG
        fin >> genes[genes_i];
#else
        cin >> genes[genes_i];
#endif
    }
    vector<int> health(n);
    for(int health_i = 0; health_i < n; health_i++){
#if DEBUG
        fin >> health[health_i];
#else
        cin >> health[health_i];
#endif
    }

    buildMatchingMachine(genes, n);

    int s;

#if DEBUG
    fin >> s;
#else
    cin >> s;
#endif

    for(int a0 = 0; a0 < s; a0++){
        int first;
        int last;
        string d;

#if DEBUG
        fin >> first >> last >> d;
#else
        cin >> first >> last >> d;
#endif
        // your code goes here

        unsigned long long ret = searchWords(genes, n, d, first, last, health);
        if (ret > max) {
            max = ret;
        }

        if (0 > min || ret < min) {
            min = ret;
        }
    }

    printf("%lld %lld\n", min, max);

    return 0;
}
<|endoftext|>