Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

CommPort.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_CommPort_h_
00003 #define INCLUDED_CommPort_h_
00004 
00005 #include "Shared/plistCollections.h"
00006 #include "Shared/InstanceTracker.h"
00007 #include "Shared/Resource.h"
00008 #include "IPC/Thread.h"
00009 #include <string>
00010 #include <streambuf>
00011 #include <ios>
00012 
00013 //! A CommPort provides an abstract interface to a communication resource, based on wrapping a standard library stream buffer
00014 /*! Key extensions provided beyond the std::basic_streambuf are a mutual exclusion lock,
00015  *  explicitly separate read/write buffers (which, depending on implementation, could refer
00016  *  to the same stream), recursive open/close, plist-based configuration parameters, and
00017  *  integration with an InstanceTracker registry and factory for dynamic reconfigurability.
00018  *
00019  *  Usually you can get by using one of the standard stream buffers (streambuf, filebuf, stringbuf)
00020  *  but if you need to implement a custom stream, these links may help
00021  *  get you started:
00022  *  - An article by Paul Grenyer stepping through the process:
00023  *    http://accu.org/index.php/journals/264
00024  *  - Sample code from Chris Frey for a network class:
00025  *    http://www.netdirect.ca/~cdfrey/software/sockstream.cc.txt
00026  * 
00027  *  See also our own network stream class, ionetstream (Wireless/netstream.h/.cc).
00028  *  Although intended for networking, you can pass it any file descriptor, which makes
00029  *  it handy for pipes as well.
00030  *
00031  *  Clients should be careful to use the locking mechanism if there is a possibility of
00032  *  confusing query-responses or competing command/query-polls!
00033  *
00034  * Example usage: look up an instance named "Foo", and send it a query.
00035  * @code
00036  * CommPort* comm = CommPort::getRegistry().getInstance("Foo");
00037  * std::ostream is(&comm->getReadStreambuf());
00038  * std::ostream os(&comm->getWriteStreambuf());
00039  * is.tie(&os); // fancy failsafe -- make sure 'os' is flushed anytime we read from 'is'
00040  *
00041  * // Locking a CommPort across query-response pairs:
00042  * int value;
00043  * {
00044  *     MarkScope autolock(*comm); // marks the comm port as "in use" until the end of its scope
00045  *     os << "query-value-command" << endl;
00046  *     is >> value;
00047  *     // because we have the lock, we know 'value' is in response
00048  *     // to the 'query-value-command', and not a response to any other thread
00049  * }
00050  * @endcode
00051  *
00052  * Advanced locking: try to get a lock, then transfer it to a MarkScope to
00053  * ensure exception-safety.
00054  * @code
00055  * Thread::Lock& l = comm->getLock();
00056  * if(l.trylock()) {
00057  *     MarkScope autolock(l); l.unlock(); // transfer lock to MarkScope
00058  *     // use comm ...
00059  * }
00060  * @endcode
00061  */
00062 class CommPort : public virtual plist::Dictionary, public Resource {
00063 public:
00064   //! destructor, removes from registry in case we're deleting it from some other source than registry's own destroy()
00065   virtual ~CommPort() { getRegistry().destroy(instanceName); }
00066   
00067   //! the streambuf which does the actual work should inherit from basic_streambuf, using the system's default character type
00068   typedef std::basic_streambuf<std::ios::char_type> streambuf;
00069   
00070   //! Returns the name of the class (aka its type)
00071   /*! Suggested implementation is to declare a static string member, set it to the result of
00072    *  calling the registry's registerType, and then return that member here */
00073   virtual std::string getClassName() const=0;
00074   
00075   //! Provides serialized access to the comm port
00076   /*! Multiple drivers might be using the same comm port, callers should get the
00077    *  lock when doing operations on the comm port, particularly across sending
00078    *  a command and waiting for the reply.  See MarkScope for usage. */
00079   virtual Thread::Lock& getLock() { return lock; }
00080   
00081   //! Called when communication is about to begin, should handle recursive open/close calls
00082   /*! The subclass is expected to have its own configuration settings
00083    *  which define the parameters of what is to be "opened".
00084    *  Hence, no arguments are passed.
00085    *
00086    *  You should be able to handle recursive levels of open/close in case multiple
00087    *  drivers are using the same CommPort.
00088    *
00089    *  @return true if successful (or already open) */
00090   virtual bool open()=0;
00091   
00092   //! Called when communication is complete, should handle recursive open/close calls
00093   /*! @return true if successful, false if still open (in use elsewhere) */
00094   virtual bool close()=0;
00095   
00096   //! Allows you to check whether the reference from getReadStreambuf() is currently functional (if checking is supported!)
00097   /*! For streambufs which don't have a way to check this, always returns true. */
00098   virtual bool isReadable() { return true; }
00099   
00100   //! Allows you to check whether the reference from getWriteStreambuf() is currently functional (if checking is supported!)
00101   /*! For streambufs which don't have a way to check this, always returns true. */
00102   virtual bool isWriteable() { return true; }
00103   
00104   //! Returns a std::basic_streambuf, which is expected to implement the actual work
00105   /*! You can pass this to an istream to use the nice C++ style input and output,
00106    *  or you can call the streambuf functions directly.  However, if you're going
00107    *  the latter route, probably easier to just call CommPort's own read() and write().
00108    *
00109    *  Depending on implementation, the streambuf this returns might be a
00110    *  different instance than getWriteStreambuf.  If they are the same instance,
00111    *  then you could use an iostream instead of separate istream and ostream.*/
00112   virtual streambuf& getReadStreambuf()=0;
00113   
00114   //! Returns a std::basic_streambuf, which is expected to implement the actual work
00115   /*! You can pass this to an ostream to use the nice C++ style input and output,
00116    *  or you can call the streambuf functions directly.  However, if you're going
00117    *  the latter route, probably easier to just call CommPort's own read() and write().
00118    *
00119    *  Depending on implementation, the streambuf this returns might be a
00120    *  different instance than getReadStreambuf.  If they are the same instance,
00121    *  then you could use an iostream instead of separate istream and ostream.*/
00122   virtual streambuf& getWriteStreambuf()=0;
00123   
00124   //! returns up to @a n bytes from the streambuf, returns the number read
00125   virtual size_t read(char* buf, size_t n) { return getReadStreambuf().sgetn(buf,n); }
00126   //! writes up to @a n bytes from the streambuf, returns the number written
00127   virtual size_t write(const char* buf, size_t n) { return getWriteStreambuf().sputn(buf,n); }
00128   
00129   //! reads all available data from getReadStreambuf()
00130   virtual void read(std::string& s) {
00131     s.clear();
00132     const size_t BUFSIZE=256;
00133     char buf[BUFSIZE];
00134     size_t nread=read(buf,BUFSIZE);
00135     while(nread!=0) {
00136       s.append(buf,nread);
00137       if(nread!=BUFSIZE || getReadStreambuf().in_avail()<=0)
00138         break;
00139       nread=read(buf,BUFSIZE);
00140     }
00141   }
00142   //! writes the string into getWriteStreambuf()
00143   virtual size_t write(const std::string& s) { return write(s.c_str(),s.size()); }
00144   
00145   //! short hand for the instance tracker, which allows dynamic reconfiguration of CommPort instances
00146   typedef InstanceTracker<CommPort,std::string,Factory1Arg<CommPort,std::string> > registry_t;
00147   //! registry from which current instances can be discovered and new instances allocated based on their class names
00148   static registry_t& getRegistry() { static registry_t registry; return registry; }
00149   
00150 protected:
00151   //! constructor, pass the name of the class's type so we can use it in error messages, and a name for the instance so we can register it for MotionHook's to lookup
00152   CommPort(const std::string& /*classname*/, const std::string& instancename)
00153   : plist::Dictionary(), Resource(), instanceName(instancename), lock()
00154   {
00155     setLoadSavePolicy(FIXED,SYNC);
00156   }
00157   
00158   //! To be called be "deepest" subclass constructor at the end of construction
00159   /*! Don't want to register until completed construction!  plist::Collection listeners would be
00160    *  triggered and might start performing operations on instance while partially constructed */
00161   virtual void registerInstance() {
00162     if(CommPort * inst=getRegistry().getInstance(instanceName)) {
00163       std::cerr << "Warning: registration of CommPort " << getClassName() << " named " << instanceName << " @ " << this
00164       << " blocked by previous " << inst->getClassName() << " instance of same name @ " << inst << std::endl;
00165     }
00166     if(!getRegistry().registerInstance(getClassName(),instanceName,this))
00167       std::cerr << "Error: failed to register " << getClassName() << " named " << instanceName << " @ " << this;
00168     //addEntry(".type",new plist::Primitive<std::string>(className),"Stores the typename of the comm port so it can be re-instantiated on load.\n** Do not edit ** ");
00169   }
00170     
00171   //! Provides a Resource interface, allowing you to use MarkScope directly on the CommPort instead of calling through getLock().
00172   /*! Users don't need to call this directly... either pass the CommPort to a MarkScope, or call getLock(). */
00173   virtual void useResource(Data& d) { static_cast<Resource&>(lock).useResource(d); }
00174   //! provides a Resource interface, allowing you to use MarkScope directly on the CommPort instead of calling through getLock().
00175   /*! Users don't need to call this directly... either pass the CommPort to a MarkScope, or call getLock(). */
00176   virtual void releaseResource(Data& d) { static_cast<Resource&>(lock).releaseResource(d); }
00177   
00178   virtual void opened() {} //!< should be called by open() once the connection is successfully made, so deeper subclasses can do initialization
00179   virtual void closing() {} //!< should be called by close() before the connection is closed, so deeper subclasses can do cleanup
00180   
00181   const std::string instanceName; //!< holds the name of this instance of CommPort (mainly for error message reporting by the class itself)
00182   
00183   //!< ensures that serialized access is maintained (assuming clients use the lock...)
00184   /*! Often devices have either half-duplex communication, or may give responses to
00185    *  command strings.  It is important to get a lock across a query-response pair so that
00186    *  there is no risk of a second thread attempting a competing command or query. */
00187   Thread::Lock lock;
00188   
00189 private:
00190   CommPort(const CommPort&); // no copy, don't call
00191   CommPort& operator=(const CommPort&); // no copy, don't call
00192 };
00193 
00194 /*! @file
00195  * @brief 
00196  * @author Ethan Tira-Thompson (ejt) (Creator)
00197  */
00198 
00199 #endif

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3