|
From: <gb...@us...> - 2008-06-20 02:30:49
|
Revision: 199
http://gearbox.svn.sourceforge.net/gearbox/?rev=199&view=rev
Author: gbiggs
Date: 2008-06-19 19:30:52 -0700 (Thu, 19 Jun 2008)
Log Message:
-----------
Submitting the flexiport library and the replacement for the urg_nz library.
Modified Paths:
--------------
gearbox/trunk/submitted/CMakeLists.txt
Added Paths:
-----------
gearbox/trunk/submitted/flexiport/
gearbox/trunk/submitted/flexiport/CMakeLists.txt
gearbox/trunk/submitted/flexiport/doc.dox
gearbox/trunk/submitted/flexiport/flexiport.cpp
gearbox/trunk/submitted/flexiport/flexiport.h
gearbox/trunk/submitted/flexiport/flexiport_config.h.in
gearbox/trunk/submitted/flexiport/logfile.cpp
gearbox/trunk/submitted/flexiport/logfile.h
gearbox/trunk/submitted/flexiport/logreaderport.cpp
gearbox/trunk/submitted/flexiport/logreaderport.h
gearbox/trunk/submitted/flexiport/logwriterport.cpp
gearbox/trunk/submitted/flexiport/logwriterport.h
gearbox/trunk/submitted/flexiport/port.cpp
gearbox/trunk/submitted/flexiport/port.h
gearbox/trunk/submitted/flexiport/serialport.cpp
gearbox/trunk/submitted/flexiport/serialport.h
gearbox/trunk/submitted/flexiport/tcpport.cpp
gearbox/trunk/submitted/flexiport/tcpport.h
gearbox/trunk/submitted/flexiport/test/
gearbox/trunk/submitted/flexiport/test/CMakeLists.txt
gearbox/trunk/submitted/flexiport/test/example.cmake.in
gearbox/trunk/submitted/flexiport/test/example.logr
gearbox/trunk/submitted/flexiport/test/example.logw
gearbox/trunk/submitted/flexiport/test/example.readme
gearbox/trunk/submitted/flexiport/test/serial_example.cpp
gearbox/trunk/submitted/flexiport/test/tcp_example.cpp
gearbox/trunk/submitted/flexiport/timeout.cpp
gearbox/trunk/submitted/flexiport/timeout.h
gearbox/trunk/submitted/flexiport/utils/
gearbox/trunk/submitted/flexiport/utils/CMakeLists.txt
gearbox/trunk/submitted/flexiport/utils/porttoport.cpp
gearbox/trunk/submitted/flexiport/utils/utils.cmake.in
gearbox/trunk/submitted/flexiport/utils/utils.readme
gearbox/trunk/submitted/urg_nz/
gearbox/trunk/submitted/urg_nz/CMakeLists.txt
gearbox/trunk/submitted/urg_nz/doc.dox
gearbox/trunk/submitted/urg_nz/test/
gearbox/trunk/submitted/urg_nz/test/CMakeLists.txt
gearbox/trunk/submitted/urg_nz/test/example.cmake.in
gearbox/trunk/submitted/urg_nz/test/example.cpp
gearbox/trunk/submitted/urg_nz/test/example.logr
gearbox/trunk/submitted/urg_nz/test/example.logw
gearbox/trunk/submitted/urg_nz/test/example.readme
gearbox/trunk/submitted/urg_nz/urg_nz.cpp
gearbox/trunk/submitted/urg_nz/urg_nz.h
Modified: gearbox/trunk/submitted/CMakeLists.txt
===================================================================
--- gearbox/trunk/submitted/CMakeLists.txt 2008-06-19 15:36:49 UTC (rev 198)
+++ gearbox/trunk/submitted/CMakeLists.txt 2008-06-20 02:30:52 UTC (rev 199)
@@ -10,7 +10,8 @@
# When adding new directories, please maintain order of inter-dependencies.
# Otherwise, maintain alphabetical order.
# E.g. ADD_SUBDIRECTORY( mydir )
-
+ ADD_SUBDIRECTORY( flexiport )
ADD_SUBDIRECTORY( gbxnovatelacfr )
+ ADD_SUBDIRECTORY( urg_nz )
ENDIF( GBX_BUILD_SUBMITTED )
Added: gearbox/trunk/submitted/flexiport/CMakeLists.txt
===================================================================
--- gearbox/trunk/submitted/flexiport/CMakeLists.txt (rev 0)
+++ gearbox/trunk/submitted/flexiport/CMakeLists.txt 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,54 @@
+SET (libName flexiport)
+GBX_ADD_LICENSE (LGPL)
+
+SET (build TRUE)
+GBX_REQUIRE_OPTION (build LIB ${libName} ON)
+
+IF (build)
+ INCLUDE (${GBX_CMAKE_DIR}/UseBasicRules.cmake)
+
+ OPTION (FLEXIPORT_INCLUDE_SERIAL "Include the serial port in FlexiPort" ON)
+ OPTION (FLEXIPORT_INCLUDE_TCP "Include the TCP network port in FlexiPort" ON)
+ OPTION (FLEXIPORT_INCLUDE_LOGGING "Include the log reader/writer ports in FlexiPort" ON)
+ MARK_AS_ADVANCED (FLEXIPORT_INCLUDE_SERIAL FLEXIPORT_INCLUDE_TCP)
+
+ SET (flexiport_config_h_in ${CMAKE_CURRENT_SOURCE_DIR}/flexiport_config.h.in)
+ SET (flexiport_config_h ${CMAKE_CURRENT_BINARY_DIR}/flexiport_config.h)
+ CONFIGURE_FILE (${flexiport_config_h_in} ${flexiport_config_h})
+ INCLUDE_DIRECTORIES (${CMAKE_CURRENT_BINARY_DIR})
+
+ SET (hdrs flexiport.h port.h timeout.h ${flexiport_config_h})
+ SET (srcs flexiport.cpp port.cpp timeout.cpp ${flexiport_config_h})
+ IF (FLEXIPORT_INCLUDE_SERIAL)
+ SET (hdrs ${hdrs} serialport.h)
+ SET (srcs ${srcs} serialport.cpp)
+ ENDIF (FLEXIPORT_INCLUDE_SERIAL)
+ IF (FLEXIPORT_INCLUDE_TCP)
+ SET (hdrs ${hdrs} tcpport.h)
+ SET (srcs ${srcs} tcpport.cpp)
+ ENDIF (FLEXIPORT_INCLUDE_TCP)
+ IF (FLEXIPORT_INCLUDE_LOGGING)
+ IF (NOT GBX_OS_WIN)
+ SET (hdrs ${hdrs} logwriterport.h logreaderport.h)
+ SET (srcs ${srcs} logwriterport.cpp logreaderport.cpp logfile.cpp)
+ ELSE (NOT GBX_OS_WIN)
+ MESSAGE (STATUS "Flexiport log file system is not currently supported on Windows.")
+ ENDIF (NOT GBX_OS_WIN)
+ ENDIF (FLEXIPORT_INCLUDE_LOGGING)
+
+ IF (WIN32)
+ ADD_DEFINITIONS (-DFLEXIPORT_EXPORTS)
+ ENDIF (WIN32)
+ ADD_DEFINITIONS (-DFLEXIPORT_EXPORTS)
+ GBX_ADD_LIBRARY (${libName} SHARED ${srcs})
+ GBX_ADD_PKGCONFIG (${libName} "FlexiPort generic comms library" "" "" "" "")
+ GBX_ADD_HEADERS (${libName} ${hdrs})
+ IF (WIN32)
+ TARGET_LINK_LIBRARIES (${libName} Ws2_32)
+ ENDIF (WIN32)
+
+ ADD_SUBDIRECTORY (utils)
+ IF (GBX_BUILD_TESTS)
+ ADD_SUBDIRECTORY (test)
+ ENDIF (GBX_BUILD_TESTS)
+ENDIF (build)
Added: gearbox/trunk/submitted/flexiport/doc.dox
===================================================================
--- gearbox/trunk/submitted/flexiport/doc.dox (rev 0)
+++ gearbox/trunk/submitted/flexiport/doc.dox 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,91 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * This distribution is licensed to you under the terms described in
+ * the LICENSE file included in this distribution.
+ *
+ */
+
+/*!
+@ingroup gbx_libs
+@ingroup gbx_cpp
+@ingroup gbx_linux
+@ingroup gbx_windows
+@ingroup gbx_osx
+@defgroup gbx_library_flexiport FlexiPort
+@brief FlexiPort data communications library.
+
+Provides a consistent interface for communicating over a range of data port types. Currently serial
+and TCP ports are supported. Support for USB ports is planned for when I get a suitable USB device
+to test with. Logging is possible, which allows communications sessions to be played back at a later
+date without the original hardware present.
+
+The @ref SerialPort and @ref TCPPort port types are cross-platform, usable on Linux, Mac OSX and
+Microsoft Windows.
+
+@par Header file
+
+Necessary:
+@verbatim
+#include <flexiport/flexiport.h>
+#include <flexiport/port.h>
+@endverbatim
+
+Optional if port type-specific features are required:
+For SerialPort:
+@verbatim
+#include <flexiport/serialport.h>
+@endverbatim
+For TCPPort:
+@verbatim
+#include <flexiport/tcpport.h>
+@endverbatim
+For logging ports:
+@verbatim
+#include <flexiport/logwriterport.h>
+#include <flexiport/logreaderport.h>
+@endverbatim
+
+@par Example
+ See test/tcp_example.cpp and test/serial_example.cpp.
+
+@par Style
+- Naming conventions:
+ - Class methods start with a capital letter.
+ - variableNamesLikeThis.
+ - Private member variables start with an underscore _like _this.
+ - Avoid using \#defines. \#define'd values in all capitals when necessary.
+- Formatting:
+ - 1 tab indentation at start of lines, spaces only for inside lines (e.g. before commands).
+ - Function declarations on one line.
+ - Space between function name and arguments.
+- C++ API.
+ - Functionality provided through classes.
+
+@par Copyright
+ Geoffrey Biggs
+
+@par Responsible Developer
+ Geoffrey Biggs
+
+@par License
+ LGPL
+
+@par Dependencies
+
+- None
+
+*/
+
+
+/*!
+@brief FlexiPort data communications library.
+@namespace flexiport
+
+This namespace is part of the FlexiPort library.
+
+@see @ref gbx_library_flexiport
+
+*/
Added: gearbox/trunk/submitted/flexiport/flexiport.cpp
===================================================================
--- gearbox/trunk/submitted/flexiport/flexiport.cpp (rev 0)
+++ gearbox/trunk/submitted/flexiport/flexiport.cpp 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,159 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: ___
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "flexiport.h"
+#include "port.h"
+#include "serialport.h"
+#include "tcpport.h"
+#include "logwriterport.h"
+#include "logreaderport.h"
+#include "flexiport_config.h"
+
+#include <errno.h>
+#include <iostream>
+#include <sstream>
+using namespace std;
+
+namespace flexiport
+{
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Central factory function
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Port* CreatePort (string options)
+{
+ const char* separators = ", \t\n";
+ map<string, string> optionsMap;
+
+ string::size_type pos = 0;
+ // Find the start of the first option (skip delimeters at the beginning)
+ string::size_type lastPos = options.find_first_not_of (separators, 0);
+ // Check first that we did actually get something in the string
+ if (lastPos == string::npos)
+ {
+ // No options. Go boom.
+ throw PortException ("No options specified. Must specify a port type.");
+ }
+
+ do
+ {
+ pos = options.find_first_of (separators, lastPos);
+
+ // Got an option, one way or the other
+ string option = options.substr (lastPos, pos - lastPos);
+
+ string::size_type splitPos = option.find ('=');
+ if (splitPos != string::npos)
+ {
+ // Need to split this token
+ optionsMap[option.substr (0, splitPos)] =
+ option.substr (splitPos + 1, option.length () - splitPos - 1);
+ }
+ else
+ {
+ // Boolean option
+ optionsMap[option] = "1";
+ }
+
+ // Bring lastPos forward to the start of the next option (not just current pos)
+ lastPos = options.find_first_not_of (separators, pos);
+ }
+ while (lastPos != string::npos && pos != string::npos);
+
+ // Now that the options are in a map, call the standard CreatePort function
+ return CreatePort (optionsMap);
+}
+
+Port* CreatePort (map<string, string> options)
+{
+ // Look for a type option
+ string type;
+ if (options.count ("type") == 0)
+ throw PortException ("No port type specified.");
+ else if (options.count ("type") != 1)
+ throw PortException ("Multiple port types specified.");
+ else
+ type = options["type"];
+ options.erase ("type");
+
+#ifdef FLEXIPORT_INCLUDE_SERIAL
+ if (type == "serial")
+ {
+ SerialPort *newPort = new SerialPort (options);
+ if (newPort == NULL)
+ throw PortException ("Failed to allocate new port");
+ return newPort;
+ }
+#endif // FLEXIPORT_INCLUDE_SERIAL
+#ifdef FLEXIPORT_INCLUDE_TCP
+ if (type == "tcp")
+ {
+ TCPPort *newPort = new TCPPort (options);
+ if (newPort == NULL)
+ throw PortException ("Failed to allocate new port");
+ return newPort;
+ }
+#endif // FLEXIPORT_INCLUDE_TCP
+
+#ifdef FLEXIPORT_INCLUDE_LOGGING
+ if (type == "logreader")
+ {
+ LogReaderPort *newPort = new LogReaderPort (options);
+ if (newPort == NULL)
+ throw PortException ("Failed to allocate new port");
+ return newPort;
+ }
+ #ifdef FLEXIPORT_INCLUDE_SERIAL
+ if (type == "seriallog")
+ {
+ options["type"] = "serial";
+ LogWriterPort *newPort = new LogWriterPort (options);
+ if (newPort == NULL)
+ throw PortException ("Failed to allocate new port");
+ return newPort;
+ }
+ #endif // FLEXIPORT_INCLUDE_SERIAL
+ #ifdef FLEXIPORT_INCLUDE_TCP
+ if (type == "tcplog")
+ {
+ options["type"] = "tcp";
+ LogWriterPort *newPort = new LogWriterPort (options);
+ if (newPort == NULL)
+ throw PortException ("Failed to allocate new port");
+ return newPort;
+ }
+ #endif // FLEXIPORT_INCLUDE_TCP
+#endif // FLEXIPORT_INCLUDE_LOGGING
+
+ // If got to here, it's an unsupported port type
+ stringstream ss;
+ ss << "Unsupported port type: " << type;
+ throw PortException (ss.str ());
+}
+
+} // namespace flexiport
Added: gearbox/trunk/submitted/flexiport/flexiport.h
===================================================================
--- gearbox/trunk/submitted/flexiport/flexiport.h (rev 0)
+++ gearbox/trunk/submitted/flexiport/flexiport.h 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,103 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: ___
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FLEXIPORT_H
+#define __FLEXIPORT_H
+
+#include <map>
+#include <string>
+
+#if defined (WIN32)
+ #if defined (FLEXIPORT_EXPORTS)
+ #define FLEXIPORT_EXPORT __declspec (dllexport)
+ #else
+ #define FLEXIPORT_EXPORT __declspec (dllimport)
+ #endif
+#else
+ #define FLEXIPORT_EXPORT
+#endif
+
+/** @ingroup gbx_library_flexiport
+@{
+*/
+
+namespace flexiport
+{
+
+/// @brief Exception thrown by the Port class and its derivatives.
+class FLEXIPORT_EXPORT PortException : public std::exception
+{
+ public:
+ PortException (const char *errorStr)
+ : _errorStr (errorStr) {};
+ PortException (const std::string &errorStr)
+ : _errorStr (errorStr) {};
+ ~PortException (void) throw () {};
+
+ virtual const char* what (void) const throw ()
+ {
+ return _errorStr.c_str ();
+ }
+
+ private:
+ std::string _errorStr;
+};
+
+// Forward declaration of the Port class
+class Port;
+
+/** @brief Factory function to create a @ref Port object of the necessary port type.
+
+This factory function is the only way to create a @ref Port object matching the type of port you
+need. Options are passed in as key/value pairs in the @ref options argument. A minimum of one option
+is necessary, the "type" option. This specifies the type of port to be created. An object of the
+correct class (@ref SerialPort, @ref TCPPort or @ref LogReaderPort, for example) will be created
+based on the value of this option. All other options, including both universal options and
+type-specific options, will be passed on to the created object.
+
+See @ref Options for a list of options accepted by all port types. See each port type's description
+for a list of options accepted by that port type.
+
+@return A pointer to a @ref Port object.*/
+FLEXIPORT_EXPORT Port* CreatePort (std::map<std::string, std::string> options);
+
+/** @brief Overloaded factory function. Accepts options as a string.
+
+This overload accepts the options as a string, similar in format to command-line options. For
+example:
+"type=serial device=/dev/ttyS0 baudrate=4800 readwrite"
+Options may be separated by any of: spaces, tabs, new lines or commas. Do not put whitespace around
+an equals sign.
+
+@return A pointer to a @ref Port object.*/
+FLEXIPORT_EXPORT Port* CreatePort (std::string options);
+
+} // namespace flexiport
+
+/** @} */
+
+#endif // __FLEXIPORT_H
Added: gearbox/trunk/submitted/flexiport/flexiport_config.h.in
===================================================================
--- gearbox/trunk/submitted/flexiport/flexiport_config.h.in (rev 0)
+++ gearbox/trunk/submitted/flexiport/flexiport_config.h.in 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,5 @@
+#cmakedefine FLEXIPORT_EXPORTS 1
+
+#cmakedefine FLEXIPORT_INCLUDE_SERIAL 1
+#cmakedefine FLEXIPORT_INCLUDE_TCP 1
+#cmakedefine FLEXIPORT_INCLUDE_LOGGING 1
\ No newline at end of file
Added: gearbox/trunk/submitted/flexiport/logfile.cpp
===================================================================
--- gearbox/trunk/submitted/flexiport/logfile.cpp (rev 0)
+++ gearbox/trunk/submitted/flexiport/logfile.cpp 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,1286 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: ___
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "flexiport.h"
+#include "logfile.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sstream>
+#include <iostream>
+using namespace std;
+
+#if defined (WIN32)
+ #define __func__ __FUNCTION__
+#endif
+
+namespace flexiport
+{
+
+inline int ErrNo (void)
+{
+#if defined (WIN32)
+ return WSAGetLastError ();
+#else
+ return errno;
+#endif
+}
+
+inline string StrError (int errNo)
+{
+#if defined (WIN32)
+ LPVOID bufferPointer = NULL;
+ FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
+ errNo, 0, reinterpret_cast<LPTSTR> (&bufferPointer), 0, NULL);
+ string result (reinterpret_cast<LPTSTR> (bufferPointer));
+ LocalFree (bufferPointer);
+ return result;
+#else
+ return string (strerror (errNo));
+#endif
+}
+
+const size_t CHUNK_HEADER_SIZE = sizeof (size_t) + sizeof (struct timeval);
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Constructor/destructor
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+LogFile::LogFile (unsigned int debug)
+ : _read (false), _readFile (NULL), _writeFile (NULL), _readFileSize (0), _writeFileSize (0),
+ _debug (debug), _readUsage (0), _writeUsage (0), _readSize (0), _writeSize (0),
+ _readBuffer (NULL), _writeBuffer (NULL), _ignoreTimes (false)
+// _bufferSize (0),
+{
+ timerclear (&_openTime);
+}
+
+LogFile::~LogFile (void)
+{
+ Close ();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// File management
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void LogFile::Open (string fileName, bool read, bool ignoreTimes)
+{
+ Close ();
+
+ _fileName = fileName;
+ _read = read;
+ _ignoreTimes = ignoreTimes;
+
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Opening " << _fileName << " for " <<
+ (_read ? "reading." : "writing.") << endl;
+ }
+
+ if (_read)
+ {
+ if ((_readFile = fopen ((fileName + "r").c_str (), "r")) == NULL)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fopen(_readFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ if ((_writeFile = fopen ((fileName + "w").c_str (), "r")) == NULL)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fopen(_writeFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ // Get the file sizes for open checks
+ _readFileSize = GetFileSize (_readFile);
+ _writeFileSize = GetFileSize (_writeFile);
+ }
+ else
+ {
+ if ((_readFile = fopen ((fileName + "r").c_str (), "w")) == NULL)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fopen(_readFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ if ((_writeFile = fopen ((fileName + "w").c_str (), "w")) == NULL)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fopen(_writeFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+
+ if (_debug >= 1)
+ {
+ cerr << "LogFile::" << __func__ << "() Opened " << _fileName << "r/w for " <<
+ (_read ? "reading." : "writing.") << endl;
+ }
+}
+
+void LogFile::Close (void)
+{
+ if (_readFile != NULL)
+ {
+ if (fclose (_readFile) == EOF)
+ {
+ _readFile = NULL;
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fclose(_readFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+ if (_writeFile != NULL)
+ {
+ if (fclose (_writeFile) == EOF)
+ {
+ _writeFile = NULL;
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fclose(_writeFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+
+ if (_readBuffer != NULL)
+ {
+ free (_readBuffer);
+ _readBuffer = NULL;
+ }
+ if (_writeBuffer != NULL)
+ {
+ free (_writeBuffer);
+ _writeBuffer = NULL;
+ }
+
+ if (_debug >= 1)
+ cerr << "LogFile::" << __func__ << "() Closed file." << endl;
+}
+
+bool LogFile::IsOpen (void) const
+{
+ if (_readFile == NULL || _writeFile == NULL)
+ return false;
+
+ long pos;
+ if ((pos = ftell (_readFile)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() ftell() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ if (pos == _readFileSize)
+ return false;
+
+ if ((pos = ftell (_writeFile)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() ftell() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ if (pos == _writeFileSize)
+ return false;
+
+ return true;
+}
+
+//void LogFile::SetBufferSize (unsigned int bufferSize)
+//{
+// _bufferSize = bufferSize;
+// AllocateReadBuffer (_bufferSize);
+// AllocateWriteBuffer (_bufferSize);
+//}
+
+void LogFile::ResetFile (void)
+{
+ // Rewind file positions
+ if (fseek (_readFile, 0, SEEK_SET) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek(_readFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ if (fseek (_writeFile, 0, SEEK_SET) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek(_writeFile) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Free buffers
+ DeallocateReadBuffer ();
+ DeallocateWriteBuffer ();
+
+ // Reset file open time
+ if (gettimeofday (&_openTime, NULL) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() gettimeofday() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (_debug >= 1)
+ cerr << "LogFile::" << __func__ << "() Reset file." << endl;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Chunk reading (this stuff is messy)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ssize_t LogFile::Read (void *data, size_t count, Timeout &timeout)
+{
+ size_t totalRead = 0;
+
+ // Get the current file time
+ struct timeval now;
+ GetCurrentFileTime (now);
+
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Reading up to " << count << " bytes." << endl;
+
+ // First copy any data in the overflow buffer
+ if (_readUsage > 0)
+ {
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Getting " << _readUsage <<
+ " bytes from overflow buffer." << endl;
+ }
+
+ size_t toCopy = (count < _readUsage) ? count : _readUsage;
+ memcpy (data, _readBuffer, toCopy);
+ totalRead += toCopy;
+ // Check if we've used up the stuff in the buffer
+ if (count < _readUsage)
+ {
+ // Shift the remaining data in the overflow buffer to the beginning
+ memmove (_readBuffer, &(reinterpret_cast<uint8_t*> (_readBuffer)[count]),
+ _readUsage - count);
+ _readUsage -= count;
+ // Have all the data we need, return
+ return count;
+ }
+ else if (count == _readUsage)
+ {
+ DeallocateReadBuffer ();
+ // Have all the data we need, return
+ return count;
+ }
+ else // count > _readUsage
+ {
+ DeallocateReadBuffer ();
+ // We haven't met count yet
+ count -= _readUsage;
+ data = reinterpret_cast<uint8_t*> (data) + _readUsage;
+ }
+ }
+
+ // Now get data from the file
+ if (_ignoreTimes)
+ {
+ // Read chunks until we have enough data
+ while (count > 0 && IsOpen ())
+ {
+ struct timeval timestamp;
+ size_t size, read;
+ read = GetSingleChunk (_readFile, &(reinterpret_cast<uint8_t*> (data)[count]), count,
+ timestamp, size);
+ count -= read;
+ data = reinterpret_cast<uint8_t*> (data) + read;
+ totalRead += read;
+ }
+ return totalRead;
+ }
+ else
+ {
+ // Have to pay attention to time stamps (annoying and messy)
+ if (DataAvailableWithinLimit (_readFile, now))
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Data available in file now." << endl;
+
+ // Get all the data that is immediatly available and return it
+ totalRead += GetChunksToTimeLimit (_readFile, data, count, now);
+ return totalRead;
+ }
+
+ // There was no data instantly, so now the timeout gets involved
+ if (timeout._sec == -1)
+ {
+ // Infinite timeout
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Getting next chunk, no timeout." << endl;
+
+ // Get the next available chunk from the file
+ struct timeval timestamp;
+ size_t size;
+ totalRead += GetSingleChunk (_readFile, data, count, timestamp, size);
+
+ // Calculate the time difference between now and this chunk's timestamp
+ struct timeval diff;
+ GetCurrentFileTime (now);
+ timersub (×tamp, &now, &diff);
+ if (diff.tv_sec >= 0 && diff.tv_usec >= 0)
+ {
+ // Sleep for this period of time
+ struct timespec diff2;
+ diff2.tv_sec = diff.tv_sec;
+ diff2.tv_nsec = diff.tv_usec * 1000;
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Sleeping for " << diff2.tv_sec << "s " <<
+ diff2.tv_nsec << "ns." << endl;
+ }
+ nanosleep (&diff2, NULL);
+ }
+ return totalRead;
+ }
+ else if (timeout._sec > 0 || timeout._usec > 0)
+ {
+ // Limited timeout
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Getting next chunk with timeout." << endl;
+
+ // Check if there is actually data available within this time limit
+ struct timeval timeoutVal, limit;
+ timeout.AsTimeval (timeoutVal);
+ GetCurrentFileTime (now);
+ timeradd (&now, &timeoutVal, &limit);
+ if (DataAvailableWithinLimit (_readFile, limit))
+ {
+ // There is so get it
+ struct timeval timestamp;
+ size_t size;
+ totalRead += GetSingleChunk (_readFile, data, count, timestamp, size);
+
+ // Calculate the time difference between now and this chunk's timestamp
+ struct timeval diff;
+ GetCurrentFileTime (now);
+ timersub (×tamp, &now, &diff);
+ if (diff.tv_sec >= 0 && diff.tv_usec >= 0)
+ {
+ // Sleep for this period of time
+ struct timespec diff2;
+ diff2.tv_sec = diff.tv_sec;
+ diff2.tv_nsec = diff.tv_usec * 1000;
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Sleeping for " << diff2.tv_sec << "s " <<
+ diff2.tv_nsec << "ns." << endl;
+ }
+ nanosleep (&diff2, NULL);
+ }
+ return totalRead;
+ }
+ else
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() No chunks within timeout." << endl;
+ // No data available, return timeout
+ return -1;
+ }
+ }
+ else
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() No data available at all." << endl;
+ // No data available, return timeout
+ return -1;
+ }
+ }
+}
+
+ssize_t LogFile::BytesAvailable (const Timeout &timeout)
+{
+ if (_ignoreTimes)
+ {
+ // Don't care about times, so just get the file size.
+ return GetFileSize (_readFile);
+ }
+ else
+ {
+ // Get the current file time
+ struct timeval now;
+ GetCurrentFileTime (now);
+
+ // Now count data from the file
+ if (DataAvailableWithinLimit (_readFile, now))
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Enough data available in file now." << endl;
+ // Data available immediately, return its size plus the size of the overflow buffer
+ return _readUsage + GetChunkSizesToTimeLimit (_readFile, now);
+ }
+ else if (_readUsage > 0)
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Only data in overflow buffer." << endl;
+ // No data from file, but there is data in the overflow buffer, so that will do
+ return _readUsage;
+ }
+
+ // There was no data instantly, so now the timeout gets involved
+ if (timeout._sec == -1)
+ {
+ // Infinite timeout
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Getting next chunk, no timeout." << endl;
+
+ // Get the next available chunk from the file
+ struct timeval timeStamp;
+ size_t size;
+ GetNextChunkInfo (_readFile, timeStamp, size);
+
+ // Calculate the time difference between now and this chunk's timestamp
+ struct timeval diff;
+ GetCurrentFileTime (now);
+ timersub (&timeStamp, &now, &diff);
+ if (diff.tv_sec >= 0 && diff.tv_usec >= 0)
+ {
+ // Sleep for this period of time
+ struct timespec diff2;
+ diff2.tv_sec = diff.tv_sec;
+ diff2.tv_nsec = diff.tv_usec * 1000;
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Sleeping for " << diff2.tv_sec << "s " <<
+ diff2.tv_nsec << "ns." << endl;
+ }
+ nanosleep (&diff2, NULL);
+ }
+ return size;
+ }
+ else if (timeout._sec > 0 || timeout._usec > 0)
+ {
+ // Limited timeout
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() Getting next chunk with timeout." << endl;
+
+ // Check if there is actually data available within this time limit
+ struct timeval timeoutVal, limit;
+ timeout.AsTimeval (timeoutVal);
+ GetCurrentFileTime (now);
+ timeradd (&now, &timeoutVal, &limit);
+ if (DataAvailableWithinLimit (_readFile, limit))
+ {
+ // There is so get it
+ struct timeval timeStamp;
+ size_t size;
+ GetNextChunkInfo (_readFile, timeStamp, size);
+
+ // Calculate the time difference between now and this chunk's timestamp
+ struct timeval diff;
+ GetCurrentFileTime (now);
+ timersub (&timeStamp, &now, &diff);
+ if (diff.tv_sec >= 0 && diff.tv_usec >= 0)
+ {
+ // Sleep for this period of time
+ struct timespec diff2;
+ diff2.tv_sec = diff.tv_sec;
+ diff2.tv_nsec = diff.tv_usec * 1000;
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Sleeping for " << diff2.tv_sec << "s " <<
+ diff2.tv_nsec << "ns." << endl;
+ }
+ nanosleep (&diff2, NULL);
+ }
+ return size;
+ }
+ else
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() No chunks within timeout." << endl;
+ // No data available, return timeout
+ return -1;
+ }
+ }
+ else
+ {
+ if (_debug >= 2)
+ cerr << "LogFile::" << __func__ << "() No data available at all." << endl;
+ // No data available, return timeout
+ return -1;
+ }
+ }
+}
+
+bool LogFile::CheckWrite (const void * const data, const size_t count, size_t * const numWritten,
+ const Timeout * const timeout)
+{
+ size_t totalRead = 0;
+ long readFileOffset;
+ // Get the current read file offset (see comment at function end)
+ if ((readFileOffset = ftell (_readFile)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() ftell() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Checking " << count <<
+ " bytes for accuracy. Timeouts will " << ((timeout == NULL) ? "" : "not ") <<
+ "be used." << endl;
+ }
+
+ // Allocate space to store the data to compare with
+ uint8_t *fileData;
+ if ((fileData = new uint8_t[count]) == NULL)
+ {
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("Failed to allocate temporary space for write comparison."));
+ }
+
+ // Pull any data out of the overflow first
+ bool needMore = true;
+ if (_writeUsage > 0)
+ {
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Getting " << _writeUsage <<
+ " bytes from overflow buffer." << endl;
+ }
+
+ size_t toCopy = (count < _writeUsage) ? count : _writeUsage;
+ memcpy (fileData, _writeBuffer, toCopy);
+ totalRead += toCopy;
+ // Check if we've used up the stuff in the buffer
+ if (count < _writeUsage)
+ {
+ // Shift the remaining data in the buffer to the beginning
+ memmove (_writeBuffer, &(reinterpret_cast<uint8_t*> (_writeBuffer)[count]),
+ _writeUsage - count);
+ _writeUsage -= count;
+ // Have all the data we need
+ needMore = false;
+ }
+ else if (count == _writeUsage)
+ {
+ DeallocateWriteBuffer ();
+ // Have all the data we need
+ needMore = false;
+ }
+ else // count > _writeUsage
+ {
+ DeallocateWriteBuffer ();
+ // We haven't met count yet so get the rest from the file
+ }
+ }
+
+ if (needMore)
+ {
+ if (timeout == NULL || _ignoreTimes)
+ {
+ // Don't care about times, so just get data and compare
+ while (totalRead < count && IsOpen ())
+ {
+ struct timeval timestamp;
+ size_t size, read;
+ read = GetSingleChunk (_writeFile, &fileData[totalRead], count - totalRead,
+ timestamp, size);
+ totalRead += read;
+ }
+ }
+ else
+ {
+ // Things are more complex in this case
+ if (timeout->_sec == -1)
+ {
+ // Infinite timeout - ignore jitter
+
+ // Get the next available chunk from the file
+ struct timeval timestamp;
+ size_t size, read;
+ read = GetSingleChunk (_writeFile, &fileData[totalRead], count - totalRead,
+ timestamp, size);
+ totalRead += read;
+
+ // Calculate the time difference between now and this chunk's timestamp
+ struct timeval diff, now;
+ GetCurrentFileTime (now);
+ timersub (×tamp, &now, &diff);
+ if (diff.tv_sec >= 0 && diff.tv_usec >= 0)
+ {
+ // Sleep for this period of time
+ struct timespec diff2;
+ diff2.tv_sec = diff.tv_sec;
+ diff2.tv_nsec = diff.tv_usec * 1000;
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Sleeping for " << diff2.tv_sec << "s " <<
+ diff2.tv_nsec << "ns." << endl;
+ }
+ nanosleep (&diff2, NULL);
+ }
+ }
+ else if (timeout->_sec > 0 || timeout->_usec > 0)
+ {
+ // Limited timeout
+
+ // Check if there is actually data available within this time limit
+ struct timeval now, timeoutVal, limit;
+ timeout->AsTimeval (timeoutVal);
+ GetCurrentFileTime (now);
+ timeradd (&now, &timeoutVal, &limit);
+ if (DataAvailableWithinLimit (_writeFile, limit))
+ {
+ // There is so get it
+ struct timeval timestamp;
+ size_t size, read;
+ read = GetSingleChunk (_writeFile, &fileData[totalRead], count - totalRead,
+ timestamp, size);
+ totalRead += read;
+
+ // Calculate the time difference between now and this chunk's timestamp
+ struct timeval diff;
+ GetCurrentFileTime (now);
+ timersub (×tamp, &now, &diff);
+ if (diff.tv_sec >= 0 && diff.tv_usec >= 0)
+ {
+ // Sleep for this period of time
+ struct timespec diff2;
+ diff2.tv_sec = diff.tv_sec;
+ diff2.tv_nsec = diff.tv_usec * 1000;
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Sleeping for " << diff2.tv_sec <<
+ "s " << diff2.tv_nsec << "ns." << endl;
+ }
+ nanosleep (&diff2, NULL);
+ }
+ }
+ // else no data available
+ }
+ // else no data available
+ }
+ }
+
+ // At this point, the fileData buffer is full with the right quantity of data (or less if less
+ // was available). Compare it to the given data and return the result;
+ bool result;
+ if (totalRead < count)
+ result = false;
+ else if (memcmp (data, fileData, count) != 0)
+ result = false;
+ else
+ result = true;
+
+ // One final thing to do: it's possible that the write file has reached its end and so been
+ // closed. This means that any attempts to read the response to the write will now fail. To
+ // prevent this, reopen the files and re-position the read file's offset to where it was. Any
+ // writes after this would have choked anyway since the file is at an end so having the write
+ // file at the beginning is unlikely to cause any problems.
+ if (!IsOpen ())
+ {
+ Open (_fileName, _read, _ignoreTimes);
+ if (fseek (_readFile, readFileOffset, SEEK_SET) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+
+ delete[] fileData;
+ *numWritten = totalRead;
+ return result;
+}
+
+void LogFile::Flush (void)
+{
+ // Dump the read overflow buffer
+ DeallocateWriteBuffer();
+
+ // If there is data available in the read file, skip passed it
+ struct timeval now, timeStamp;
+ size_t size;
+ GetCurrentFileTime (now);
+ if (DataAvailableWithinLimit (_readFile, now))
+ {
+ GetNextChunkInfo (_readFile, timeStamp, size);
+ do
+ {
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Skipping " << CHUNK_HEADER_SIZE + size <<
+ "bytes in _readFile." << endl;
+ }
+ if (fseek (_readFile, CHUNK_HEADER_SIZE + size, SEEK_CUR) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek(_readFile) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ GetNextChunkInfo (_readFile, timeStamp, size);
+ }
+ while (!(timercmp (&timeStamp, &now, >)) && IsOpen ());
+ }
+
+ // Call Drain to do the same on the write file
+ Drain ();
+}
+
+void LogFile::Drain (void)
+{
+ // Dump the write overflow buffer
+ DeallocateWriteBuffer();
+
+ // If there is data available in the write file, skip passed it
+ struct timeval now, timeStamp;
+ size_t size;
+ GetCurrentFileTime (now);
+ if (DataAvailableWithinLimit (_readFile, now))
+ {
+ GetNextChunkInfo (_writeFile, timeStamp, size);
+ do
+ {
+ if (_debug >= 2)
+ {
+ cerr << "LogFile::" << __func__ << "() Skipping " << CHUNK_HEADER_SIZE + size <<
+ "bytes in _writeFile." << endl;
+ }
+ if (fseek (_writeFile, CHUNK_HEADER_SIZE + size, SEEK_CUR) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek(_writeFile) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ GetNextChunkInfo (_writeFile, timeStamp, size);
+ }
+ while (!(timercmp (&timeStamp, &now, >)) && IsOpen ());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// File writing (this stuff is easy)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void LogFile::WriteRead (const void * const data, size_t count)
+{
+ if (_debug >= 1)
+ {
+ cerr << "LogFile::" << __func__ << "() Writing read chunk of size " << count <<
+ " bytes." << endl;
+ }
+ WriteTimeStamp (_readFile);
+ WriteToFile (_readFile, &count, sizeof (count));
+ WriteToFile (_readFile, data, count);
+}
+
+void LogFile::WriteWrite (const void * const data, size_t count)
+{
+ if (_debug >= 1)
+ {
+ cerr << "LogFile::" << __func__ << "() Writing write chunk of size " << count <<
+ " bytes." << endl;
+ }
+ WriteTimeStamp (_writeFile);
+ WriteToFile (_writeFile, &count, sizeof (count));
+ WriteToFile (_writeFile, data, count);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Internal functions
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void LogFile::AllocateReadBuffer (unsigned int size)
+{
+ // Allocate based on size
+ if (_readBuffer == NULL && size > 0)
+ {
+ // Allocate a new buffer
+ if ((_readBuffer = reinterpret_cast<uint8_t*> (malloc (sizeof (uint8_t) * size))) ==
+ NULL)
+ {
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Failed to allocate memory for read buffer."));
+ }
+ _readUsage = 0;
+ }
+ else if (_readBuffer != NULL && size > 0)
+ {
+ // Reallocate
+ uint8_t *newBuffer;
+ if ((newBuffer = reinterpret_cast<uint8_t*> (realloc (_readBuffer, sizeof (uint8_t) *
+ size))) == NULL)
+ {
+ free (_readBuffer);
+ _readBuffer = NULL;
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Failed to allocate memory for read buffer."));
+ }
+ _readBuffer = newBuffer;
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Read overflow buffer was reallocated." << endl;
+ }
+ else if (_readBuffer != NULL && size == 0)
+ {
+ // Free
+ free (_readBuffer);
+ _readBuffer = NULL;
+ _readUsage = 0;
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Read overflow buffer was freed." << endl;
+ }
+ _readSize = size;
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Allocated read overflow buffer of size " <<
+ _readSize << "bytes." << endl;
+ }
+}
+
+void LogFile::AllocateWriteBuffer (unsigned int size)
+{
+ // Allocate based on size
+ if (_writeBuffer == NULL && size > 0)
+ {
+ // Allocate a new buffer
+ if ((_writeBuffer = reinterpret_cast<uint8_t*> (malloc (sizeof (uint8_t) * size))) ==
+ NULL)
+ {
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Failed to allocate memory for read buffer."));
+ }
+ _writeUsage = 0;
+ }
+ else if (_readBuffer != NULL && size > 0)
+ {
+ // Reallocate
+ uint8_t *newBuffer;
+ if ((newBuffer = reinterpret_cast<uint8_t*> (realloc (_writeBuffer, sizeof (uint8_t) *
+ size))) == NULL)
+ {
+ free (_writeBuffer);
+ _writeBuffer = NULL;
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Failed to allocate memory for read buffer."));
+ }
+ _writeBuffer = newBuffer;
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Write overflow buffer was reallocated." << endl;
+ }
+ else if (_writeBuffer != NULL && size == 0)
+ {
+ // Free
+ free (_writeBuffer);
+ _writeBuffer = NULL;
+ _writeUsage = 0;
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Write overflow buffer was freed." << endl;
+ }
+ _writeSize = size;
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Allocated write overflow buffer of size " <<
+ _writeSize << "bytes." << endl;
+ }
+}
+
+void LogFile::DeallocateReadBuffer (void)
+{
+ if (_readBuffer != NULL)
+ {
+ free (_readBuffer);
+ _readBuffer = NULL;
+ _readSize = 0;
+ _readUsage = 0;
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Deallocated read overflow buffer." << endl;
+ }
+}
+
+void LogFile::DeallocateWriteBuffer (void)
+{
+ if (_writeBuffer != NULL)
+ {
+ free (_writeBuffer);
+ _writeBuffer = NULL;
+ _writeSize = 0;
+ _writeUsage = 0;
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Deallocated write overflow buffer." << endl;
+ }
+}
+
+void LogFile::GetCurrentFileTime (struct timeval &dest)
+{
+ struct timeval now;
+
+ if (gettimeofday (&now, NULL) < 0)
+ {
+ stringstream ss;
+ ss << "LogReaderPort::" << __func__ << "() gettimeofday() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ timersub (&now, &_openTime, &dest);
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Current file time is " << dest.tv_sec << "s " <<
+ dest.tv_usec << "us." << endl;
+ }
+}
+
+bool LogFile::DataAvailableWithinLimit (FILE * const file, const struct timeval &limit)
+{
+ struct timeval chunkTime;
+ size_t chunkSize;
+
+ GetNextChunkInfo (file, chunkTime, chunkSize);
+ if (!(timercmp (&chunkTime, &limit, >)))
+ return true;
+ return false;
+}
+
+void LogFile::GetNextChunkInfo (FILE * const file, struct timeval &timeStamp, size_t &size)
+{
+ // Read the chunk info
+ ReadFromFile (file, &timeStamp, sizeof (timeStamp));
+ ReadFromFile (file, &size, sizeof (size));
+ // Rewind the file back to the beginning of the chunk header
+ if (fseek (file, -1 * CHUNK_HEADER_SIZE, SEEK_CUR) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Next chunk has size " << size <<
+ " bytes and time stamp " << timeStamp.tv_sec << "s " << timeStamp.tv_usec <<
+ "us." << endl;
+ }
+}
+
+size_t LogFile::GetChunksToTimeLimit (FILE * const file, void *data, size_t count,
+ const struct timeval &limit)
+{
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Getting chunks up to " << limit.tv_sec << "s " <<
+ limit.tv_usec << "us." << endl;
+ }
+
+ // Keep reading chunks from the file until the limit is passed or count bytes have been read.
+ struct timeval chunkTime;
+ size_t chunkSize, totalRead = 0;
+ GetNextChunkInfo (file, chunkTime, chunkSize);
+ while (!(timercmp (&chunkTime, &limit, >)) && count > 0 && IsOpen ())
+ {
+ size_t numSaved = GetSingleChunk (file, data, count, chunkTime, chunkSize);
+ data = reinterpret_cast<uint8_t*> (data) + numSaved;
+ count -= numSaved;
+ totalRead += numSaved;
+ // Safety check
+ if (!IsOpen ())
+ break;
+ // Next chunk
+ GetNextChunkInfo (file, chunkTime, chunkSize);
+ }
+
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Read a total of " << totalRead << " bytes." << endl;
+ return totalRead;
+}
+
+size_t LogFile::GetChunkSizesToTimeLimit (FILE * const file, const struct timeval &limit)
+{
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Getting chunk sizes up to " << limit.tv_sec <<
+ "s " << limit.tv_usec << "us." << endl;
+ }
+
+ struct timeval chunkTime;
+ size_t chunkSize, totalSize = 0;
+
+ // Store the current file position
+ long currentPos;
+ if ((currentPos = ftell (file)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() ftell() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ GetNextChunkInfo (file, chunkTime, chunkSize);
+ while (!(timercmp (&chunkTime, &limit, >)) && IsOpen ())
+ {
+ totalSize += chunkSize;
+ // Safety check
+ if (!IsOpen ())
+ break;
+ // Next chunk
+ GetNextChunkInfo (file, chunkTime, chunkSize);
+ }
+ // Reopen if necessary
+ if (!IsOpen ())
+ Open (_fileName, _read, _ignoreTimes);
+
+ // Go back to the start position
+ if (fseek (file, currentPos, SEEK_SET) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Found a total of " << totalSize << " bytes." << endl;
+ return totalSize;
+}
+
+// This function returns the number of bytes put into data. This may be less than the number of
+// bytes actually read from the chunk, as some might go into an overflow buffer.
+size_t LogFile::GetSingleChunk (FILE * const file, void *data, size_t count,
+ struct timeval &timeStamp, size_t &size)
+{
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Reading a single chunk from " <<
+ ((file == _readFile) ? "read file." : "write file.") << endl;
+ }
+
+ // Read the chunk info
+ ReadFromFile (file, &timeStamp, sizeof (timeStamp));
+ ReadFromFile (file, &size, sizeof (size));
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Chunk is " << size << " bytes." << endl;
+
+ // Check if this chunk will fit in data
+ if (size > count)
+ {
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Chunk is too big for destination." << endl;
+
+ // Need to allocate an overflow buffer
+ uint8_t *overFlow;
+ if (file == _readFile)
+ {
+ AllocateReadBuffer (size - count);
+ overFlow = _readBuffer;
+ _readUsage = size - count;
+ }
+ else
+ {
+ AllocateWriteBuffer (size - count);
+ overFlow = _writeBuffer;
+ _writeUsage = size - count;
+ }
+
+ // Read as much as can fit into data
+ ReadFromFile (file, data, sizeof (*reinterpret_cast<uint8_t*> (data)) * count);
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Read " <<
+ sizeof (*reinterpret_cast<uint8_t*> (data)) * count << " bytes into destination." <<
+ endl;
+ }
+ // Read the rest into the overflow buffer
+ ReadFromFile (file, overFlow, sizeof (uint8_t) * (size - count));
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Read " << sizeof (uint8_t) * (size - count) <<
+ " bytes into overflow buffer." << endl;
+ }
+
+ return count;
+ }
+ else
+ {
+ // It'll fit so read straight into data
+ ReadFromFile (file, data, sizeof (*reinterpret_cast<uint8_t*> (data)) * size);
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Read " <<
+ sizeof (*reinterpret_cast<uint8_t*> (data)) * size << " bytes into destination." <<
+ endl;
+ }
+ return size;
+ }
+}
+
+size_t LogFile::GetFileSize (FILE * const file)
+{
+ // Store the current position
+ long currentPos;
+ if ((currentPos = ftell (file)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() ftell() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Seek to the end of the file and find its position
+ if (fseek (file, 0, SEEK_END) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ long fileSize;
+ if ((fileSize = ftell (file)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() ftell() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Go back to the original position
+ if (fseek (file, currentPos, SEEK_SET) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fseek() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ return fileSize;
+}
+
+void LogFile::ReadFromFile (FILE * const file, void * const dest, size_t count)
+{
+ if (!_read)
+ {
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Cannot read from write log file."));
+ }
+
+ // Test if this file is actually open
+ if (file == NULL || ftell (file) < 0)
+ throw PortException (string ("LogFile::") + __func__ + string ("() Log file is not open."));
+ if (dest == NULL)
+ throw PortException (string ("LogFile::") + __func__ + string ("() No destination."));
+
+ size_t numRead;
+ if ((numRead = fread (dest, 1, count, file)) < count)
+ {
+ cerr << "Only read " << numRead << " of " << count << " bytes" << endl;
+ if (feof (file))
+ {
+ // Close the files
+ Close ();
+ }
+ else if (ferror (file))
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fread() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ else
+ {
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Didn't get enough data from fread()."));
+ }
+ }
+}
+
+void LogFile::WriteToFile (FILE * const file, const void * const data, size_t count)
+{
+ size_t totalWritten = 0;
+
+ if (_read)
+ {
+ throw PortException (string ("LogFile::") + __func__ +
+ string ("() Cannot write to read log file."));
+ }
+ if (file == NULL)
+ throw PortException (string ("LogFile::") + __func__ + string ("() Log file is not open."));
+ if (data == NULL)
+ throw PortException (string ("LogFile::") + __func__ + string ("() No data to write."));
+
+ while (totalWritten < count)
+ {
+ ssize_t numWritten;
+ if ((numWritten = fwrite (&(reinterpret_cast<const uint8_t*> (data)[totalWritten]),
+ sizeof (uint8_t), count - totalWritten, file)) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() fwrite() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ totalWritten += numWritten;
+ }
+ if (_debug >= 3)
+ cerr << "LogFile::" << __func__ << "() Wrote " << totalWritten << " bytes." << endl;
+}
+
+void LogFile::WriteTimeStamp (FILE * const file)
+{
+ // Calculate the time difference between now and the time the file was opened
+ struct timeval now, diff;
+ if (gettimeofday (&now, NULL) < 0)
+ {
+ stringstream ss;
+ ss << "LogFile::" << __func__ << "() gettimeofday() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ timersub (&now, &_openTime, &diff);
+ // Write the time stamp to the file
+ WriteToFile (file, &diff, sizeof (diff));
+ if (_debug >= 3)
+ {
+ cerr << "LogFile::" << __func__ << "() Wrote time stamp: " << diff.tv_sec << "s " <<
+ diff.tv_usec << "us (time of write is " << now.tv_sec << "s " << now.tv_usec << "us)."
+ << endl;
+ }
+}
+
+} // namespace flexiport
Added: gearbox/trunk/submitted/flexiport/logfile.h
===================================================================
--- gearbox/trunk/submitted/flexiport/logfile.h (rev 0)
+++ gearbox/trunk/submitted/flexiport/logfile.h 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,101 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: ___
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOGFILE_H
+#define __LOGFILE_H
+
+#include <sys/time.h>
+#include <string>
+#include <vector>
+
+#include "timeout.h"
+
+namespace flexiport
+{
+
+// Class for managing a log file pair
+class LogFile
+{
+ public:
+ LogFile (unsigned int debug);
+ ~LogFile (void);
+
+ void Open (std::string fileName, bool read, bool ignoreTimes = false);
+ void Close (void);
+ bool IsOpen (void) const;
+// void SetBufferSize (unsigned int bufferSize);
+ void ResetFile (void);
+
+ // File reading
+ ssize_t Read (void *data, size_t count, Timeout &timeout);
+ ssize_t BytesAvailable (const Timeout &timeout);
+ bool CheckWrite (const void * const data, const size_t count, size_t * const numWritten,
+ const Timeout * const timeout = NULL);
+ void Flush (void);
+ void Drain (void);
+
+ // File writing
+ void WriteRead (const void * const data, size_t count);
+ void WriteWrite (const void * const data, size_t count);
+
+ private:
+ std::string _fileName;
+ bool _read;
+ FILE *_readFile, *_writeFile;
+ long _readFileSize, _writeFileSize;
+ // When writing, this is the time the file was opened. When reading, it's the reset time.
+ struct timeval _openTime;
+ unsigned int _debug;
+// unsigned int _bufferSize;
+ size_t _readUsage, _writeUsage;
+ size_t _readSize, _writeSize;
+ uint8_t *_readBuffer, *_writeBuffer;
+ bool _ignoreTimes;
+
+ void AllocateReadBuffer (unsigned int size = 0);
+ void AllocateWriteBuffer (unsigned int size = 0);
+ void DeallocateReadBuffer (void);
+ void DeallocateWriteBuffer (void);
+
+ void GetCurrentFileTime (struct timeval &dest);
+ bool DataAvailableWithinLimit (FILE * const file, const struct timeval &limit);
+ void GetNextChunkInfo (FILE * const file, struct timeval &timeStamp, size_t &size);
+ size_t GetChunksToTimeLimit (FILE * const file, void *data, size_t count,
+ const struct timeval &limit);
+ size_t GetChunkSizesToTimeLimit (FILE * const file, const struct timeval &limit);
+ size_t GetSingleChunk (FILE * const file, void *data, size_t count,
+ struct timeval &timeStamp, size_t &size);
+ size_t GetFileSize (FILE * const file);
+
+ void ReadFromFile (FILE * const file, void * const dest, size_t count);
+ void WriteToFile (FILE * const file, const void * const data, size_t count);
+ void WriteTimeStamp (FILE * const file);
+};
+
+} // namespace flexiport
+
+#endif // __LOGFILE_H
Added: gearbox/trunk/submitted/flexiport/logreaderport.cpp
===================================================================
--- gearbox/trunk/submitted/flexiport/logreaderport.cpp (rev 0)
+++ gearbox/trunk/submitted/flexiport/logreaderport.cpp 2008-06-20 02:30:52 UTC (rev 199)
@@ -0,0 +1,360 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: ___
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even t...
[truncated message content] |