Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
CommPort.hGo 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 |