/[projects]/smsdaemon/serialport/SerialPort.h
ViewVC logotype

Diff of /smsdaemon/serialport/SerialPort.h

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 195 by torben, Mon Jun 16 10:56:02 2008 UTC revision 196 by torben, Thu Dec 18 06:53:29 2008 UTC
# Line 40  Line 40 
40   *   *
41   * :FIXME: Provide examples of the above potential problem.   * :FIXME: Provide examples of the above potential problem.
42   *   *
43   * @todo The current implementation does not check if another process has   * @todo The current implementation does not check if another process has
44   * locked the serial port device and does not lock the serial port device after   * locked the serial port device and does not lock the serial port device after
45   * opening it. This has been observed to cause problems while using this   * opening it. This has been observed to cause problems while using this
46   * library while other programs such as minicom are also accessing the same device.   * library while other programs such as minicom are also accessing the same device.
47   * It will be useful to lock the serial port device when it is being used by   * It will be useful to lock the serial port device when it is being used by
48   * this class.   * this class.
49   */   */
50  class SerialPort  class SerialPort
51  {  {
52  public:  public:
53      /**          /**
54       * The allowed set of baud rates.           * The allowed set of baud rates.
55       */           */
56      enum BaudRate {          enum BaudRate
57          BAUD_50      = B50,          {
58          BAUD_75      = B75,                  BAUD_50      = B50,
59          BAUD_110     = B110,                  BAUD_75      = B75,
60          BAUD_134     = B134,                  BAUD_110     = B110,
61          BAUD_150     = B150,                  BAUD_134     = B134,
62          BAUD_200     = B200,                  BAUD_150     = B150,
63          BAUD_300     = B300,                  BAUD_200     = B200,
64          BAUD_600     = B600,                  BAUD_300     = B300,
65          BAUD_1200    = B1200,                  BAUD_600     = B600,
66          BAUD_1800    = B1800,                  BAUD_1200    = B1200,
67          BAUD_2400    = B2400,                  BAUD_1800    = B1800,
68          BAUD_4800    = B4800,                  BAUD_2400    = B2400,
69          BAUD_9600    = B9600,                  BAUD_4800    = B4800,
70          BAUD_19200   = B19200,                  BAUD_9600    = B9600,
71          BAUD_38400   = B38400,                  BAUD_19200   = B19200,
72          BAUD_57600   = B57600,                  BAUD_38400   = B38400,
73          BAUD_115200  = B115200,                  BAUD_57600   = B57600,
74          BAUD_230400  = B230400,                  BAUD_115200  = B115200,
75          //                  BAUD_230400  = B230400,
76          // Bug#1318912                  //
77          // B460800 is defined on Linux but not on Mac OS X. What about other                  // Bug#1318912
78          // operating systems ?                  // B460800 is defined on Linux but not on Mac OS X. What about other
79          //                  // operating systems ?
80  #ifdef __linux__                        //
81          BAUD_460800  = B460800,  #ifdef __linux__
82                    BAUD_460800  = B460800,
83  #endif  #endif
84          BAUD_DEFAULT = BAUD_57600                  BAUD_DEFAULT = BAUD_57600
85      } ;          } ;
86    
87            enum CharacterSize
88            {
89                    CHAR_SIZE_5  = CS5, //!< 5 bit characters.
90                    CHAR_SIZE_6  = CS6, //!< 6 bit characters.
91                    CHAR_SIZE_7  = CS7, //!< 7 bit characters.
92                    CHAR_SIZE_8  = CS8, //!< 8 bit characters.
93                    CHAR_SIZE_DEFAULT = CHAR_SIZE_8
94            } ;
95    
96            enum StopBits
97            {
98                    STOP_BITS_1,   //! 1 stop bit.
99                    STOP_BITS_2,   //! 2 stop bits.
100                    STOP_BITS_DEFAULT = STOP_BITS_1
101            } ;
102    
103            enum Parity
104            {
105                    PARITY_EVEN,     //!< Even parity.
106                    PARITY_ODD,      //!< Odd parity.
107                    PARITY_NONE,     //!< No parity i.e. parity checking disabled.
108                    PARITY_DEFAULT = PARITY_NONE
109            } ;
110    
111            enum FlowControl
112            {
113                    FLOW_CONTROL_HARD,
114                    // FLOW_CONTROL_SOFT,
115                    FLOW_CONTROL_NONE,
116                    FLOW_CONTROL_DEFAULT = FLOW_CONTROL_NONE
117            } ;
118    
119            class NotOpen : public std::logic_error
120            {
121            public:
122                    NotOpen(const std::string& whatArg) :
123                                    logic_error(whatArg) { }
124            } ;
125    
126            class OpenFailed : public std::runtime_error
127            {
128            public:
129                    OpenFailed(const std::string& whatArg) :
130                                    runtime_error(whatArg) { }
131            } ;
132    
133            class AlreadyOpen : public std::logic_error
134            {
135            public:
136                    AlreadyOpen( const std::string& whatArg ) :
137                                    logic_error(whatArg) { }
138            } ;
139    
140            class UnsupportedBaudRate : public std::runtime_error
141            {
142            public:
143                    UnsupportedBaudRate( const std::string& whatArg ) :
144                                    runtime_error(whatArg) { }
145            } ;
146    
147            class ReadTimeout : public std::runtime_error
148            {
149            public:
150                    ReadTimeout() : runtime_error( "Read timeout" ) { }
151            } ;
152    
153            /**
154             * Constructor for a serial port.
155             */
156            explicit SerialPort( const std::string& serialPortName ) ;
157    
158            /**
159             * Destructor.
160             */
161            ~SerialPort() throw() ;
162    
163            /**
164             * Open the serial port with the specified settings. A serial port
165             * cannot be used till it is open.
166             *
167             * @throw AlreadyOpen This exception is thrown if the serial port
168             * is already open.
169             *
170             * @throw OpenFailed This exception is thrown if the serial port
171             * could not be opened.
172             *
173             * @throw std::invalid_argument This exception is thrown if an
174             * invalid parameter value is specified.
175             */
176            void
177            Open( const BaudRate      baudRate    = BAUD_DEFAULT,
178                  const CharacterSize charSize    = CHAR_SIZE_DEFAULT,
179                  const Parity        parityType  = PARITY_DEFAULT,
180                  const StopBits      stopBits    = STOP_BITS_DEFAULT,
181                  const FlowControl   flowControl = FLOW_CONTROL_DEFAULT )
182            throw( AlreadyOpen,
183                   OpenFailed,
184                   UnsupportedBaudRate,
185                   std::invalid_argument ) ;
186    
187            /**
188             * Check if the serial port is open for I/O.
189             */
190            bool
191            IsOpen() const ;
192    
193            /**
194             * Close the serial port. All settings of the serial port will be
195             * lost and no more I/O can be performed on the serial port.
196             *
197             * @throw NotOpen Thrown if this method is called while the serial
198             * port is not open.
199             *
200             */
201            void
202            Close()
203            throw(NotOpen) ;
204    
205            /**
206             * Set the baud rate for the serial port to the specified value
207             * (baudRate).
208             *
209             * @throw NotOpen Thrown if this method is called while the serial
210             * port is not open.
211             *
212             * @throw std::invalid_argument Thrown if an invalid baud rate is
213             * specified.
214             */
215            void
216            SetBaudRate( const BaudRate baudRate )
217            throw( UnsupportedBaudRate,
218                   NotOpen,
219                   std::invalid_argument ) ;
220    
221            /**
222             * Get the current baud rate for the serial port.
223             *
224             * @throw NotOpen Thrown if this method is called while the serial
225             * port is not open.
226             */
227            BaudRate
228            GetBaudRate() const
229            throw( NotOpen,
230                   std::runtime_error ) ;
231    
232            /**
233             * Set the character size for the serial port.
234             *
235             * @throw NotOpen Thrown if this method is called while the serial
236             * port is not open.
237             *
238             * @throw std::invalid_argument Thrown if an invalid character
239             * size is specified.
240             */
241            void
242            SetCharSize( const CharacterSize charSize )
243            throw( NotOpen,
244                   std::invalid_argument ) ;
245            /**
246             * Get the current character size for the serial port.
247             *
248             * @throw NotOpen Thrown if this method is called while the serial
249             * port is not open.
250             *
251             */
252            CharacterSize
253            GetCharSize() const
254            throw(NotOpen) ;
255    
256            /**
257             * Set the parity type for the serial port.
258             *
259             * @throw NotOpen Thrown if this method is called while the serial
260             * port is not open.
261             *
262             * @throw std::invalid_argument Thrown if an invalid parity is
263             * specified.
264             */
265            void
266            SetParity( const Parity parityType )
267            throw( NotOpen,
268                   std::invalid_argument ) ;
269    
270            /**
271             * Get the parity type for the serial port.
272             *
273             * @throw NotOpen Thrown if this method is called while the serial
274             * port is not open.
275             *
276             */
277            Parity
278            GetParity() const
279            throw(NotOpen) ;
280    
281            /**
282             * Set the number of stop bits to be used with the serial port.
283             *
284             * @throw NotOpen Thrown if this method is called while the serial
285             * port is not open.
286             *
287             * @throw std::invalid_argument Thrown if an invalid number of
288             * stop bits is specified.
289             */
290            void
291            SetNumOfStopBits( const StopBits numOfStopBits )
292            throw( NotOpen,
293                   std::invalid_argument ) ;
294    
295            /**
296             * Get the number of stop bits currently being used by the serial
297             * port.
298             *
299             * @throw NotOpen Thrown if this method is called while the serial
300             * port is not open.
301             *
302             */
303            StopBits
304            GetNumOfStopBits() const
305            throw(NotOpen) ;
306    
307            /**
308             * Set flow control.
309             *
310             * @throw NotOpen Thrown if this method is called while the serial
311             * port is not open.
312             *
313             * @throw std::invalid_argument Thrown if an invalid flow control
314             * is specified.
315             */
316            void
317            SetFlowControl( const FlowControl   flowControl )
318            throw( NotOpen,
319                   std::invalid_argument ) ;
320    
321      enum CharacterSize {          /**
322          CHAR_SIZE_5  = CS5, //!< 5 bit characters.           * Get the current flow control setting.
323          CHAR_SIZE_6  = CS6, //!< 6 bit characters.           *
324          CHAR_SIZE_7  = CS7, //!< 7 bit characters.           * @throw NotOpen Thrown if this method is called while the serial
325          CHAR_SIZE_8  = CS8, //!< 8 bit characters.           * port is not open.
326          CHAR_SIZE_DEFAULT = CHAR_SIZE_8           *
327      } ;           */
328            FlowControl
329      enum StopBits {          GetFlowControl() const
330          STOP_BITS_1,   //! 1 stop bit.          throw( NotOpen ) ;
331          STOP_BITS_2,   //! 2 stop bits.  
332          STOP_BITS_DEFAULT = STOP_BITS_1          /**
333      } ;           * Check if data is available at the input of the serial port.
334             *
335      enum Parity {           * @throw NotOpen Thrown if this method is called while the serial
336          PARITY_EVEN,     //!< Even parity.           * port is not open.
337          PARITY_ODD,      //!< Odd parity.           *
338          PARITY_NONE,     //!< No parity i.e. parity checking disabled.           */
339          PARITY_DEFAULT = PARITY_NONE          bool
340      } ;          IsDataAvailable() const
341            throw(NotOpen) ;
342      enum FlowControl {  
343          FLOW_CONTROL_HARD,          /**
344          // FLOW_CONTROL_SOFT,           * Read a single byte from the serial port. If no data is
345          FLOW_CONTROL_NONE,           * available in the specified number of milliseconds (msTimeout),
346          FLOW_CONTROL_DEFAULT = FLOW_CONTROL_NONE           * then this method will throw ReadTimeout exception. If msTimeout
347      } ;           * is 0, then this method will block till data is available.
348             */
349      class NotOpen : public std::logic_error          unsigned char
350      {          ReadByte( const unsigned int msTimeout = 0 )
351      public:          throw( NotOpen,
352          NotOpen(const std::string& whatArg) :                 ReadTimeout,
353              logic_error(whatArg) { }                 std::runtime_error ) ;
354      } ;  
355            /**
356      class OpenFailed : public std::runtime_error           * Read the specified number of bytes from the serial port. The
357      {           * method will timeout if no data is received in the specified
358      public:           * number of milliseconds (msTimeout). If msTimeout is 0, then
359          OpenFailed(const std::string& whatArg) :           * this method will block till all requested bytes are
360              runtime_error(whatArg) { }           * received. If numOfBytes is zero, then this method will keep
361      } ;           * reading data till no more data is available at the serial
362             * port. In all cases, all read data is available in dataBuffer on
363      class AlreadyOpen : public std::logic_error           * return from this method.
364      {           */
365      public:          typedef std::vector<unsigned char> DataBuffer ;
366          AlreadyOpen( const std::string& whatArg ) :          void
367              logic_error(whatArg) { }          Read( DataBuffer&        dataBuffer,
368      } ;                const unsigned int numOfBytes = 0,
369                  const unsigned int msTimeout  = 0 )
370      class UnsupportedBaudRate : public std::runtime_error          throw( NotOpen,
371      {                 ReadTimeout,
372      public:                 std::runtime_error ) ;
373          UnsupportedBaudRate( const std::string& whatArg ) :  
374              runtime_error(whatArg) { }  
375      } ;          /**
376             * Read a line of characters from the serial port.
377      class ReadTimeout : public std::runtime_error           */
378      {          const std::string
379      public:          ReadLine( const unsigned int msTimeout = 0,
380          ReadTimeout() : runtime_error( "Read timeout" ) { }                    const char         lineTerminator = '\n' )
381      } ;          throw( NotOpen,
382                   ReadTimeout,
383      /**                 std::runtime_error ) ;
384       * Constructor for a serial port.  
385       */          /**
386      explicit SerialPort( const std::string& serialPortName ) ;           * Send a single byte to the serial port.
387             *
388      /**           * @throw NotOpen Thrown if this method is called while the serial
389       * Destructor.           * port is not open.
390       */           */
391      ~SerialPort() throw() ;          void
392            WriteByte(const unsigned char dataByte)
393      /**          throw( NotOpen,
394       * Open the serial port with the specified settings. A serial port                 std::runtime_error ) ;
395       * cannot be used till it is open.  
396       *          /**
397       * @throw AlreadyOpen This exception is thrown if the serial port           * Write the data from the specified vector to the serial port.
398       * is already open.           */
399       *          void
400       * @throw OpenFailed This exception is thrown if the serial port          Write(const DataBuffer& dataBuffer)
401       * could not be opened.          throw( NotOpen,
402       *                 std::runtime_error ) ;
403       * @throw std::invalid_argument This exception is thrown if an  
404       * invalid parameter value is specified.          /**
405       */           * Write a string to the serial port.
406      void           */
407      Open( const BaudRate      baudRate    = BAUD_DEFAULT,          void
408            const CharacterSize charSize    = CHAR_SIZE_DEFAULT,          Write(const std::string& dataString)
409            const Parity        parityType  = PARITY_DEFAULT,          throw( NotOpen,
410            const StopBits      stopBits    = STOP_BITS_DEFAULT,                 std::runtime_error ) ;
411            const FlowControl   flowControl = FLOW_CONTROL_DEFAULT )  
412          throw( AlreadyOpen,          /**
413                 OpenFailed,           * Set the DTR line to the specified value.
414                 UnsupportedBaudRate,           */
415                 std::invalid_argument ) ;          void
416            SetDtr( const bool dtrState = true )
417      /**          throw( NotOpen,
418       * Check if the serial port is open for I/O.                 std::runtime_error ) ;
419       */  
420      bool          /**
421      IsOpen() const ;           * Get the status of the DTR line.
422             */
423      /**          bool
424       * Close the serial port. All settings of the serial port will be          GetDtr() const
425       * lost and no more I/O can be performed on the serial port.          throw( NotOpen,
426       *                 std::runtime_error ) ;
427       * @throw NotOpen Thrown if this method is called while the serial  
428       * port is not open.          /**
429       *           * Set the RTS line to the specified value.
430       */           */
431      void          void
432      Close()          SetRts( const bool rtsState = true )
433          throw(NotOpen) ;          throw( NotOpen,
434                   std::runtime_error ) ;
435      /**  
436       * Set the baud rate for the serial port to the specified value          /**
437       * (baudRate).           * Get the status of the RTS line.
438       *           */
439       * @throw NotOpen Thrown if this method is called while the serial          bool
440       * port is not open.          GetRts() const
441       *          throw( NotOpen,
442       * @throw std::invalid_argument Thrown if an invalid baud rate is                 std::runtime_error ) ;
443       * specified.  
444       */          //void
445      void          //SetCts( const bool ctsState = true )
446      SetBaudRate( const BaudRate baudRate )          //    throw( NotOpen,
447          throw( UnsupportedBaudRate,          //           std::runtime_error ) ;
448                 NotOpen,  
449                 std::invalid_argument ) ;          bool
450            GetCts() const
451      /**          throw( NotOpen,
452       * Get the current baud rate for the serial port.                 std::runtime_error ) ;
453       *  
454       * @throw NotOpen Thrown if this method is called while the serial          //void
455       * port is not open.          //SetDsr( const bool dsrState = true )
456       */          //    throw( NotOpen,
457      BaudRate          //           std::runtime_error ) ;
458      GetBaudRate() const  
459          throw( NotOpen,          bool
460                 std::runtime_error ) ;          GetDsr() const
461            throw( NotOpen,
462      /**                 std::runtime_error ) ;
      * Set the character size for the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      * @throw std::invalid_argument Thrown if an invalid character  
      * size is specified.  
      */  
     void  
     SetCharSize( const CharacterSize charSize )  
         throw( NotOpen,  
                std::invalid_argument ) ;  
     /**  
      * Get the current character size for the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      */  
     CharacterSize  
     GetCharSize() const  
         throw(NotOpen) ;  
   
     /**  
      * Set the parity type for the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      * @throw std::invalid_argument Thrown if an invalid parity is  
      * specified.  
      */  
     void  
     SetParity( const Parity parityType )  
         throw( NotOpen,  
                std::invalid_argument ) ;  
   
     /**  
      * Get the parity type for the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      */  
     Parity  
     GetParity() const  
         throw(NotOpen) ;  
   
     /**  
      * Set the number of stop bits to be used with the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      * @throw std::invalid_argument Thrown if an invalid number of  
      * stop bits is specified.  
      */  
     void  
     SetNumOfStopBits( const StopBits numOfStopBits )  
         throw( NotOpen,  
                std::invalid_argument ) ;  
   
     /**  
      * Get the number of stop bits currently being used by the serial  
      * port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      */  
     StopBits  
     GetNumOfStopBits() const  
         throw(NotOpen) ;  
   
     /**  
      * Set flow control.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      * @throw std::invalid_argument Thrown if an invalid flow control  
      * is specified.  
      */  
     void  
     SetFlowControl( const FlowControl   flowControl )  
         throw( NotOpen,  
                std::invalid_argument ) ;  
   
     /**  
      * Get the current flow control setting.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      */  
     FlowControl  
     GetFlowControl() const  
         throw( NotOpen ) ;  
   
     /**  
      * Check if data is available at the input of the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      *  
      */  
     bool  
     IsDataAvailable() const  
         throw(NotOpen) ;  
   
     /**  
      * Read a single byte from the serial port. If no data is  
      * available in the specified number of milliseconds (msTimeout),  
      * then this method will throw ReadTimeout exception. If msTimeout  
      * is 0, then this method will block till data is available.  
      */  
     unsigned char  
     ReadByte( const unsigned int msTimeout = 0 )  
         throw( NotOpen,  
                ReadTimeout,  
                std::runtime_error ) ;  
   
     /**  
      * Read the specified number of bytes from the serial port. The  
      * method will timeout if no data is received in the specified  
      * number of milliseconds (msTimeout). If msTimeout is 0, then  
      * this method will block till all requested bytes are  
      * received. If numOfBytes is zero, then this method will keep  
      * reading data till no more data is available at the serial  
      * port. In all cases, all read data is available in dataBuffer on  
      * return from this method.  
      */  
     typedef std::vector<unsigned char> DataBuffer ;  
     void  
     Read( DataBuffer&        dataBuffer,  
           const unsigned int numOfBytes = 0,  
           const unsigned int msTimeout  = 0 )  
         throw( NotOpen,  
                ReadTimeout,  
                std::runtime_error ) ;  
   
   
     /**  
      * Read a line of characters from the serial port.  
      */  
     const std::string  
     ReadLine( const unsigned int msTimeout = 0,  
               const char         lineTerminator = '\n' )  
         throw( NotOpen,  
                ReadTimeout,  
                std::runtime_error ) ;  
   
     /**  
      * Send a single byte to the serial port.  
      *  
      * @throw NotOpen Thrown if this method is called while the serial  
      * port is not open.  
      */  
     void  
     WriteByte(const unsigned char dataByte)  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     /**  
      * Write the data from the specified vector to the serial port.  
      */  
     void  
     Write(const DataBuffer& dataBuffer)  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     /**  
      * Write a string to the serial port.  
      */  
     void  
     Write(const std::string& dataString)  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     /**  
      * Set the DTR line to the specified value.  
      */  
     void  
     SetDtr( const bool dtrState = true )  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     /**  
      * Get the status of the DTR line.  
      */  
     bool  
     GetDtr() const  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     /**  
      * Set the RTS line to the specified value.  
      */  
     void  
     SetRts( const bool rtsState = true )  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     /**  
      * Get the status of the RTS line.  
      */  
     bool  
     GetRts() const  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     //void  
     //SetCts( const bool ctsState = true )  
     //    throw( NotOpen,  
     //           std::runtime_error ) ;  
       
     bool  
     GetCts() const  
         throw( NotOpen,  
                std::runtime_error ) ;  
   
     //void  
     //SetDsr( const bool dsrState = true )  
     //    throw( NotOpen,  
     //           std::runtime_error ) ;  
       
     bool  
     GetDsr() const  
         throw( NotOpen,  
                std::runtime_error ) ;  
463  private:  private:
464          /**          /**
465           * Prevent copying of objects of this class by declaring the copy           * Prevent copying of objects of this class by declaring the copy
466       * constructor private. This method is never defined.           * constructor private. This method is never defined.
467       */           */
468      SerialPort( const SerialPort& otherSerialPort ) ;          SerialPort( const SerialPort& otherSerialPort ) ;
469        
470      /**          /**
471       * Prevent copying of objects of this class by declaring the assignment           * Prevent copying of objects of this class by declaring the assignment
472       * operator private. This method is never defined.           * operator private. This method is never defined.
473       */           */
474      SerialPort& operator=(const SerialPort& otherSerialPort ) ;          SerialPort& operator=(const SerialPort& otherSerialPort ) ;
475        
476      /*          /*
477       * Forward declaration of the implementation class folowing the           * Forward declaration of the implementation class folowing the
478       * PImpl idiom.           * PImpl idiom.
479       */           */
480      class SerialPortImpl ;          class SerialPortImpl ;
481        
482      /**          /**
483       * Pointer to implementation class instance.           * Pointer to implementation class instance.
484       */           */
485      SerialPortImpl* mSerialPortImpl ;          SerialPortImpl* mSerialPortImpl ;
486  } ;  } ;
487    
488  #endif // #ifndef _SerialPort_h_  #endif // #ifndef _SerialPort_h_

Legend:
Removed from v.195  
changed lines
  Added in v.196

  ViewVC Help
Powered by ViewVC 1.1.20