From 285dc9fd4b95673639fa6091632e7fb629c02fcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Guillem=20Aleny=C3=A0=20Ribas?= <galenya@iri.upc.edu>
Date: Mon, 18 Apr 2011 13:38:36 +0000
Subject: [PATCH] trunk/tags/branches creation

---
 CMakeLists.txt                        |  84 ++++
 Findcomm.cmake                        |  21 +
 ReadMe.txt                            |  17 +
 doc/doxygen.conf                      | 251 ++++++++++
 doc/doxygen_project_name.conf         |   1 +
 doc/images/comm_states.eps            | 506 +++++++++++++++++++
 doc/images/comm_states.odp            | Bin 0 -> 12467 bytes
 doc/images/comm_states.png            | Bin 0 -> 22446 bytes
 doc/main.dox                          | 143 ++++++
 src/CMakeLists.txt                    |  57 +++
 src/comm.cpp                          | 328 +++++++++++++
 src/comm.h                            | 649 ++++++++++++++++++++++++
 src/commexceptions.cpp                |  12 +
 src/commexceptions.h                  |  58 +++
 src/examples/CMakeLists.txt           |  35 ++
 src/examples/test_ftdi.cpp            |  73 +++
 src/examples/test_multiple_client.cpp |  51 ++
 src/examples/test_multiple_server.cpp |  84 ++++
 src/examples/test_rs232.cpp           | 119 +++++
 src/examples/test_simple_client.cpp   |  44 ++
 src/examples/test_simple_server.cpp   |  65 +++
 src/serial/rs232.cpp                  | 357 ++++++++++++++
 src/serial/rs232.h                    | 424 ++++++++++++++++
 src/serial/rs232exceptions.cpp        |  11 +
 src/serial/rs232exceptions.h          |  60 +++
 src/sockets/socket.cpp                | 182 +++++++
 src/sockets/socket.h                  | 322 ++++++++++++
 src/sockets/socketclient.cpp          |  70 +++
 src/sockets/socketclient.h            | 152 ++++++
 src/sockets/socketexceptions.cpp      |  15 +
 src/sockets/socketexceptions.h        | 116 +++++
 src/sockets/socketserver.cpp          | 525 ++++++++++++++++++++
 src/sockets/socketserver.h            | 677 ++++++++++++++++++++++++++
 src/usb_ftdi/ftdiexceptions.cpp       |  37 ++
 src/usb_ftdi/ftdiexceptions.h         | 116 +++++
 src/usb_ftdi/ftdimodule.cpp           | 210 ++++++++
 src/usb_ftdi/ftdimodule.h             | 334 +++++++++++++
 src/usb_ftdi/ftdiserver.cpp           | 247 ++++++++++
 src/usb_ftdi/ftdiserver.h             | 395 +++++++++++++++
 39 files changed, 6848 insertions(+)
 create mode 100644 CMakeLists.txt
 create mode 100644 Findcomm.cmake
 create mode 100644 ReadMe.txt
 create mode 100644 doc/doxygen.conf
 create mode 100644 doc/doxygen_project_name.conf
 create mode 100644 doc/images/comm_states.eps
 create mode 100644 doc/images/comm_states.odp
 create mode 100644 doc/images/comm_states.png
 create mode 100644 doc/main.dox
 create mode 100644 src/CMakeLists.txt
 create mode 100644 src/comm.cpp
 create mode 100644 src/comm.h
 create mode 100644 src/commexceptions.cpp
 create mode 100644 src/commexceptions.h
 create mode 100644 src/examples/CMakeLists.txt
 create mode 100644 src/examples/test_ftdi.cpp
 create mode 100644 src/examples/test_multiple_client.cpp
 create mode 100644 src/examples/test_multiple_server.cpp
 create mode 100644 src/examples/test_rs232.cpp
 create mode 100644 src/examples/test_simple_client.cpp
 create mode 100644 src/examples/test_simple_server.cpp
 create mode 100644 src/serial/rs232.cpp
 create mode 100644 src/serial/rs232.h
 create mode 100644 src/serial/rs232exceptions.cpp
 create mode 100644 src/serial/rs232exceptions.h
 create mode 100644 src/sockets/socket.cpp
 create mode 100644 src/sockets/socket.h
 create mode 100644 src/sockets/socketclient.cpp
 create mode 100644 src/sockets/socketclient.h
 create mode 100644 src/sockets/socketexceptions.cpp
 create mode 100644 src/sockets/socketexceptions.h
 create mode 100644 src/sockets/socketserver.cpp
 create mode 100644 src/sockets/socketserver.h
 create mode 100644 src/usb_ftdi/ftdiexceptions.cpp
 create mode 100644 src/usb_ftdi/ftdiexceptions.h
 create mode 100644 src/usb_ftdi/ftdimodule.cpp
 create mode 100644 src/usb_ftdi/ftdimodule.h
 create mode 100644 src/usb_ftdi/ftdiserver.cpp
 create mode 100644 src/usb_ftdi/ftdiserver.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..212386f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Pre-requisites about cmake itself
+CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
+
+if(COMMAND cmake_policy)
+  cmake_policy(SET CMP0005 NEW) 
+  cmake_policy(SET CMP0003 NEW) 
+endif(COMMAND cmake_policy)
+
+# The project name and the type of project
+PROJECT(comm)
+
+SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
+SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
+SET(CMAKE_INSTALL_PREFIX /usr/local)
+
+IF (NOT CMAKE_BUILD_TYPE)
+ SET(CMAKE_BUILD_TYPE "DEBUG") 
+ENDIF (NOT CMAKE_BUILD_TYPE)
+
+SET(CMAKE_C_FLAGS_DEBUG "-g -Wall")
+SET(CMAKE_C_FLAGS_RELEASE "-O3")
+
+ADD_SUBDIRECTORY(src)
+
+FIND_PACKAGE(Doxygen)
+
+FIND_PATH(IRI_DOC_DIR doxygen.conf ${CMAKE_SOURCE_DIR}/doc/iri_doc/)
+IF (IRI_DOC_DIR)
+  ADD_CUSTOM_TARGET (doc ${DOXYGEN_EXECUTABLE} ${CMAKE_SOURCE_DIR}/doc/iri_doc/doxygen.conf)
+ELSE (IRI_DOC_DIR)
+  ADD_CUSTOM_TARGET (doc ${DOXYGEN_EXECUTABLE} ${CMAKE_SOURCE_DIR}/doc/doxygen.conf)
+ENDIF (IRI_DOC_DIR)
+
+ADD_CUSTOM_TARGET (distclean @echo cleaning cmake files)
+
+IF (UNIX)
+  ADD_CUSTOM_COMMAND(
+    COMMENT "distribution clean"
+    COMMAND make ARGS clean
+    COMMAND rm ARGS -rf ${CMAKE_SOURCE_DIR}/build/*
+
+    TARGET  distclean
+  )
+ELSE(UNIX)
+  ADD_CUSTOM_COMMAND(
+    COMMENT "distclean only implemented in unix"
+    TARGET  distclean
+  )
+ENDIF(UNIX)
+
+ADD_CUSTOM_TARGET (uninstall @echo uninstall package)
+
+IF (UNIX)
+  ADD_CUSTOM_COMMAND(
+    COMMENT "uninstall package"
+    COMMAND xargs ARGS rm < install_manifest.txt
+
+    TARGET  uninstall
+  )
+ELSE(UNIX)
+  ADD_CUSTOM_COMMAND(
+    COMMENT "uninstall only implemented in unix"
+    TARGET  uninstall
+  )
+ENDIF(UNIX)
+
+
+IF (UNIX)
+  SET(CPACK_PACKAGE_FILE_NAME "iri-${PROJECT_NAME}-dev-${CPACK_PACKAGE_VERSION}${DISTRIB}${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
+  SET(CPACK_PACKAGE_NAME "iri-${PROJECT_NAME}-dev")
+  SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Part of IRI-laboratory libraries. More information at http://wikiri.upc.es/index.php/Robotics_Lab")
+  SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
+  SET(CPACK_GENERATOR "DEB")
+  SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "labrobotica@iri.upc.edu")
+  SET(CPACK_SET_DESTDIR "ON")  # Necessary because of the absolute install paths
+
+  INCLUDE(CPack)
+ELSE(UNIX)
+  ADD_CUSTOM_COMMAND(
+    COMMENT "packaging only implemented in unix"
+    TARGET  uninstall
+  )
+ENDIF(UNIX)
+
diff --git a/Findcomm.cmake b/Findcomm.cmake
new file mode 100644
index 0000000..8408e22
--- /dev/null
+++ b/Findcomm.cmake
@@ -0,0 +1,21 @@
+FIND_PATH(comm_INCLUDE_DIR comm.h commexceptions.h rs232.h rs232exceptions.h ftdiserver.h ftdimodule.h ftdiexcetpions.h socket.h socketclient.h socketserver.h socketexceptions.h /usr/include/iridrivers /usr/local/include/iridrivers)
+
+
+FIND_LIBRARY(comm_LIBRARY
+    NAMES comm
+    PATHS /usr/lib /usr/local/lib /usr/local/lib/iridrivers) 
+
+IF (comm_INCLUDE_DIR AND comm_LIBRARY)
+   SET(comm_FOUND TRUE)
+ENDIF (comm_INCLUDE_DIR AND comm_LIBRARY)
+
+IF (comm_FOUND)
+   IF (NOT comm_FIND_QUIETLY)
+      MESSAGE(STATUS "Found comm library: ${comm_LIBRARY}")
+   ENDIF (NOT comm_FIND_QUIETLY)
+ELSE (comm_FOUND)
+   IF (comm_FIND_REQUIRED)
+      MESSAGE(FATAL_ERROR "Could not find comm library")
+   ENDIF (comm_FIND_REQUIRED)
+ENDIF (comm_FOUND)
+
diff --git a/ReadMe.txt b/ReadMe.txt
new file mode 100644
index 0000000..6095168
--- /dev/null
+++ b/ReadMe.txt
@@ -0,0 +1,17 @@
+Copyright (C) 2009-2010 Institut de Robòtica i Informàtica Industrial, CSIC-UPC.
+Author shernand (shernand@iri.upc.edu)
+All rights reserved.
+
+This file is part of IRI communications library
+IRI communications library 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.
+  
+This program 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 this program.  If not, see <http://www.gnu.org/licenses/>
diff --git a/doc/doxygen.conf b/doc/doxygen.conf
new file mode 100644
index 0000000..347af2e
--- /dev/null
+++ b/doc/doxygen.conf
@@ -0,0 +1,251 @@
+# Doxyfile 1.5.5
+
+@INCLUDE_PATH = ../doc/
+@INCLUDE = doxygen_project_name.conf
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NUMBER         = 
+OUTPUT_DIRECTORY       = ../doc
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = NO
+ABBREVIATE_BRIEF       = 
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = 
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+TYPEDEF_HIDES_STRUCT   = NO
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = NO
+SORT_BRIEF_DOCS        = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = ../src \
+                         ../doc/main.dox
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.c \
+                         *.h \
+                         *.cpp
+RECURSIVE              = YES
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = *.tab.c \
+                         *.tab.h \
+                         lex* \
+                         *glr.h \
+                         *llr.h \
+                         *glr.c \
+                         *llr.c \
+                         *general.h
+EXCLUDE_SYMBOLS        = 
+EXAMPLE_PATH           = ../src/examples
+EXAMPLE_PATTERNS       = 
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = ../doc/images
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+HTML_DYNAMIC_SECTIONS  = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = _USE_MPI=1
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            = 
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = NO
+INCLUDED_BY_GRAPH      = NO
+CALL_GRAPH             = YES
+CALLER_GRAPH           = YES
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = NO
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 2
+DOT_TRANSPARENT        = YES
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/doc/doxygen_project_name.conf b/doc/doxygen_project_name.conf
new file mode 100644
index 0000000..b8e4e36
--- /dev/null
+++ b/doc/doxygen_project_name.conf
@@ -0,0 +1 @@
+PROJECT_NAME = "IRI communications"
diff --git a/doc/images/comm_states.eps b/doc/images/comm_states.eps
new file mode 100644
index 0000000..0eb51e7
--- /dev/null
+++ b/doc/images/comm_states.eps
@@ -0,0 +1,506 @@
+%!PS-Adobe-3.0 EPSF-3.0 
+%%BoundingBox: 0 0 794 595
+%%Pages: 0
+%%Creator: Sun Microsystems, Inc.
+%%Title: none
+%%CreationDate: none
+%%LanguageLevel: 2
+%%EndComments
+%%BeginProlog
+%%BeginResource: procset SDRes-Prolog 1.0 0
+/b4_inc_state save def
+/dict_count countdictstack def
+/op_count count 1 sub def
+userdict begin
+0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit[] 0 setdash newpath
+/languagelevel where {pop languagelevel 1 ne {false setstrokeadjust false setoverprint} if} if
+/bdef {bind def} bind def
+/c {setgray} bdef
+/l {neg lineto} bdef
+/rl {neg rlineto} bdef
+/lc {setlinecap} bdef
+/lj {setlinejoin} bdef
+/lw {setlinewidth} bdef
+/ml {setmiterlimit} bdef
+/ld {setdash} bdef
+/m {neg moveto} bdef
+/ct {6 2 roll neg 6 2 roll neg 6 2 roll neg curveto} bdef
+/r {rotate} bdef
+/t {neg translate} bdef
+/s {scale} bdef
+/sw {show} bdef
+/gs {gsave} bdef
+/gr {grestore} bdef
+/f {findfont dup length dict begin
+{1 index /FID ne {def} {pop pop} ifelse} forall /Encoding ISOLatin1Encoding def
+currentdict end /NFont exch definefont pop /NFont findfont} bdef
+/p {closepath} bdef
+/sf {scalefont setfont} bdef
+/ef {eofill}bdef
+/pc {closepath stroke}bdef
+/ps {stroke}bdef
+/pum {matrix currentmatrix}bdef
+/pom {setmatrix}bdef
+/bs {/aString exch def /nXOfs exch def /nWidth exch def currentpoint nXOfs 0 rmoveto pum nWidth aString stringwidth pop div 1 scale aString show pom moveto} bdef
+%%EndResource
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+%%EndPageSetup
+pum
+0.02835 0.02833 s 
+0 -21000 t
+/tm matrix currentmatrix def
+gs
+0 0 m 27999 0 l 27999 20999 l 0 20999 l 0 0 l eoclip newpath
+gs
+0 0 m 27999 0 l 27999 20999 l 0 20999 l 0 0 l eoclip newpath
+0 0 m 28000 0 l 28000 21000 l 0 21000 l 0 0 l eoclip newpath
+gs
+gs
+pum
+1402 19590 t
+pom
+gr
+gr
+gs
+gs
+pum
+13937 19590 t
+pom
+gr
+gr
+0 lw 1 lj 0.000 c 10160 7773 m  9459 7773 8890 7204 8890 6503 ct 8890 5802 9459 5233 10160 5233 ct 
+10861 5233 11430 5802 11430 6503 ct 11430 7204 10861 7773 10160 7773 ct pc
+gs
+gs
+pum
+9340 6638 t
+206 -223 m  206 -187 l  196 -193 185 -197 174 -200 ct 163 -203 152 -205 141 -205 ct 
+117 -205 97 -197 84 -181 ct 70 -166 63 -144 63 -115 ct 63 -87 70 -65 84 -50 ct 
+97 -34 117 -26 141 -26 ct 152 -26 163 -28 174 -31 ct 185 -34 196 -38 206 -44 ct 
+206 -9 l  196 -4 185 0 173 2 ct 162 5 150 6 137 6 ct 102 6 75 -5 54 -27 ct 34 -49 23 -78 23 -115 ct 
+23 -153 34 -183 54 -205 ct 75 -226 104 -237 140 -237 ct 151 -237 163 -236 174 -233 ct 
+185 -231 196 -227 206 -223 ct p ef
+407 -196 m  403 -198 398 -200 393 -201 ct 388 -203 382 -203 376 -203 ct 355 -203 338 -196 327 -182 ct 
+315 -168 310 -148 310 -122 ct 310 0 l  271 0 l  271 -231 l  310 -231 l  310 -195 l 
+318 -210 328 -220 341 -227 ct 354 -234 369 -237 388 -237 ct 390 -237 393 -237 396 -236 ct 
+399 -236 403 -236 407 -235 ct 407 -196 l  p ef
+644 -125 m  644 -106 l  469 -106 l  471 -80 479 -60 493 -47 ct 507 -33 526 -26 552 -26 ct 
+566 -26 580 -28 594 -32 ct 608 -35 621 -41 635 -48 ct 635 -12 l  621 -6 607 -2 593 1 ct 
+579 4 564 6 549 6 ct 512 6 483 -5 462 -26 ct 440 -48 429 -77 429 -113 ct 429 -151 440 -181 460 -204 ct 
+480 -226 508 -237 543 -237 ct 574 -237 598 -227 617 -207 ct 635 -187 644 -160 644 -125 ct 
+p
+606 -136 m  605 -157 600 -174 588 -186 ct 577 -199 562 -205 543 -205 ct 522 -205 505 -199 492 -187 ct 
+480 -175 472 -158 470 -136 ct 606 -136 l  p ef
+814 -116 m  783 -116 762 -113 750 -106 ct 738 -99 732 -87 732 -70 ct 732 -56 737 -46 746 -38 ct 
+755 -30 767 -26 782 -26 ct 803 -26 820 -33 833 -48 ct 845 -63 852 -83 852 -108 ct 
+852 -116 l  814 -116 l  p
+890 -132 m  890 0 l  852 0 l  852 -35 l  843 -21 832 -11 819 -4 ct 806 3 791 6 772 6 ct 
+748 6 729 -1 715 -14 ct 701 -27 694 -45 694 -67 ct 694 -93 703 -113 721 -126 ct 
+738 -139 764 -146 799 -146 ct 852 -146 l  852 -150 l  852 -167 846 -181 835 -190 ct 
+823 -200 807 -205 786 -205 ct 773 -205 760 -203 747 -200 ct 735 -197 723 -192 711 -186 ct 
+711 -221 l  725 -226 739 -230 752 -233 ct 765 -236 778 -237 790 -237 ct 823 -237 848 -228 865 -211 ct 
+882 -194 890 -167 890 -132 ct p ef
+1004 -297 m  1004 -232 l  1083 -232 l  1083 -202 l  1004 -202 l  1004 -76 l 
+1004 -57 1007 -45 1012 -40 ct 1017 -35 1028 -32 1044 -32 ct 1083 -32 l  1083 0 l 
+1044 0 l  1014 0 994 -5 983 -16 ct 972 -27 966 -47 966 -76 ct 966 -202 l  938 -202 l 
+938 -232 l  966 -232 l  966 -297 l  1004 -297 l  p ef
+1330 -125 m  1330 -106 l  1155 -106 l  1157 -80 1165 -60 1179 -47 ct 1193 -33 1212 -26 1238 -26 ct 
+1252 -26 1266 -28 1280 -32 ct 1294 -35 1307 -41 1321 -48 ct 1321 -12 l  1307 -6 1293 -2 1279 1 ct 
+1265 4 1250 6 1235 6 ct 1198 6 1169 -5 1148 -26 ct 1126 -48 1115 -77 1115 -113 ct 
+1115 -151 1126 -181 1146 -204 ct 1166 -226 1194 -237 1229 -237 ct 1260 -237 1284 -227 1303 -207 ct 
+1321 -187 1330 -160 1330 -125 ct p
+1292 -136 m  1291 -157 1286 -174 1274 -186 ct 1263 -199 1248 -205 1229 -205 ct 
+1208 -205 1191 -199 1178 -187 ct 1166 -175 1158 -158 1156 -136 ct 1292 -136 l 
+p ef
+1547 -197 m  1547 -322 l  1585 -322 l  1585 0 l  1547 0 l  1547 -35 l 
+1539 -21 1529 -11 1517 -4 ct 1505 2 1490 6 1473 6 ct 1445 6 1422 -5 1405 -28 ct 
+1387 -50 1378 -80 1378 -116 ct 1378 -152 1387 -182 1405 -204 ct 1422 -227 1445 -238 1473 -238 ct 
+1490 -238 1505 -234 1517 -228 ct 1529 -221 1539 -211 1547 -197 ct p
+1418 -116 m  1418 -88 1423 -66 1435 -50 ct 1446 -34 1462 -26 1482 -26 ct 1502 -26 1518 -34 1530 -50 ct 
+1541 -66 1547 -88 1547 -116 ct 1547 -144 1541 -166 1530 -182 ct 1518 -198 1502 -206 1482 -206 ct 
+1462 -206 1446 -198 1435 -182 ct 1423 -166 1418 -144 1418 -116 ct p ef
+pom
+gr
+gr
+19685 7773 m  18984 7773 18415 7204 18415 6503 ct 18415 5802 18984 5233 19685 5233 ct 
+20386 5233 20955 5802 20955 6503 ct 20955 7204 20386 7773 19685 7773 ct pc
+gs
+gs
+pum
+18891 6638 t
+130 -205 m  109 -205 93 -197 81 -181 ct 69 -165 63 -143 63 -115 ct 63 -88 69 -66 81 -50 ct 
+93 -34 109 -26 130 -26 ct 150 -26 166 -34 178 -50 ct 189 -66 195 -88 195 -116 ct 
+195 -143 189 -165 178 -181 ct 166 -197 150 -205 130 -205 ct p
+130 -237 m  163 -237 189 -226 207 -205 ct 226 -183 236 -154 236 -115 ct 236 -78 226 -48 207 -26 ct 
+189 -5 163 6 130 6 ct 96 6 70 -5 52 -26 ct 33 -48 23 -78 23 -115 ct 23 -154 33 -183 52 -205 ct 
+70 -226 96 -237 130 -237 ct p ef
+335 -35 m  335 88 l  296 88 l  296 -231 l  335 -231 l  335 -196 l  343 -210 353 -220 365 -227 ct 
+377 -234 392 -237 409 -237 ct 437 -237 459 -226 477 -204 ct 495 -181 503 -152 503 -115 ct 
+503 -79 495 -50 477 -27 ct 459 -5 437 6 409 6 ct 392 6 377 3 365 -4 ct 353 -11 343 -21 335 -35 ct 
+p
+464 -116 m  464 -143 458 -165 447 -181 ct 435 -197 419 -205 399 -205 ct 379 -205 363 -197 352 -181 ct 
+340 -165 335 -143 335 -115 ct 335 -88 340 -66 352 -50 ct 363 -34 379 -26 399 -26 ct 
+419 -26 435 -34 447 -50 ct 458 -66 464 -88 464 -116 ct p ef
+763 -125 m  763 -106 l  588 -106 l  590 -80 598 -60 612 -47 ct 626 -33 645 -26 671 -26 ct 
+685 -26 699 -28 713 -32 ct 727 -35 740 -41 754 -48 ct 754 -12 l  740 -6 726 -2 712 1 ct 
+698 4 683 6 668 6 ct 631 6 602 -5 581 -26 ct 559 -48 548 -77 548 -113 ct 548 -151 559 -181 579 -204 ct 
+599 -226 627 -237 662 -237 ct 693 -237 717 -227 736 -207 ct 754 -187 763 -160 763 -125 ct 
+p
+725 -136 m  724 -157 719 -174 707 -186 ct 696 -199 681 -205 662 -205 ct 641 -205 624 -199 611 -187 ct 
+599 -175 591 -158 589 -136 ct 725 -136 l  p ef
+1019 -140 m  1019 0 l  981 0 l  981 -138 l  981 -160 977 -177 968 -188 ct 
+960 -199 947 -204 930 -204 ct 909 -204 893 -197 881 -184 ct 870 -171 864 -153 864 -131 ct 
+864 0 l  825 0 l  825 -231 l  864 -231 l  864 -195 l  873 -209 883 -220 896 -227 ct 
+908 -234 922 -237 938 -237 ct 965 -237 985 -229 999 -212 ct 1012 -196 1019 -172 1019 -140 ct 
+p ef
+1292 -125 m  1292 -106 l  1117 -106 l  1119 -80 1127 -60 1141 -47 ct 1155 -33 1174 -26 1200 -26 ct 
+1214 -26 1228 -28 1242 -32 ct 1256 -35 1269 -41 1283 -48 ct 1283 -12 l  1269 -6 1255 -2 1241 1 ct 
+1227 4 1212 6 1197 6 ct 1160 6 1131 -5 1110 -26 ct 1088 -48 1077 -77 1077 -113 ct 
+1077 -151 1088 -181 1108 -204 ct 1128 -226 1156 -237 1191 -237 ct 1222 -237 1246 -227 1265 -207 ct 
+1283 -187 1292 -160 1292 -125 ct p
+1254 -136 m  1253 -157 1248 -174 1236 -186 ct 1225 -199 1210 -205 1191 -205 ct 
+1170 -205 1153 -199 1140 -187 ct 1128 -175 1120 -158 1118 -136 ct 1254 -136 l 
+p ef
+1509 -197 m  1509 -322 l  1547 -322 l  1547 0 l  1509 0 l  1509 -35 l 
+1501 -21 1491 -11 1479 -4 ct 1467 2 1452 6 1435 6 ct 1407 6 1384 -5 1367 -28 ct 
+1349 -50 1340 -80 1340 -116 ct 1340 -152 1349 -182 1367 -204 ct 1384 -227 1407 -238 1435 -238 ct 
+1452 -238 1467 -234 1479 -228 ct 1491 -221 1501 -211 1509 -197 ct p
+1380 -116 m  1380 -88 1385 -66 1397 -50 ct 1408 -34 1424 -26 1444 -26 ct 1464 -26 1480 -34 1492 -50 ct 
+1503 -66 1509 -88 1509 -116 ct 1509 -144 1503 -166 1492 -182 ct 1480 -198 1464 -206 1444 -206 ct 
+1424 -206 1408 -198 1397 -182 ct 1385 -166 1380 -144 1380 -116 ct p ef
+pom
+gr
+gr
+19685 15393 m  18984 15393 18415 14824 18415 14123 ct 18415 13422 18984 12853 19685 12853 ct 
+20386 12853 20955 13422 20955 14123 ct 20955 14824 20386 15393 19685 15393 ct 
+pc
+gs
+gs
+pum
+18547 14258 t
+206 -223 m  206 -187 l  196 -193 185 -197 174 -200 ct 163 -203 152 -205 141 -205 ct 
+117 -205 97 -197 84 -181 ct 70 -166 63 -144 63 -115 ct 63 -87 70 -65 84 -50 ct 
+97 -34 117 -26 141 -26 ct 152 -26 163 -28 174 -31 ct 185 -34 196 -38 206 -44 ct 
+206 -9 l  196 -4 185 0 173 2 ct 162 5 150 6 137 6 ct 102 6 75 -5 54 -27 ct 34 -49 23 -78 23 -115 ct 
+23 -153 34 -183 54 -205 ct 75 -226 104 -237 140 -237 ct 151 -237 163 -236 174 -233 ct 
+185 -231 196 -227 206 -223 ct p ef
+363 -205 m  342 -205 326 -197 314 -181 ct 302 -165 296 -143 296 -115 ct 296 -88 302 -66 314 -50 ct 
+326 -34 342 -26 363 -26 ct 383 -26 399 -34 411 -50 ct 422 -66 428 -88 428 -116 ct 
+428 -143 422 -165 411 -181 ct 399 -197 383 -205 363 -205 ct p
+363 -237 m  396 -237 422 -226 440 -205 ct 459 -183 469 -154 469 -115 ct 469 -78 459 -48 440 -26 ct 
+422 -5 396 6 363 6 ct 329 6 303 -5 285 -26 ct 266 -48 256 -78 256 -115 ct 256 -154 266 -183 285 -205 ct 
+303 -226 329 -237 363 -237 ct p ef
+723 -140 m  723 0 l  685 0 l  685 -138 l  685 -160 681 -177 672 -188 ct 664 -199 651 -204 634 -204 ct 
+613 -204 597 -197 585 -184 ct 574 -171 568 -153 568 -131 ct 568 0 l  529 0 l 
+529 -231 l  568 -231 l  568 -195 l  577 -209 587 -220 600 -227 ct 612 -234 626 -237 642 -237 ct 
+669 -237 689 -229 703 -212 ct 716 -196 723 -172 723 -140 ct p ef
+915 -322 m  915 -290 l  879 -290 l  865 -290 856 -288 850 -282 ct 845 -277 842 -267 842 -252 ct 
+842 -232 l  905 -232 l  905 -202 l  842 -202 l  842 0 l  804 0 l  804 -202 l 
+768 -202 l  768 -232 l  804 -232 l  804 -248 l  804 -274 810 -293 822 -304 ct 
+834 -316 853 -322 879 -322 ct 915 -322 l  p ef
+946 -232 m  984 -232 l  984 -1 l  946 -1 l  946 -232 l  p
+946 -322 m  984 -322 l  984 -274 l  946 -274 l  946 -322 l  p ef
+1216 -118 m  1216 -146 1210 -167 1199 -182 ct 1188 -198 1172 -205 1151 -205 ct 
+1131 -205 1115 -198 1104 -182 ct 1092 -167 1087 -146 1087 -118 ct 1087 -91 1092 -70 1104 -55 ct 
+1115 -39 1131 -32 1151 -32 ct 1172 -32 1188 -39 1199 -55 ct 1210 -70 1216 -91 1216 -118 ct 
+p
+1254 -29 m  1254 11 1245 40 1228 59 ct 1210 78 1184 88 1148 88 ct 1134 88 1122 87 1110 85 ct 
+1098 83 1086 80 1075 76 ct 1075 39 l  1086 45 1097 49 1108 52 ct 1119 55 1130 57 1142 57 ct 
+1166 57 1185 50 1197 37 ct 1210 24 1216 4 1216 -22 ct 1216 -41 l  1208 -27 1198 -17 1186 -10 ct 
+1174 -3 1159 0 1142 0 ct 1114 0 1091 -11 1073 -32 ct 1056 -54 1047 -83 1047 -118 ct 
+1047 -154 1056 -183 1073 -205 ct 1091 -226 1114 -237 1142 -237 ct 1159 -237 1174 -234 1186 -227 ct 
+1198 -220 1208 -210 1216 -196 ct 1216 -231 l  1254 -231 l  1254 -29 l  p ef
+1327 -91 m  1327 -231 l  1365 -231 l  1365 -93 l  1365 -71 1369 -54 1378 -43 ct 
+1386 -33 1399 -27 1416 -27 ct 1437 -27 1453 -34 1465 -47 ct 1477 -60 1483 -78 1483 -100 ct 
+1483 -231 l  1521 -231 l  1521 0 l  1483 0 l  1483 -36 l  1473 -21 1463 -11 1451 -4 ct 
+1438 3 1424 6 1408 6 ct 1382 6 1361 -2 1348 -19 ct 1334 -35 1327 -59 1327 -91 ct 
+p
+p ef
+1732 -196 m  1728 -198 1723 -200 1718 -201 ct 1713 -203 1707 -203 1701 -203 ct 
+1680 -203 1663 -196 1652 -182 ct 1640 -168 1635 -148 1635 -122 ct 1635 0 l  1596 0 l 
+1596 -231 l  1635 -231 l  1635 -195 l  1643 -210 1653 -220 1666 -227 ct 1679 -234 1694 -237 1713 -237 ct 
+1715 -237 1718 -237 1721 -236 ct 1724 -236 1728 -236 1732 -235 ct 1732 -196 l 
+p ef
+1969 -125 m  1969 -106 l  1794 -106 l  1796 -80 1804 -60 1818 -47 ct 1832 -33 1851 -26 1877 -26 ct 
+1891 -26 1905 -28 1919 -32 ct 1933 -35 1946 -41 1960 -48 ct 1960 -12 l  1946 -6 1932 -2 1918 1 ct 
+1904 4 1889 6 1874 6 ct 1837 6 1808 -5 1787 -26 ct 1765 -48 1754 -77 1754 -113 ct 
+1754 -151 1765 -181 1785 -204 ct 1805 -226 1833 -237 1868 -237 ct 1899 -237 1923 -227 1942 -207 ct 
+1960 -187 1969 -160 1969 -125 ct p
+1931 -136 m  1930 -157 1925 -174 1913 -186 ct 1902 -199 1887 -205 1868 -205 ct 
+1847 -205 1830 -199 1817 -187 ct 1805 -175 1797 -158 1795 -136 ct 1931 -136 l 
+p ef
+2186 -197 m  2186 -322 l  2224 -322 l  2224 0 l  2186 0 l  2186 -35 l 
+2178 -21 2168 -11 2156 -4 ct 2144 2 2129 6 2112 6 ct 2084 6 2061 -5 2044 -28 ct 
+2026 -50 2017 -80 2017 -116 ct 2017 -152 2026 -182 2044 -204 ct 2061 -227 2084 -238 2112 -238 ct 
+2129 -238 2144 -234 2156 -228 ct 2168 -221 2178 -211 2186 -197 ct p
+2057 -116 m  2057 -88 2062 -66 2074 -50 ct 2085 -34 2101 -26 2121 -26 ct 2141 -26 2157 -34 2169 -50 ct 
+2180 -66 2186 -88 2186 -116 ct 2186 -144 2180 -166 2169 -182 ct 2157 -198 2141 -206 2121 -206 ct 
+2101 -206 2085 -198 2074 -182 ct 2062 -166 2057 -144 2057 -116 ct p ef
+pom
+gr
+gr
+18785 5603 m  18414 5307 l  18666 5144 l  18785 5603 l  p ef
+1 lw 0 lj 11058 5603 m  11064 5543 l  11080 5484 l  11107 5428 l  11145 5374 l 
+11249 5270 l  11390 5175 l  11565 5087 l  11771 5007 l  12265 4869 l  12850 4762 l 
+13503 4685 l  14922 4624 l  16340 4685 l  16993 4762 l  17578 4869 l  18072 5007 l 
+18278 5087 l  18453 5175 l  18594 5270 l  18620 5296 l  ps
+gs
+gs
+pum
+14076 4336 t
+130 -205 m  109 -205 93 -197 81 -181 ct 69 -165 63 -143 63 -115 ct 63 -88 69 -66 81 -50 ct 
+93 -34 109 -26 130 -26 ct 150 -26 166 -34 178 -50 ct 189 -66 195 -88 195 -116 ct 
+195 -143 189 -165 178 -181 ct 166 -197 150 -205 130 -205 ct p
+130 -237 m  163 -237 189 -226 207 -205 ct 226 -183 236 -154 236 -115 ct 236 -78 226 -48 207 -26 ct 
+189 -5 163 6 130 6 ct 96 6 70 -5 52 -26 ct 33 -48 23 -78 23 -115 ct 23 -154 33 -183 52 -205 ct 
+70 -226 96 -237 130 -237 ct p ef
+335 -35 m  335 88 l  296 88 l  296 -231 l  335 -231 l  335 -196 l  343 -210 353 -220 365 -227 ct 
+377 -234 392 -237 409 -237 ct 437 -237 459 -226 477 -204 ct 495 -181 503 -152 503 -115 ct 
+503 -79 495 -50 477 -27 ct 459 -5 437 6 409 6 ct 392 6 377 3 365 -4 ct 353 -11 343 -21 335 -35 ct 
+p
+464 -116 m  464 -143 458 -165 447 -181 ct 435 -197 419 -205 399 -205 ct 379 -205 363 -197 352 -181 ct 
+340 -165 335 -143 335 -115 ct 335 -88 340 -66 352 -50 ct 363 -34 379 -26 399 -26 ct 
+419 -26 435 -34 447 -50 ct 458 -66 464 -88 464 -116 ct p ef
+763 -125 m  763 -106 l  588 -106 l  590 -80 598 -60 612 -47 ct 626 -33 645 -26 671 -26 ct 
+685 -26 699 -28 713 -32 ct 727 -35 740 -41 754 -48 ct 754 -12 l  740 -6 726 -2 712 1 ct 
+698 4 683 6 668 6 ct 631 6 602 -5 581 -26 ct 559 -48 548 -77 548 -113 ct 548 -151 559 -181 579 -204 ct 
+599 -226 627 -237 662 -237 ct 693 -237 717 -227 736 -207 ct 754 -187 763 -160 763 -125 ct 
+p
+725 -136 m  724 -157 719 -174 707 -186 ct 696 -199 681 -205 662 -205 ct 641 -205 624 -199 611 -187 ct 
+599 -175 591 -158 589 -136 ct 725 -136 l  p ef
+1019 -140 m  1019 0 l  981 0 l  981 -138 l  981 -160 977 -177 968 -188 ct 
+960 -199 947 -204 930 -204 ct 909 -204 893 -197 881 -184 ct 870 -171 864 -153 864 -131 ct 
+864 0 l  825 0 l  825 -231 l  864 -231 l  864 -195 l  873 -209 883 -220 896 -227 ct 
+908 -234 922 -237 938 -237 ct 965 -237 985 -229 999 -212 ct 1012 -196 1019 -172 1019 -140 ct 
+p ef
+1185 -322 m  1167 -290 1153 -259 1144 -228 ct 1135 -197 1131 -166 1131 -134 ct 
+1131 -102 1135 -70 1144 -39 ct 1153 -8 1167 23 1185 55 ct 1152 55 l  1131 23 1116 -9 1106 -41 ct 
+1095 -72 1090 -103 1090 -134 ct 1090 -164 1095 -195 1106 -227 ct 1116 -258 1131 -289 1152 -322 ct 
+1185 -322 l  p ef
+1253 -322 m  1286 -322 l  1307 -289 1322 -258 1332 -227 ct 1343 -195 1348 -164 1348 -134 ct 
+1348 -103 1343 -72 1332 -41 ct 1322 -9 1307 23 1286 55 ct 1253 55 l  1271 23 1285 -8 1294 -39 ct 
+1303 -70 1307 -102 1307 -134 ct 1307 -166 1303 -197 1294 -228 ct 1285 -259 1271 -290 1253 -322 ct 
+p ef
+pom
+gr
+gr
+11058 7401 m  11508 7251 l  11508 7551 l  11058 7401 l  p ef
+18785 7401 m  11418 7401 l  ps
+gs
+gs
+pum
+14208 7247 t
+206 -223 m  206 -187 l  196 -193 185 -197 174 -200 ct 163 -203 152 -205 141 -205 ct 
+117 -205 97 -197 84 -181 ct 70 -166 63 -144 63 -115 ct 63 -87 70 -65 84 -50 ct 
+97 -34 117 -26 141 -26 ct 152 -26 163 -28 174 -31 ct 185 -34 196 -38 206 -44 ct 
+206 -9 l  196 -4 185 0 173 2 ct 162 5 150 6 137 6 ct 102 6 75 -5 54 -27 ct 34 -49 23 -78 23 -115 ct 
+23 -153 34 -183 54 -205 ct 75 -226 104 -237 140 -237 ct 151 -237 163 -236 174 -233 ct 
+185 -231 196 -227 206 -223 ct p ef
+273 -322 m  311 -322 l  311 0 l  273 0 l  273 -322 l  p ef
+481 -205 m  460 -205 444 -197 432 -181 ct 420 -165 414 -143 414 -115 ct 414 -88 420 -66 432 -50 ct 
+444 -34 460 -26 481 -26 ct 501 -26 517 -34 529 -50 ct 540 -66 546 -88 546 -116 ct 
+546 -143 540 -165 529 -181 ct 517 -197 501 -205 481 -205 ct p
+481 -237 m  514 -237 540 -226 558 -205 ct 577 -183 587 -154 587 -115 ct 587 -78 577 -48 558 -26 ct 
+540 -5 514 6 481 6 ct 447 6 421 -5 403 -26 ct 384 -48 374 -78 374 -115 ct 374 -154 384 -183 403 -205 ct 
+421 -226 447 -237 481 -237 ct p ef
+797 -225 m  797 -189 l  787 -194 775 -198 764 -201 ct 752 -204 740 -205 728 -205 ct 
+709 -205 695 -202 685 -197 ct 676 -191 671 -182 671 -170 ct 671 -162 675 -155 681 -150 ct 
+688 -145 702 -140 722 -135 ct 735 -132 l  762 -127 781 -119 793 -108 ct 804 -97 810 -83 810 -64 ct 
+810 -42 801 -25 784 -13 ct 767 0 744 6 714 6 ct 702 6 689 5 675 2 ct 662 0 648 -4 633 -8 ct 
+633 -48 l  647 -40 661 -35 674 -31 ct 688 -28 702 -26 715 -26 ct 733 -26 747 -29 756 -35 ct 
+766 -41 771 -50 771 -61 ct 771 -71 767 -79 760 -85 ct 753 -90 738 -96 715 -101 ct 
+701 -104 l  678 -109 661 -116 650 -127 ct 640 -137 635 -151 635 -169 ct 635 -191 642 -207 658 -219 ct 
+673 -231 695 -237 723 -237 ct 737 -237 751 -236 763 -234 ct 775 -232 787 -229 797 -225 ct 
+p ef
+1068 -125 m  1068 -106 l  893 -106 l  895 -80 903 -60 917 -47 ct 931 -33 950 -26 976 -26 ct 
+990 -26 1004 -28 1018 -32 ct 1032 -35 1045 -41 1059 -48 ct 1059 -12 l  1045 -6 1031 -2 1017 1 ct 
+1003 4 988 6 973 6 ct 936 6 907 -5 886 -26 ct 864 -48 853 -77 853 -113 ct 853 -151 864 -181 884 -204 ct 
+904 -226 932 -237 967 -237 ct 998 -237 1022 -227 1041 -207 ct 1059 -187 1068 -160 1068 -125 ct 
+p
+1030 -136 m  1029 -157 1024 -174 1012 -186 ct 1001 -199 986 -205 967 -205 ct 
+946 -205 929 -199 916 -187 ct 904 -175 896 -158 894 -136 ct 1030 -136 l  p ef
+1223 -322 m  1205 -290 1191 -259 1182 -228 ct 1173 -197 1169 -166 1169 -134 ct 
+1169 -102 1173 -70 1182 -39 ct 1191 -8 1205 23 1223 55 ct 1190 55 l  1169 23 1154 -9 1144 -41 ct 
+1133 -72 1128 -103 1128 -134 ct 1128 -164 1133 -195 1144 -227 ct 1154 -258 1169 -289 1190 -322 ct 
+1223 -322 l  p ef
+1291 -322 m  1324 -322 l  1345 -289 1360 -258 1370 -227 ct 1381 -195 1386 -164 1386 -134 ct 
+1386 -103 1381 -72 1370 -41 ct 1360 -9 1345 23 1324 55 ct 1291 55 l  1309 23 1323 -8 1332 -39 ct 
+1341 -70 1345 -102 1345 -134 ct 1345 -166 1341 -197 1332 -228 ct 1323 -259 1309 -290 1291 -322 ct 
+p ef
+pom
+gr
+gr
+10159 5233 m  9910 4829 l  10202 4761 l  10159 5233 l  p ef
+4475 2927 m  4479 2989 l  4491 3047 l  4511 3101 l  4539 3153 l  4616 3246 l 
+4719 3329 l  4848 3400 l  5000 3462 l  5363 3563 l  5793 3638 l  6273 3697 l 
+7317 3799 l  8361 3936 l  8841 4039 l  9271 4175 l  9462 4259 l  9634 4355 l 
+9786 4463 l  9915 4585 l  10018 4722 l  10095 4875 l  10097 4880 l  ps
+gs
+gs
+pum
+5953 3331 t
+206 -223 m  206 -187 l  196 -193 185 -197 174 -200 ct 163 -203 152 -205 141 -205 ct 
+117 -205 97 -197 84 -181 ct 70 -166 63 -144 63 -115 ct 63 -87 70 -65 84 -50 ct 
+97 -34 117 -26 141 -26 ct 152 -26 163 -28 174 -31 ct 185 -34 196 -38 206 -44 ct 
+206 -9 l  196 -4 185 0 173 2 ct 162 5 150 6 137 6 ct 102 6 75 -5 54 -27 ct 34 -49 23 -78 23 -115 ct 
+23 -153 34 -183 54 -205 ct 75 -226 104 -237 140 -237 ct 151 -237 163 -236 174 -233 ct 
+185 -231 196 -227 206 -223 ct p ef
+407 -196 m  403 -198 398 -200 393 -201 ct 388 -203 382 -203 376 -203 ct 355 -203 338 -196 327 -182 ct 
+315 -168 310 -148 310 -122 ct 310 0 l  271 0 l  271 -231 l  310 -231 l  310 -195 l 
+318 -210 328 -220 341 -227 ct 354 -234 369 -237 388 -237 ct 390 -237 393 -237 396 -236 ct 
+399 -236 403 -236 407 -235 ct 407 -196 l  p ef
+644 -125 m  644 -106 l  469 -106 l  471 -80 479 -60 493 -47 ct 507 -33 526 -26 552 -26 ct 
+566 -26 580 -28 594 -32 ct 608 -35 621 -41 635 -48 ct 635 -12 l  621 -6 607 -2 593 1 ct 
+579 4 564 6 549 6 ct 512 6 483 -5 462 -26 ct 440 -48 429 -77 429 -113 ct 429 -151 440 -181 460 -204 ct 
+480 -226 508 -237 543 -237 ct 574 -237 598 -227 617 -207 ct 635 -187 644 -160 644 -125 ct 
+p
+606 -136 m  605 -157 600 -174 588 -186 ct 577 -199 562 -205 543 -205 ct 522 -205 505 -199 492 -187 ct 
+480 -175 472 -158 470 -136 ct 606 -136 l  p ef
+814 -116 m  783 -116 762 -113 750 -106 ct 738 -99 732 -87 732 -70 ct 732 -56 737 -46 746 -38 ct 
+755 -30 767 -26 782 -26 ct 803 -26 820 -33 833 -48 ct 845 -63 852 -83 852 -108 ct 
+852 -116 l  814 -116 l  p
+890 -132 m  890 0 l  852 0 l  852 -35 l  843 -21 832 -11 819 -4 ct 806 3 791 6 772 6 ct 
+748 6 729 -1 715 -14 ct 701 -27 694 -45 694 -67 ct 694 -93 703 -113 721 -126 ct 
+738 -139 764 -146 799 -146 ct 852 -146 l  852 -150 l  852 -167 846 -181 835 -190 ct 
+823 -200 807 -205 786 -205 ct 773 -205 760 -203 747 -200 ct 735 -197 723 -192 711 -186 ct 
+711 -221 l  725 -226 739 -230 752 -233 ct 765 -236 778 -237 790 -237 ct 823 -237 848 -228 865 -211 ct 
+882 -194 890 -167 890 -132 ct p ef
+1004 -297 m  1004 -232 l  1083 -232 l  1083 -202 l  1004 -202 l  1004 -76 l 
+1004 -57 1007 -45 1012 -40 ct 1017 -35 1028 -32 1044 -32 ct 1083 -32 l  1083 0 l 
+1044 0 l  1014 0 994 -5 983 -16 ct 972 -27 966 -47 966 -76 ct 966 -202 l  938 -202 l 
+938 -232 l  966 -232 l  966 -297 l  1004 -297 l  p ef
+1330 -125 m  1330 -106 l  1155 -106 l  1157 -80 1165 -60 1179 -47 ct 1193 -33 1212 -26 1238 -26 ct 
+1252 -26 1266 -28 1280 -32 ct 1294 -35 1307 -41 1321 -48 ct 1321 -12 l  1307 -6 1293 -2 1279 1 ct 
+1265 4 1250 6 1235 6 ct 1198 6 1169 -5 1148 -26 ct 1126 -48 1115 -77 1115 -113 ct 
+1115 -151 1126 -181 1146 -204 ct 1166 -226 1194 -237 1229 -237 ct 1260 -237 1284 -227 1303 -207 ct 
+1321 -187 1330 -160 1330 -125 ct p
+1292 -136 m  1291 -157 1286 -174 1274 -186 ct 1263 -199 1248 -205 1229 -205 ct 
+1208 -205 1191 -199 1178 -187 ct 1166 -175 1158 -158 1156 -136 ct 1292 -136 l 
+p ef
+1486 -322 m  1468 -290 1454 -259 1445 -228 ct 1436 -197 1432 -166 1432 -134 ct 
+1432 -102 1436 -70 1445 -39 ct 1454 -8 1468 23 1486 55 ct 1453 55 l  1432 23 1417 -9 1407 -41 ct 
+1396 -72 1391 -103 1391 -134 ct 1391 -164 1396 -195 1407 -227 ct 1417 -258 1432 -289 1453 -322 ct 
+1486 -322 l  p ef
+1554 -322 m  1587 -322 l  1608 -289 1623 -258 1633 -227 ct 1644 -195 1649 -164 1649 -134 ct 
+1649 -103 1644 -72 1633 -41 ct 1623 -9 1608 23 1587 55 ct 1554 55 l  1572 23 1586 -8 1595 -39 ct 
+1604 -70 1608 -102 1608 -134 ct 1608 -166 1604 -197 1595 -228 ct 1586 -259 1572 -290 1554 -322 ct 
+p ef
+pom
+gr
+gr
+19684 12853 m  19549 12398 l  19849 12408 l  19684 12853 l  p ef
+20583 7401 m  20544 8733 l  20500 9200 l  20443 9560 l  20374 9834 l  20299 10041 l 
+20217 10202 l  20134 10336 l  20050 10464 l  19968 10605 l  19893 10780 l 
+19824 11008 l  19767 11309 l  19723 11704 l  19696 12493 l  ps
+gs
+gs
+pum
+20558 10554 t
+206 -223 m  206 -187 l  196 -193 185 -197 174 -200 ct 163 -203 152 -205 141 -205 ct 
+117 -205 97 -197 84 -181 ct 70 -166 63 -144 63 -115 ct 63 -87 70 -65 84 -50 ct 
+97 -34 117 -26 141 -26 ct 152 -26 163 -28 174 -31 ct 185 -34 196 -38 206 -44 ct 
+206 -9 l  196 -4 185 0 173 2 ct 162 5 150 6 137 6 ct 102 6 75 -5 54 -27 ct 34 -49 23 -78 23 -115 ct 
+23 -153 34 -183 54 -205 ct 75 -226 104 -237 140 -237 ct 151 -237 163 -236 174 -233 ct 
+185 -231 196 -227 206 -223 ct p ef
+363 -205 m  342 -205 326 -197 314 -181 ct 302 -165 296 -143 296 -115 ct 296 -88 302 -66 314 -50 ct 
+326 -34 342 -26 363 -26 ct 383 -26 399 -34 411 -50 ct 422 -66 428 -88 428 -116 ct 
+428 -143 422 -165 411 -181 ct 399 -197 383 -205 363 -205 ct p
+363 -237 m  396 -237 422 -226 440 -205 ct 459 -183 469 -154 469 -115 ct 469 -78 459 -48 440 -26 ct 
+422 -5 396 6 363 6 ct 329 6 303 -5 285 -26 ct 266 -48 256 -78 256 -115 ct 256 -154 266 -183 285 -205 ct 
+303 -226 329 -237 363 -237 ct p ef
+723 -140 m  723 0 l  685 0 l  685 -138 l  685 -160 681 -177 672 -188 ct 664 -199 651 -204 634 -204 ct 
+613 -204 597 -197 585 -184 ct 574 -171 568 -153 568 -131 ct 568 0 l  529 0 l 
+529 -231 l  568 -231 l  568 -195 l  577 -209 587 -220 600 -227 ct 612 -234 626 -237 642 -237 ct 
+669 -237 689 -229 703 -212 ct 716 -196 723 -172 723 -140 ct p ef
+915 -322 m  915 -290 l  879 -290 l  865 -290 856 -288 850 -282 ct 845 -277 842 -267 842 -252 ct 
+842 -232 l  905 -232 l  905 -202 l  842 -202 l  842 0 l  804 0 l  804 -202 l 
+768 -202 l  768 -232 l  804 -232 l  804 -248 l  804 -274 810 -293 822 -304 ct 
+834 -316 853 -322 879 -322 ct 915 -322 l  p ef
+946 -232 m  984 -232 l  984 -1 l  946 -1 l  946 -232 l  p
+946 -322 m  984 -322 l  984 -274 l  946 -274 l  946 -322 l  p ef
+1216 -118 m  1216 -146 1210 -167 1199 -182 ct 1188 -198 1172 -205 1151 -205 ct 
+1131 -205 1115 -198 1104 -182 ct 1092 -167 1087 -146 1087 -118 ct 1087 -91 1092 -70 1104 -55 ct 
+1115 -39 1131 -32 1151 -32 ct 1172 -32 1188 -39 1199 -55 ct 1210 -70 1216 -91 1216 -118 ct 
+p
+1254 -29 m  1254 11 1245 40 1228 59 ct 1210 78 1184 88 1148 88 ct 1134 88 1122 87 1110 85 ct 
+1098 83 1086 80 1075 76 ct 1075 39 l  1086 45 1097 49 1108 52 ct 1119 55 1130 57 1142 57 ct 
+1166 57 1185 50 1197 37 ct 1210 24 1216 4 1216 -22 ct 1216 -41 l  1208 -27 1198 -17 1186 -10 ct 
+1174 -3 1159 0 1142 0 ct 1114 0 1091 -11 1073 -32 ct 1056 -54 1047 -83 1047 -118 ct 
+1047 -154 1056 -183 1073 -205 ct 1091 -226 1114 -237 1142 -237 ct 1159 -237 1174 -234 1186 -227 ct 
+1198 -220 1208 -210 1216 -196 ct 1216 -231 l  1254 -231 l  1254 -29 l  p ef
+1422 -322 m  1404 -290 1390 -259 1381 -228 ct 1372 -197 1368 -166 1368 -134 ct 
+1368 -102 1372 -70 1381 -39 ct 1390 -8 1404 23 1422 55 ct 1389 55 l  1368 23 1353 -9 1343 -41 ct 
+1332 -72 1327 -103 1327 -134 ct 1327 -164 1332 -195 1343 -227 ct 1353 -258 1368 -289 1389 -322 ct 
+1422 -322 l  p ef
+1490 -322 m  1523 -322 l  1544 -289 1559 -258 1569 -227 ct 1580 -195 1585 -164 1585 -134 ct 
+1585 -103 1580 -72 1569 -41 ct 1559 -9 1544 23 1523 55 ct 1490 55 l  1508 23 1522 -8 1531 -39 ct 
+1540 -70 1544 -102 1544 -134 ct 1544 -166 1540 -197 1531 -228 ct 1522 -259 1508 -290 1490 -322 ct 
+p ef
+pom
+gr
+gr
+10159 7773 m  10359 8203 l  10060 8237 l  10159 7773 l  p ef
+18785 13223 m  18760 12834 l  18688 12484 l  18572 12171 l  18414 11892 l 
+18219 11644 l  17989 11426 l  17727 11234 l  17437 11066 l  16784 10792 l 
+16056 10585 l  14472 10290 l  12888 10021 l  12160 9846 l  11507 9618 l 
+11217 9478 l  10955 9317 l  10725 9132 l  10530 8922 l  10372 8683 l  10256 8414 l 
+10189 8132 l  ps
+gs
+gs
+pum
+14843 10131 t
+206 -223 m  206 -187 l  196 -193 185 -197 174 -200 ct 163 -203 152 -205 141 -205 ct 
+117 -205 97 -197 84 -181 ct 70 -166 63 -144 63 -115 ct 63 -87 70 -65 84 -50 ct 
+97 -34 117 -26 141 -26 ct 152 -26 163 -28 174 -31 ct 185 -34 196 -38 206 -44 ct 
+206 -9 l  196 -4 185 0 173 2 ct 162 5 150 6 137 6 ct 102 6 75 -5 54 -27 ct 34 -49 23 -78 23 -115 ct 
+23 -153 34 -183 54 -205 ct 75 -226 104 -237 140 -237 ct 151 -237 163 -236 174 -233 ct 
+185 -231 196 -227 206 -223 ct p ef
+273 -322 m  311 -322 l  311 0 l  273 0 l  273 -322 l  p ef
+481 -205 m  460 -205 444 -197 432 -181 ct 420 -165 414 -143 414 -115 ct 414 -88 420 -66 432 -50 ct 
+444 -34 460 -26 481 -26 ct 501 -26 517 -34 529 -50 ct 540 -66 546 -88 546 -116 ct 
+546 -143 540 -165 529 -181 ct 517 -197 501 -205 481 -205 ct p
+481 -237 m  514 -237 540 -226 558 -205 ct 577 -183 587 -154 587 -115 ct 587 -78 577 -48 558 -26 ct 
+540 -5 514 6 481 6 ct 447 6 421 -5 403 -26 ct 384 -48 374 -78 374 -115 ct 374 -154 384 -183 403 -205 ct 
+421 -226 447 -237 481 -237 ct p ef
+797 -225 m  797 -189 l  787 -194 775 -198 764 -201 ct 752 -204 740 -205 728 -205 ct 
+709 -205 695 -202 685 -197 ct 676 -191 671 -182 671 -170 ct 671 -162 675 -155 681 -150 ct 
+688 -145 702 -140 722 -135 ct 735 -132 l  762 -127 781 -119 793 -108 ct 804 -97 810 -83 810 -64 ct 
+810 -42 801 -25 784 -13 ct 767 0 744 6 714 6 ct 702 6 689 5 675 2 ct 662 0 648 -4 633 -8 ct 
+633 -48 l  647 -40 661 -35 674 -31 ct 688 -28 702 -26 715 -26 ct 733 -26 747 -29 756 -35 ct 
+766 -41 771 -50 771 -61 ct 771 -71 767 -79 760 -85 ct 753 -90 738 -96 715 -101 ct 
+701 -104 l  678 -109 661 -116 650 -127 ct 640 -137 635 -151 635 -169 ct 635 -191 642 -207 658 -219 ct 
+673 -231 695 -237 723 -237 ct 737 -237 751 -236 763 -234 ct 775 -232 787 -229 797 -225 ct 
+p ef
+1068 -125 m  1068 -106 l  893 -106 l  895 -80 903 -60 917 -47 ct 931 -33 950 -26 976 -26 ct 
+990 -26 1004 -28 1018 -32 ct 1032 -35 1045 -41 1059 -48 ct 1059 -12 l  1045 -6 1031 -2 1017 1 ct 
+1003 4 988 6 973 6 ct 936 6 907 -5 886 -26 ct 864 -48 853 -77 853 -113 ct 853 -151 864 -181 884 -204 ct 
+904 -226 932 -237 967 -237 ct 998 -237 1022 -227 1041 -207 ct 1059 -187 1068 -160 1068 -125 ct 
+p
+1030 -136 m  1029 -157 1024 -174 1012 -186 ct 1001 -199 986 -205 967 -205 ct 
+946 -205 929 -199 916 -187 ct 904 -175 896 -158 894 -136 ct 1030 -136 l  p ef
+1223 -322 m  1205 -290 1191 -259 1182 -228 ct 1173 -197 1169 -166 1169 -134 ct 
+1169 -102 1173 -70 1182 -39 ct 1191 -8 1205 23 1223 55 ct 1190 55 l  1169 23 1154 -9 1144 -41 ct 
+1133 -72 1128 -103 1128 -134 ct 1128 -164 1133 -195 1144 -227 ct 1154 -258 1169 -289 1190 -322 ct 
+1223 -322 l  p ef
+1291 -322 m  1324 -322 l  1345 -289 1360 -258 1370 -227 ct 1381 -195 1386 -164 1386 -134 ct 
+1386 -103 1381 -72 1370 -41 ct 1360 -9 1345 23 1324 55 ct 1291 55 l  1309 23 1323 -8 1332 -39 ct 
+1341 -70 1345 -102 1345 -134 ct 1345 -166 1341 -197 1332 -228 ct 1323 -259 1309 -290 1291 -322 ct 
+p ef
+pom
+gr
+gr
+gr
+gs
+0 0 m 27999 0 l 27999 20999 l 0 20999 l 0 0 l eoclip newpath
+gr
+gr
+0 21000 t 
+pom
+count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore
+%%PageTrailer
+%%Trailer
+%%EOF
diff --git a/doc/images/comm_states.odp b/doc/images/comm_states.odp
new file mode 100644
index 0000000000000000000000000000000000000000..3cd96c6f84857f1dd2ad958453ade4ecf691d861
GIT binary patch
literal 12467
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
I{oUFB0l{sKQ2+n{

literal 0
HcmV?d00001

diff --git a/doc/images/comm_states.png b/doc/images/comm_states.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9a452f08de48c6415ee98a66a1c8bc346d6ab27
GIT binary patch
literal 22446
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

literal 0
HcmV?d00001

diff --git a/doc/main.dox b/doc/main.dox
new file mode 100644
index 0000000..73bc774
--- /dev/null
+++ b/doc/main.dox
@@ -0,0 +1,143 @@
+/*! \mainpage IRI Communications
+
+  \section Introduction
+
+  This set of drivers for standard communication devices. This library offers a 
+  generic communication device which can be used to create drivers for new 
+  communication devices easily: it is only necessary to implement the low level, 
+  operating system dependent functions, all the other features are already 
+  implemented.
+
+  The main features of all communication devices are:
+
+      - Data is automatically received and stored into a data FIFO by an internal thread.
+      - An event is generated whenever there is new data into this FIFO. Read operations are non-blocking.
+      - An event is generated when an unexpected error happen.
+      - Only a few functions have to be implemented to generate a new driver.
+
+  The communication devices currently supported by this library include:
+
+      - serial port: This drivers provides a simple and easy to use yet 
+        complete interface to a standard computer serial port. This driver allows 
+        the user to open any available serial port, configure it as needed and send 
+        and receive data to and from it. Some USB devices can also use this driver 
+        (those that appear as a virtual COM port). 
+
+      - ftdi driver: This driver is intended to be used to interface with 
+        devices with any of the following FTDI chips:
+            - FT2232H
+            - FT4232H
+            - FT232R
+            - FT245R
+            - FT2232
+            - FT232B
+            - FT245B
+            - FT8U232AM
+            - FT8U245AM
+   This driver can also be used with USB devices that appear as virtual serial 
+   ports in the host computer. In this case it is also possible to use the serial 
+   port driver
+
+      - sockets: This driver provide an implementation of server and client 
+        TCP sockets in Linux. UDP connections are not yet supported. The server 
+        side can handle as many connections as needed, providing notifications for 
+        the following event for each of the connections:
+            - new connection 
+            - disconnection 
+            - reception of new data
+
+        Both the client and server can be used with other implementations of the POSIX sockets.
+
+  \section Installation
+
+  \subsection Pre-Requisites
+
+  This package requires of the following libraries and packages
+	 - <A href="http://www.cmake.org">cmake</A>, a cross-platform build system.
+	 - <A href="http://www.doxygen.org">doxygen</a> and 
+	   <A href="http://www.graphviz.org">graphviz</a> to generate the documentation.
+         - <A href="http://wikiri.upc.es/index.php/Utilities_library">utilities library</a>, a set of basic tools to develop software.
+	 .
+  
+  All the IRI-related dependencies can be automatically installed by calling 
+   - sudo make dep
+   .
+  from the <em>build</em> folder and after calling the <em>cmake ..</em> command.
+
+  Under MacOS most of the packages are available via <a href="http://www.finkproject.org/">fink</a>. <br>
+
+  \subsection Compilation
+
+  Just download this package, uncompress it, and execute
+   - cd build
+   - cmake ..
+   .
+  to generate the makefile and then
+   - make
+   .
+  to obtain the executable (in this example called <em>main_example</em>).
+
+  The <em>cmake</em> only need to be executed once (make will automatically call
+  <em>cmake</em> if you modify one of the <em>CMakeList.txt</em> files).
+
+  To generate this documentation type
+       - make doc
+       .
+
+  The files in the <em>build</em> directory are genetated by <em>cmake</em>
+  and <em>make</em> and can be safely removed.
+  After doing so you will need to call cmake manually again.
+
+  \subsection Configuration
+
+  The default build mode is DEBUG. That is, objects and executables 
+  include debug information.
+
+  The RELEASE build mode optimizes for speed. To build in this mode
+  execute
+      - cmake .. -DCMAKE_BUILD_TYPE=RELEASE
+      .
+  The release mode will be kept until next time cmake is executed.
+
+  \section Customization
+
+  To build a new application using these library, first it is necessary to locate if the library
+  has been installed or not using the following command
+
+      - FIND_PACKAGE(comm)
+
+  In the case that the package is present, it is necessary to add the header files directory to
+  the include directory path by using
+
+      - INCLUDE_DIRECTORIES(${comm_INCLUDE_DIR})
+
+  Finally, it is also nevessary to link with the desired libraries by using the following command
+
+      - TARGET_LINK_LIBRARIES(<executable name> ${comm_LIBRARY})
+      .
+
+  All these steps are automatically done when the new project is created following the instructions
+  in <A href="http://wikiri.upc.es/index.php/Create_a_new_development_project">here</a>
+
+  \section License
+
+  This package is licensed under a 
+  <a href="http://creativecommons.org/licenses/by/3.0/">
+    Creative Commons Attribution 3.0 Unported License</a>.
+
+  \section Disclaimer
+
+  This package is distributed in the hope that it will be useful, but without any warranty. 
+  it is provided "as is" without warranty of any kind, either expressed or implied, including, 
+  but not limited to, the implied warranties of merchantability and fitness for a particular 
+  purpose. The entire risk as to the quality and performance of the program is with you. 
+  should the program prove defective, the GMR group does not assume the cost of any necessary 
+  servicing, repair  or correction.
+
+  In no event unless required by applicable law the author will be liable to you for damages, 
+  including any general, special, incidental or consequential damages arising out of the use or 
+  inability to use the program (including but not limited to loss of data or data being rendered 
+  inaccurate or losses sustained by you or third parties or a failure of the program to operate 
+  with any other programs), even if the author has been advised of the possibility of such damages. 
+
+ */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..6fc247f
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,57 @@
+FIND_PATH(FTDI_INCLUDE_DIR ftd2xx.h WinTypes.h /usr/include/ /usr/local/include/)
+
+FIND_LIBRARY(FTDI_LIBRARY
+    NAMES ftd2xx
+    PATHS /usr/lib /usr/local/lib)
+
+IF(FTDI_INCLUDE_DIR AND FTDI_LIBRARY)
+  SET(BUILD_FTDI TRUE)
+ELSE(FTDI_INCLUDE_DIR AND FTDI_LIBRARY)
+  MESSAGE(STATUS "FTDI library won't be build. Impossible to locate the necessary files")     
+ENDIF(FTDI_INCLUDE_DIR AND FTDI_LIBRARY)
+
+# edit the following line to add all the source code files of the library
+SET(sources ./comm.cpp ./commexceptions.cpp ./serial/rs232.cpp ./serial/rs232exceptions.cpp  ./sockets/socket.cpp ./sockets/socketclient.cpp ./sockets/socketserver.cpp ./sockets/socketexceptions.cpp)
+# edit the following line to add all the header files of the library
+SET(headers ./comm.h ./commexceptions.h ./serial/rs232.h ./serial/rs232exceptions.h ./sockets/socket.h ./sockets/socketclient.h ./sockets/socketserver.h ./sockets/socketexceptions.h)
+
+IF(BUILD_FTDI)
+  SET(sources ${sources} ./usb_ftdi/ftdiserver.cpp ./usb_ftdi/ftdimodule.cpp ./usb_ftdi/ftdiexceptions.cpp)
+  SET(headers ${headers} ./usb_ftdi/ftdiserver.h ./usb_ftdi/ftdimodule.h ./usb_ftdi/ftdiexceptions.h)
+ENDIF(BUILD_FTDI)
+
+INCLUDE_DIRECTORIES(.)
+
+# edit the following line to find the necessary packages
+FIND_PACKAGE(iriutils REQUIRED)
+
+INCLUDE_DIRECTORIES(${iriutils_INCLUDE_DIR})
+
+# edit the following line to add the necessary include directories
+INCLUDE_DIRECTORIES(./serial ./usb_ftdi ./sockets)
+
+IF(BUILD_FTDI)
+  INCLUDE_DIRECTORIES(${FTDI_INCLUDE_DIR})
+ENDIF(BUILD_FTDI)
+
+ADD_LIBRARY(comm SHARED ${sources})
+
+#edit the following line to add the necessary system libraries (if any)
+
+TARGET_LINK_LIBRARIES(comm ${iriutils_LIBRARY})
+
+IF(BUILD_FTDI) 
+  TARGET_LINK_LIBRARIES(comm ${FTDI_LIBRARY})
+ENDIF(BUILD_FTDI)
+
+INSTALL(TARGETS comm
+  RUNTIME DESTINATION bin
+  LIBRARY DESTINATION lib/iridrivers
+  ARCHIVE DESTINATION lib/iridrivers
+)
+
+INSTALL(FILES ${headers} DESTINATION include/iridrivers)
+
+INSTALL(FILES ../Findcomm.cmake DESTINATION ${CMAKE_ROOT}/Modules/)
+
+ADD_SUBDIRECTORY(examples)
diff --git a/src/comm.cpp b/src/comm.cpp
new file mode 100644
index 0000000..6437535
--- /dev/null
+++ b/src/comm.cpp
@@ -0,0 +1,328 @@
+#include <string.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include "comm.h"
+#include "commexceptions.h"
+#include "ctime.h"
+
+CComm::CComm(const std::string& comm_id) 
+{
+  this->rx_event_id="";
+  this->error_event_id="";
+  this->comm_thread_id="";
+  this->error_msg="";
+  this->state=created;
+  try{
+    this->set_id(comm_id);
+    /* get a reference to the event handler */
+    this->event_server=CEventServer::instance();
+    /* create the events */
+    this->rx_event_id+=comm_id;
+    this->rx_event_id+="_rx_event_id";
+    this->event_server->create_event(this->rx_event_id);
+    this->error_event_id+=comm_id;
+    this->error_event_id+="_error_event";
+    this->event_server->create_event(this->error_event_id);
+    /* get the reference to the thread handler */
+    this->thread_server=CThreadServer::instance();
+    /* create the thread */
+    this->comm_thread_id+=comm_id;
+    this->comm_thread_id+="_comm_thread";
+    this->thread_server->create_thread(this->comm_thread_id);
+    /* attach the thread function */
+    this->thread_server->attach_thread(this->comm_thread_id,comm_thread,this);
+  }catch(CException &e){
+    this->close();
+    /* delete the events */
+    throw;
+  }
+}
+
+void CComm::set_id(const std::string& comm_id)
+{
+  if(comm_id.size()==0)
+  {
+    /* handle exceptions */
+    throw CCommException(_HERE_,"Invalid communication id.\n","empty name");
+  }
+  else
+    this->comm_id=comm_id;
+}
+
+std::string& CComm::get_id(void)
+{
+  return this->comm_id;
+}
+
+void CComm::open(void *comm_dev)
+{
+  if(this->state==created)
+  {
+    try{
+      this->access_comm.enter();
+      this->hard_open(comm_dev);
+      this->thread_server->start_thread(this->comm_thread_id);
+      this->state=opened;
+      this->access_comm.exit();
+    }catch(CException &e){
+      this->access_comm.exit();
+      /* handle exception */
+      throw;
+    }
+  }
+  else
+  {
+    /* handle exceptions */
+    throw CCommException(_HERE_,"The communication device is already opened\n.",this->comm_id);
+  }
+}
+
+void CComm::config(void *config)
+{
+  this->access_comm.enter();
+  if(this->state==created)
+  { 
+    this->access_comm.exit();
+    /* handle exceptions */
+    throw CCommException(_HERE_,"The communication device has not been opened yet.\n",this->comm_id);
+  }
+  else
+  {
+    try{
+      if(this->state==opened)
+        this->state=configured;
+      /* otherwise, keep the current state */
+      this->hard_config(config);
+    }catch(CException &e){
+      this->access_comm.exit();
+      /* handle exception */
+      throw;
+    }
+  }
+  this->access_comm.exit();
+}
+
+int CComm::read(unsigned char *data,int len)
+{
+  int num_available=0;
+  int i=0;
+
+  if(data==NULL)
+  {
+    /* handle exceptions */
+    throw CCommException(_HERE_,"Invalid data buffer to save the data received (NULL pointer).\n",this->comm_id);
+  }
+  else
+  {
+    if(this->state==configured || this->state==sending)
+    {
+      this->access_comm.enter();
+      num_available=this->receive_queue.size();
+      if(len>num_available)
+      {
+        len=num_available;
+        this->event_server->reset_event(this->rx_event_id);
+      }
+      for(i=0;i<len;i++)
+      {
+        data[i]=*this->receive_queue.begin();
+        this->receive_queue.pop_front();
+      }
+      this->access_comm.exit();
+      return len;
+    }
+    else
+    {
+      throw CCommException(_HERE_,"The communication device is not configured yet.\n",this->comm_id);
+    }
+  }
+  return len;
+}
+
+int CComm::write(unsigned char *data,int len)
+{
+  int written_len=0;
+
+  if(data==NULL)
+  {
+    /* handle exceptions */
+    throw CCommException(_HERE_,"Invalid data buffer to be sent (NULL pointer).\n",this->comm_id);
+  }  
+  else
+  {
+    this->access_comm.enter();
+    if(this->state==configured)
+    {
+      try{
+        written_len=this->hard_write(data,len);
+      }catch(CException &e){
+        this->access_comm.exit();
+        /* handle exceptions */
+        throw;
+      }
+      if(written_len!=len)
+      {
+        /* handle exceptions */
+        this->access_comm.exit();
+        throw CCommException(_HERE_,"Unexpected error while writing to the communication device.\n",this->comm_id);
+      }
+    }
+    else
+    {
+      this->access_comm.exit();
+      throw CCommException(_HERE_,"The communication device is not configured yet.\n",this->comm_id);
+    }
+    this->access_comm.exit();
+    return len;
+  }
+  return len;
+}
+
+unsigned int CComm::get_num_data(void)
+{
+  unsigned int num=0;
+
+  if(this->state==configured || this->state==sending)
+  {
+    this->access_comm.enter();
+    num=this->receive_queue.size();
+    this->access_comm.exit();
+  }
+
+  return num;
+}
+
+std::string& CComm::get_rx_event_id(void)
+{
+  return this->rx_event_id;
+}
+
+std::string& CComm::get_error_event_id(void)
+{
+  return this->error_event_id;
+}
+
+std::string& CComm::get_error(void)
+{
+  return this->error_msg;
+}
+
+int CComm::get_state(void)
+{
+  return this->state;
+}
+
+void CComm::close(void)
+{
+  if(this->state==configured || this->state==sending || this->state==opened)
+  {
+    /* finish the thread */
+    if(this->state==configured)
+      this->thread_server->kill_thread(this->comm_thread_id);
+    /* close the communication device */
+    this->access_comm.try_enter();
+    try{
+      this->hard_close();
+    }catch(CException &e){
+      this->access_comm.exit();
+      /* handle exceptions */
+      throw;
+    }
+    /* flush the data queues */
+    this->receive_queue.erase(this->receive_queue.begin(),this->receive_queue.end());
+    /* change the current state */
+    this->state=created;
+    this->access_comm.exit();
+  }
+}
+
+void *CComm::comm_thread(void *param)
+{
+  CComm *comm_dev=(CComm *)param;
+  int wait_result;
+  bool end=false;
+
+  while(!end)
+  {
+    wait_result=comm_dev->hard_wait_comm_event();
+    comm_dev->access_comm.enter();
+    if(wait_result==-1)
+      end=true;
+    else
+    {
+      if(wait_result==1)/* data has been received */
+      {
+        comm_dev->on_receive(); 
+      }
+      if(wait_result==2)
+      {
+        comm_dev->on_error();
+      }
+    }
+    comm_dev->access_comm.exit();
+  }
+  /* handle exceptions */
+//  throw CCommException(_HERE_,"Unexpected error while waiting for new data.\n",comm_dev->comm_id);
+
+  return NULL;
+}
+
+void CComm::on_receive(void)
+{
+  unsigned char *data=NULL;
+  int num=0,num_read=0,i=0;
+
+  if((num=this->hard_get_num_data())==-1)
+  {
+    /* handle exceptions */
+    throw CCommException(_HERE_,"Impossible to get the number of new data available on the communication device.\n",this->comm_id);
+  }
+  data=new unsigned char[num];
+  if((num_read=this->hard_read(data,num))==-1)
+  {
+    delete[] data;
+    /* handle exceptions */
+    throw CCommException(_HERE_,"Impossible to read from the communication device.\n",this->comm_id);
+  }
+  else
+  {
+    if(num_read!=num)
+    {
+      delete[] data;
+      /* handle exceptions */
+      throw CCommException(_HERE_,"Not all desired data has been read from the communicationd device.\n",this->comm_id);
+    }
+    else
+    {
+      for(i=0;i<num;i++)
+        this->receive_queue.push_back(data[i]);
+      if(!this->event_server->event_is_set(this->rx_event_id))
+      {
+        this->event_server->set_event(this->rx_event_id);
+      }
+      delete[] data;
+    }
+  }
+}
+
+void CComm::on_error(void)
+{
+  /* handle exceptions */
+}
+
+CComm::~CComm()
+{
+  /* delete the events */
+  if(this->rx_event_id.size()!=0)
+    this->event_server->delete_event(this->rx_event_id);
+  if(this->error_event_id.size()!=0)
+  {
+    this->event_server->delete_event(this->error_event_id);
+  }
+  /* delete the thread */
+  if(this->comm_thread_id.size()!=0)
+  {
+    this->thread_server->delete_thread(this->comm_thread_id);
+  }
+}
diff --git a/src/comm.h b/src/comm.h
new file mode 100644
index 0000000..27919ea
--- /dev/null
+++ b/src/comm.h
@@ -0,0 +1,649 @@
+#ifndef _COMM_H
+#define _COMM_H
+
+#include <list>
+#include "eventserver.h"
+#include "threadserver.h"
+#include "mutex.h"
+
+typedef enum {created,configured,opened,sending} comm_states;
+
+/**
+ * \brief Implementation of a generic communication devidei
+ * 
+ * This class implements the basic features of a communication device. All
+ * standard communication devices (pipes, serial ports, sockets, etc...) will
+ * inherit from this base class and only need to define the specific functions
+ * to open (hard_open()), configure (hard_config()), read (hard_read(), write
+ * (hard_write()), get the number of available data (hard_get_num_data()), 
+ * close (hard_close()) and wait for communication events (hard_wait_comm_event())
+ * on the device.
+ *
+ * The interface provided by this class includes two events (data reception,
+ * and error) and one data queue where the received data is temporarly stored. 
+ * The events can be accessed through the event handler (CEventServer) by any 
+ * other object, and two functions are provided to retrieve their 
+ * identifiers (get_rx_event_id(), and error_event_id()).
+ *
+ * The recive event indicates if the device has received data and it remains
+ * active while there is data in the internal queue to be read. When the receive 
+ * queue is empty, the event is reset. Finally the error event indicates that an
+ * error ocurred. Use the get_error() function to retrieve the error code.
+ *
+ * When created the communication device starts a thread that is actually the 
+ * one responsible of handling the communication events (received data, 
+ * and errors). This thread is used exlcusively inside the class and can not be
+ * accessed outside it. This thread uses the wait function provided by the 
+ * inherited class to wait for any communication events.
+ *
+ * Most devices in Linux are referenced by a file descriptor for both read and
+ * write operations, or also, two different file descriptors, one for read 
+ * operations, and the other one for write operations. However, other devices
+ * may provide different methods of interfacing them, so no specific device
+ * handler is provided by this class. It is the inherited class that must provide
+ * it, together with the functions to open it, configure it, read from and write 
+ * to it and close it.
+ *
+ * To open the device, the user must call the base class open() function and also
+ * provide a function (hard_open()) to actually open the communication device. 
+ * The user provided function is automatically called by the base class open()
+ * function and it is responsible of initializing the device handler.
+ *
+ * After that, the device must be configured. Similarly to what happen with the
+ * open() function, the inherited class must provide the hard_config() function
+ * to perform all the required configuration steps. Both the open() and config()
+ * function have a void * parameter to allow the inherited class to pass through 
+ * any data structure. This parameter is passed to the corresponding inherited 
+ * class function (hard_open() or hard_config() respectively).
+ *
+ * Only after configuration, it is possible to read from and write to the device.
+ * Any previous attempt to do so will result in an error. The inherited class
+ * must also provide functions to actually read from (hard_read()) and write to
+ * (hard_write()) the device. To close the communication device, it is necessary
+ * to call the close() function which ends the thread and flushes any data still
+ * inside the transmission and reception queues. Also this function calls the 
+ * hard_close() function to actually close the device handle.
+ *
+ * The events and the thread itself are not destroyed until the communciation 
+ * device object is destroyed. It is possible to close the device at any time.
+ *
+ * For a propper operation, any inherited class must provide an implementation
+ * for the hard_open(), hard_config(), hard_read(), hard_write(), hard_close(),
+ * hard_get_num_data() and hard_wait_comm_event() functions. This functions are
+ * automatically called by the base class and must not be called directly.
+ *
+ * Any communication device is driven by an internal state machine, whose state is
+ * automatically changed when the different functions are called. The next figure
+ * shows the different states and also the state transition conditions.
+ *
+ *  \image html comm_states.png
+ *  \image latex comm_states.eps "Communication device states" width=10cm
+ *
+ */
+class CComm
+{
+  protected:
+    /**
+     * \brief receive event identifier
+     *
+     * This string has a unique identifier of the reception event that is used 
+     * through out the code to take action on the desired event. This string is 
+     * initialized at construction time using the identifier of the communication 
+     * device provided to the constructor and can not be modified afterwards. The
+     * function get_rx_event_id() can be used to retrieve this identifier.
+     */
+    std::string rx_event_id;
+    /**
+     * \brief error event identifier
+     *
+     * This string has a unique identifier of the error event that is used 
+     * through out the code to take action on the desired event. This string is 
+     * initialized at construction time using the identifier of the communication 
+     * device provided to the constructor and can not be modified afterwards. The
+     * function get_error_event_id() can be used to retrieve this identifier.
+     */
+    std::string error_event_id;
+    /**
+     * \brief communication thread identifier
+     *
+     * This string has a unique identifier of the communication thread that is used 
+     * through out the code to take action on the desired thread. This string is 
+     * initialized at construction time using the identifier of the communication 
+     * device provided to the constructor and can not be modified afterwards. This
+     * thread is only accessible from inside the class so it is not possible to
+     * retrieve its identifier.
+     */
+    std::string comm_thread_id;
+    /** 
+     * \brief communication error message
+     *
+     * This string has the information message of any error that could happen on
+     * the communication device. By default it is initialized to NULL, and it is 
+     * only created when there is an error. The error message is automatically 
+     * created when an error is detected by the communication thread, and it can
+     * be retrieved by calling the get_error() function.
+     */ 
+    std::string error_msg;
+    /** 
+     * \brief received data queue
+     *
+     * This dynamic queue stores all the data that is recieved by the communication
+     * device until it is read by the user. There is no limit in the number of bytes
+     * stored in this list except for the physical memory available. The data 
+     * received through the serial port is automatically put into the queue by the 
+     * communication thread, and the function read() is used to retrieve it. The 
+     * function get_num_data() returns the number of bytes in this queue. By default,
+     * this queue is empty.
+     */
+    std::list<unsigned char> receive_queue;
+    /**
+     * \brief current state of the communication device
+     * 
+     * This variable keeps the current state of the communication device. The state
+     * of the device is changed automatically by the class functions and can not be 
+     * modified outside it. The possible states of the communication device are:
+     *
+     *  - created: when the object has been just created.
+     *  - opened: after opening the device with the open() function.
+     *  - configured: after calling the config() function.
+     *  - sending: when the device is busy sending data.
+     *
+     * It is possible to retrieve the current state of the communication device at
+     * any time using the get_state() function.
+     */
+    comm_states state;
+    /**
+     * \brief Reference to the unique event handler
+     *
+     * This reference to the unique event handler is initialized when an object of
+     * this class is first created. It is used to create and handle all the 
+     * communication events. The object pointed by this reference is shared by all
+     * objects in any application.
+     */  
+    CEventServer *event_server;
+    /**
+     * \brief Reference to the unique thread handler
+     *
+     * This reference to the unique thread handler is initialized when an object of
+     * this class is first created. It is used to create and handle all the 
+     * communication threads. The object pointed by this reference is shared by all
+     * objects in any application.
+     */ 
+    CThreadServer *thread_server;
+    /**
+     * \brief A unique identifier for the obejct
+     *
+     * This string has a unique identifier of the object that is used throug
+     * out the code to take action on the desired object. This string is
+     * initialized at contruction time and can not be modified afterwards.
+     */
+     std::string comm_id;
+    /**
+     * \brief Communication mutual exclusion object
+     *
+     * This object is intended to be used by to handle the access to the shared 
+     * communication resource defined by the inherited classes. Using this mutex
+     * multiple simultaneous read or write operations on the communication device 
+     * are avoided which would result in data corruption or unexpected errors.
+     * This object is initialized at contruction time.
+     */
+    CMutex access_comm;
+    /** 
+     * \brief Thread function
+     * 
+     * This is the main function executed by the communication device to handle 
+     * the communication events. This function waits for either a receive or error
+     * events permanently, and the calls the appropriate function to handle the 
+     * events (on_receive() and on_error() respectively).
+     *
+     * Except for the time where this function is actually waiting for an event 
+     * to get active, it locks the user mutex of the base class in order to avoid
+     * that other threads interfere with the correct execution of the function. 
+     * The mutex is freed when the function is waiting to allow other threads to
+     * access the class shared resources.
+     *
+     * When data is received, this function automatically reads it from the physical
+     * communication device and stores it into the internal receive queue. Then the 
+     * receive event is activated to awake any waiting thread.
+     *
+     * In case of an error, the error event is activated. To retrieve the error 
+     * message, it is necessary to use the get_error() function. In case of an error,
+     * the thread is terminated, but no exception is thrown because there is no way 
+     * to catch it.
+     *
+     * \param param a reference to the communication device object that is associated
+     * to the thread. This reference is necessary to access the internal attributes
+     * of the class.
+     */
+    static void *comm_thread(void *param);
+    /**
+     * \brief function to handle the reception events
+     *
+     * This function handles the receive events. When called, it first gets how many 
+     * bytes have been received. Then all the bytes are read from the physical 
+     * communication device and stored into the internal receive queue. If the event
+     * is not already set, it is activated to indicate to any waiting thread that
+     * data is available.
+     *
+     * This function is executed only by the communication thread when new data is 
+     * received, so its execution can not be interrupted by other threads trying to
+     * access the class shared resources because the mutex is already locked by the
+     * communications thread function
+     *
+     * This function throws a CCommException in case of any error.
+     */
+    void on_receive(void);
+    /** 
+     * \brief function to handle the errors
+     *
+     * This function handles any error in the communication device. This function
+     * creates an error message with the information of the error, and the activates
+     * the error event and throws an exception in order to finish the thread. It is 
+     * important to note than the thread is terminated if there is any error.
+     *
+     * This function is executed only by the communication thread when new data is 
+     * received, so its execution can not be interrupted by other threads trying to
+     * access the class shared resources because the mutex is already locked by the
+     * communications thread function
+     *
+     * This function throws a CCommException in case of any error.
+     */
+    void on_error(void);
+    /**
+     * \brief Function to set the object identifier
+     *
+     * This function sets the unique identifier of each object. This function
+     * is protected, and therefore can only be executed inside the class, because
+     * the identifier can not be modified after construction of the object.
+     *
+     * This function throws a CCommException if there is any error.
+     *
+     * \param comm_id A null terminated string which identified the 
+     *                communication device. This string is used to create a
+     *                unique identifier for all the threads and events of the
+     *                class.
+     */
+     void set_id(const std::string& comm_id);
+     /**
+      * \brief Function to actually open the device
+      *
+      * This function is called automatically when the base class open() function
+      * is called. It must create a handle to the communication device in order to 
+      * be used in the future. The device handle must be provided by any inherited 
+      * class since it is device dependant.
+      *
+      * This class accepts a generic parameter (void *) to allow the user to pass 
+      * to the function any data structure. This parameter comes from the base
+      * class open() function, but no action is performed on it.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      * \param comm_dev a generic pointer (void *) to the data structure needed to
+      *                 identify the communication device to open and any other
+      *                 required information. This parameter may be NULL if not
+      *                 needed.
+      */ 
+     virtual void hard_open(void *comm_dev=NULL)=0;
+     /**
+      * \brief Function to actually configure the device
+      * 
+      * This function is called automatically when the base class config() function
+      * is called. It must configure the communication device as required by the 
+      * application.
+      *
+      * This class accepts a generic parameter (void *) to allow the user to pass 
+      * to the function any data structure. This parameter comes from the base
+      * class open() function, but no action is performed on it.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      * \param config a generic pointer (void *) to the data structure needed to
+      *               configure the communicationd device. This parameter may be
+      *               NULL if not needed.
+      */ 
+     virtual void hard_config(void *config=NULL)=0;
+     /**
+      * \brief Function to actually read from the device
+      *
+      * This function is automatically called when the new data received is activated.
+      * The read() function from the base class gets data from the internal queue, so
+      * this function is not used. It must try to read the ammount of data specified 
+      * and store it in the data buffer provided without blocking. Also, it must 
+      * return the number of bytes actually read from the devicve, since they may be
+      * different than the desired value.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      *  \param data a reference to the buffer where the received data must be 
+      *  copied. The necessary memory for this buffer must be allocated before 
+      *  calling this function and have enough size to store all the desired data.
+      *  If this buffer is not initialized, the function throws an exception.
+      *
+      *  \param len a positive interger that indicates the number of byte to be
+      *  read from the communication device. This value must be at most the length 
+      *  of the data buffer provided to the function.
+      *
+      *  \return an integer with the number of bytes actually read. These number 
+      *  coincide with the desired number if there is enough data in the internal 
+      *  queue, but it could be smaller if not.
+      */ 
+     virtual int hard_read(unsigned char *data, int len)=0;
+     /**
+      * \brief Function to actually write to the device
+      *
+      * This function is automatically called when the base class write() function
+      * is called. It must try to write the desired ammount of data to the communication 
+      * device without blocking. Also it must return the number of bytes actually
+      * written to the communication device since they may be different from the
+      * desired value.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      *  \param data a reference to the buffer with the data must be send to the 
+      *  device. The necessary memory for this buffer must be allocated before 
+      *  calling this function and have enough size to store all the desired data.
+      *  If this buffer is not initialized, the function throws an exception.
+      *
+      *  \param len a positive interger that indicates the number of byte to be
+      *  written to the communication device. This value must be at most the length 
+      *  of the data buffer provided to the function.
+      *
+      *  \return an integer with the number of bytes actually written. These number 
+      *  coincide with the desired number if there is enough data in the internal 
+      *  queue, but it could be smaller if not.
+      */ 
+     virtual int hard_write(unsigned char *data, int len)=0;
+     /**
+      * \brief Function to actually get the number of bytes availables
+      *
+      * This function is called when the new data received event is activated.
+      * It must get the number of data bytes available from the communication
+      * device and return its value without blocking.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      * \return an integer with the number of bytes available in the receive queue. 
+       * This value can be 0 if there is no data available, but there is ni upper
+      * limit in its value.
+      */ 
+     virtual int hard_get_num_data(void)=0;
+     /**
+      * \brief Function to actually wait for a given communication event
+      *
+      * This function is called in the internal communciation thread to wait for 
+      * any event on the communuication device. It must check for any event on the
+      * device (reception or error) and return the corresponding identifier. When 
+      * an event is activated, this function must return to allow the base class 
+      * to handle it, and the it is called again to wait for the next event.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      * \return -1 if there has been any error or else the identifier of the 
+      *         communciation event:
+      *
+      * - 1 for the new data received event
+      * - 2 for the error event
+      */ 
+     virtual int hard_wait_comm_event(void)=0;
+     /**
+      * \brief Function to actually close the device
+      *
+      * This function is called when the base class close() funciton is called. It
+      * must free the device handle initialized by the open() function and also free
+      * any other resource allocated by the inherited class.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      */ 
+     virtual void hard_close(void)=0;
+  public:
+    /** 
+     * \brief Constructor
+     * 
+     * This constructor creates the receive, send and error events and also the 
+     * thread to handle these events. By default all the events are reset. After
+     * creating the object is already possible to get the event identifiers for 
+     * the receive and error events.
+     *
+     * The thread is attached to the function to be executed, but it is not 
+     * started yet. It is only started when the open() function is called.  
+     *
+     * Also the error message is not initialized (set to NULL) at construction 
+     * time. This message is only initialized when there is an error on the
+     * communication device.
+     *
+     * By default, the intial state of the communication device is created.
+     *
+     * \param comm_id A null terminated string which identified the 
+     *                communication device. This string is used to create a
+     *                unique identifier for all the threads and events of the
+     *                class.
+     */
+    CComm(const std::string& comm_id);
+    /** 
+     * \brief Function to retrieve the object identifier
+     *
+     * This function returns the object identifier of the object as a null
+     * terminated string.
+     *
+     * This function thows a CCommException if there is any error.
+     *
+     * \return A refernce to a non allocated memory space where the
+     * identifier of the object is to be copied. The necessary memory is allocated
+     * internally to match the size of the identifier. The calling process is
+     * responsible of freeing the allocated memory.
+     */
+    std::string& get_id(void);
+    /**
+     * \brief function to open the communication device
+     *
+     * This function starts the communication thread that has been created at
+     * construction time if the device is in the created state. Otherwise, an
+     * exception is thrown because the device should be alredy opened. If it is
+     * necessary to open a new communication device using a previously 
+     * initialized CCom  object, it is necessary to first close the communication 
+     * device with the close() function and the call this function again. If 
+     * successfull, this function changes the current state of the device to
+     * opened.
+     *
+     * This function does not actually open any communication device, since this
+     * class is a generic placeholder for communication devices. It is the 
+     * inherited class which has to provide a hard_open() function that actually 
+     * opens the desired communication device. The inherited class hard_open() 
+     * function must initialize the device handler.
+     * 
+     * This function throws a CCommException if there is any error.
+     */
+    void open(void *comm_dev=NULL);
+    /**
+     * \brief function to configure the communication device
+     *
+     * This function only changes the current state of the communication device
+     * if it is not already configured, but does not configure any of the 
+     * device parameters since they greatly vary from one communication device to
+     * another. If the device has not been previously opened, this function 
+     * throws an exception.
+     *
+     * It is the inherited class that has to provide a hard_config() function that 
+     * actually configures the particular parameters of the communication device. 
+     *
+     * This function throws a CCommException if there is any error.
+     */
+    void config(void *config=NULL);
+    /**
+     * \brief function to read from the communication device
+     *
+     * This function tries to read a given number of bytes from the communication
+     *  device if the device has been opened and configured. Otherwise it throws 
+     *  an exception. If the number of bytes currently in the receive queue is less
+     *  than the desired number, the function returns all the available data and
+     *  also the number of bytes actually read. 
+     *
+     *  If the number of bytes in the queue is greater or equal than the desired 
+     *  number, the function returns all the required data. In both cases, the 
+     *  function will never block. When read, the bytes are removed from the
+     *  internal queue, so they can not be read again. Use the get_num_data()
+     *  function to get how many bytes are available in the receive queue.
+     *
+     *  If the internal receive queue gets empty after a read operation, the
+     *  receive event is cleared to indicate that there is no more data available.
+     *  This event will be set again by the communication thread when new data
+     *  is received.
+     *
+     *  This function throws a CCommException if there is any error.
+     *
+     *  \param data a reference to the buffer where the received data must be 
+     *  copied. The necessary memory for this buffer must be allocated before 
+     *  calling this function and have enough size to store all the desired data.
+     *  If this buffer is not initialized, the function throws an exception.
+     *
+     *  \param len a positive interger that indicates the number of byte to be
+     *  read from the communication device. This value must be at most the length 
+     *  of the data buffer provided to the function.
+     *
+     *  \return an integer with the number of bytes actually read. These number 
+     *  coincide with the desired number if there is enough data in the internal 
+     *  queue, but it could be smaller if not.
+     */
+    int read(unsigned char *data,int len);
+    /**
+     * \brief function to write to the communication device
+     *
+     * This function tries to write a given number of bytes to the communication
+     * device if the device has been opened and configured. Otherwise it throws 
+     * an exception. If the device is currently sending data, the new data provided
+     * to this function is temporary stored into the internal send queue to be
+     * trasmited when the device is ready. Otherwise, the information is send
+     * immediately.
+     *
+     * If the new data is send immediatelly, the send event is reset to indicate 
+     * that the device is not ready to send data, and any further write operation
+     * will temporary store the data into the internal queue. This event is set
+     * again by the communication device when a transmission is ended and there
+     * is no data in the send queue.
+     *
+     * This function throws a CCommException if there is any error.
+     *
+     *  \param data a reference to the buffer with the data must be send to the 
+     *  device. The necessary memory for this buffer must be allocated before 
+     *  calling this function and have enough size to store all the desired data.
+     *  If this buffer is not initialized, the function throws an exception.
+     *
+     *  \param len a positive interger that indicates the number of byte to be
+     *  written to the communication device. This value must be at most the length 
+     *  of the data buffer provided to the function.
+     *
+     *  \return an integer with the number of bytes actually written. These number 
+     *  coincide with the desired number if there is enough data in the internal 
+     *  queue, but it could be smaller if not.
+     */
+    int write(unsigned char *data, int len);
+    /**
+     * \brief function to get the number of bytes in the reception queue
+     *
+     * This function returns the number of byte currently available in the internal
+     * receive queue.
+     *
+     * \return an integer with the number of bytes available in the receive queue. 
+     * This value can be 0 if there is no data available, but there is ni upper
+     * limit in its value.
+     */
+    unsigned int get_num_data(void);
+    /**
+     * \brief function to get the receive event identifier
+     * 
+     * This fucntion returns the receive event identifier as a null terminated 
+     * string that can be used to access the event from any thread.
+     *
+     * This function throws a CCommException in case of any error.
+     *
+     * \return A reference to a non-allocated memory space where the 
+     * identifier of the event is to be copied. The necessary memory is allocated
+     * internally to match the size of the identifier. The calling process is 
+     * responsible of freeing the allocated memory.
+     *
+     */
+    std::string& get_rx_event_id(void);
+    /** 
+     * \brief function to get the error event identifier
+     *
+     * This fucntion returns the error event identifier as a null terminated 
+     * string that can be used to access the event from any thread.
+     *
+     * This function throws a CCommException in case of any error.
+     *
+     * \return A reference to a non-allocated memory space where the 
+     * identifier of the event is to be copied. The necessary memory is allocated
+     * internally to match the size of the identifier. The calling process is 
+     * responsible of freeing the allocated memory.
+     */
+    std::string& get_error_event_id(void);
+    /**
+     * \brief function to get the error message
+     *
+     * This function returns the error message of the error that caused the 
+     * communication thread to end. This function will return always a NULL 
+     * pointer except when an error has ocurred. 
+     *
+     * This function throws a CCommException in case of any error.
+     *
+     * \return A reference to a non-allocated memory space where the 
+     * error message is to be copied. The necessary memory is allocated
+     * internally to match the size of the identifier. The calling process is 
+     * responsible of freeing the allocated memory.
+     */
+    std::string& get_error(void);
+    /** 
+     * \brief function to get the current state of the comunication device
+     * 
+     * This function returns the current state of the communication device. The
+     * state is automatically updated by the class itself and can not be modified
+     * outside the class.  
+     *
+     * This function throws a CCommException in case of any error.
+     *
+     * \return the current state of the communication device. The possible state 
+     * are:
+     *  - created: when the object has been just created.
+     *  - opened: after opening the device with the open() function.
+     *  - configured: after calling the config() function.
+     */
+    int get_state(void);
+    /**
+     * \brief function to close the communication device
+     *
+     * This function closes the communication device. This operation consists on
+     * terminating the communication thread to avoid the event from being set or
+     * reset by it. Even if the particular communication device is opened by an
+     * inherited class, this function closes it. So the inherited class does not
+     * have to do it. Finally, any data in both the receive and the send queues 
+     * are flushed.
+     *
+     * This function does not destroy the events or the thread objects because 
+     * the device can still be opened again. These objects are only destroyed 
+     * when the object itself is destroyed. After calling this function, the
+     * current state of the communication device is set to created whatever
+     * the old state was.
+     *
+     * This function throws a CCommException in case of any error.
+     */
+    void close(void);
+    /**
+     * \brief destructor
+     *
+     * This function destroys the communication object. First, it calls the
+     * close() function to finish the thread and flush any remaining data. It
+     * then destroys all the events and the communication thread.
+     *
+     * This function throws a CCommException in case of any error.  
+     */
+    virtual ~CComm();
+};
+
+#endif
diff --git a/src/commexceptions.cpp b/src/commexceptions.cpp
new file mode 100644
index 0000000..1c17119
--- /dev/null
+++ b/src/commexceptions.cpp
@@ -0,0 +1,12 @@
+#include "commexceptions.h"
+#include <string.h>
+#include <stdio.h>
+
+const std::string comm_exception_msg="[CComm class] - ";
+
+CCommException::CCommException(const std::string& where,const std::string& error_msg,const std::string& comm_id):CException(where,comm_exception_msg)
+{
+  this->error_msg+=error_msg;
+  this->error_msg+=" - ";
+  this->error_msg+=comm_id;
+}
diff --git a/src/commexceptions.h b/src/commexceptions.h
new file mode 100644
index 0000000..30b535b
--- /dev/null
+++ b/src/commexceptions.h
@@ -0,0 +1,58 @@
+#ifndef COMM_EXCEPTIONS
+#define COMM_EXCEPTIONS
+
+#include "exceptions.h"
+
+/**
+ * \brief Generic communication exception class
+ *
+ * This class implements the exceptions for the CComm class. In addition
+ * to the basic error message provided by the base class CException, this
+ * exception class provides also the unique identifier of the communication
+ * device that generated the exception.
+ *
+ * Also, similarly to other exception classes, it appends a class identifer
+ * string ("[CComm class] - ") to the error message in order to identify the
+ * class that generated the exception.
+ *
+ * The base class can be used to catch any exception thrown by the application
+ * or also, this class can be used in order to catch only exceptions generated 
+ * by CComm objects.
+ */
+class CCommException : public CException
+{
+  public:
+    /**
+     * \brief Constructor
+     *
+     * The constructor calls the base class constructor to add the general
+     * exception identifier and then adds the class identifier string 
+     * "[CComm class]" and the supplied error message. 
+     *
+     * It also appends the unique identifier of the communication device
+     * that generated the exception. So, the total exception message will 
+     * look like this:
+     *
+     * \verbatim
+     * [Exception caught] - <where>
+     * [CComm class] - <error message> - <comm id>
+     * \endverbatim
+     *
+     * \param where a null terminated string with the information about the name
+     *              of the function, the source code filename and the line where
+     *              the exception was generated. This string must be generated 
+     *              by the _HERE_ macro.
+     *
+     * \param error_msg a null terminated string that contains the error message.
+     *                  This string may have any valid character and there is no
+     *                  limit on its length.
+     *
+     * \param comm_id a null terminated string that contains the communication
+     *                device unique identifier. This string must be the one used 
+     *                to create the communication device.
+     *
+     */
+    CCommException(const std::string& where, const std::string& error_msg,const std::string& comm_id);
+};
+
+#endif
diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt
new file mode 100644
index 0000000..e90ba12
--- /dev/null
+++ b/src/examples/CMakeLists.txt
@@ -0,0 +1,35 @@
+# edit the following line to add the source code for the example and the name of the executable
+ADD_EXECUTABLE(test_rs232 test_rs232.cpp)
+
+# edit the following line to add the necessary libraries
+TARGET_LINK_LIBRARIES(test_rs232 ${IRIUTILS_LIBRARY} comm pthread)
+
+IF(BUILD_FTDI)
+  ADD_EXECUTABLE(test_ftdi test_ftdi.cpp)
+
+  TARGET_LINK_LIBRARIES(test_ftdi ${IRIUTILS_LIBRARY} ${FTDI_LIBRARY} comm pthread)
+ENDIF(BUILD_FTDI)
+
+# edit the following line to add the source code for the example and the name of the executable
+ADD_EXECUTABLE(test_client test_simple_client.cpp)
+
+# edit the following line to add the necessary libraries
+TARGET_LINK_LIBRARIES(test_client ${IRIUTILS_LIBRARY} comm pthread)
+
+# edit the following line to add the source code for the example and the name of the executable
+ADD_EXECUTABLE(test_server test_simple_server.cpp)
+
+# edit the following line to add the necessary libraries
+TARGET_LINK_LIBRARIES(test_server ${IRIUTILS_LIBRARY} comm pthread)
+
+# edit the following line to add the source code for the example and the name of the executable
+ADD_EXECUTABLE(test_multiple_server test_multiple_server.cpp)
+
+# edit the following line to add the necessary libraries
+TARGET_LINK_LIBRARIES(test_multiple_server ${IRIUTILS_LIBRARY} comm pthread)
+
+# edit the following line to add the source code for the example and the name of the executable
+ADD_EXECUTABLE(test_multiple_client test_multiple_client.cpp)
+
+# edit the following line to add the necessary libraries
+TARGET_LINK_LIBRARIES(test_multiple_client ${IRIUTILS_LIBRARY} comm pthread)
diff --git a/src/examples/test_ftdi.cpp b/src/examples/test_ftdi.cpp
new file mode 100644
index 0000000..ad031e3
--- /dev/null
+++ b/src/examples/test_ftdi.cpp
@@ -0,0 +1,73 @@
+#include "ftdimodule.h"
+#include "ftdiserver.h"
+#include "ftdiexceptions.h"
+#include <stdio.h>
+
+/** \example test_ftdi.cpp
+ * 
+ * This example shows how to use the D2XX FTDI driver.
+ *
+ * This examples first creates an FTDI server which scans for all FTDI devices
+ * connected to the computer with the default PID and VID combinations provided
+ * by the manufacturer. If devices with different PID and VID combinations are
+ * used, it is necessary to use the add_custom_PID() function.
+ *
+ * When done, the information of all devices detected is displayed on screen.
+ * Then the first FTDI device, if any, is opened and configured. If this 
+ * operations are successfull, the specific information of the device is also
+ * displyed on screen.
+ *
+ * The output of this example when a single FTDI device is connected is as 
+ * follows:
+ *
+ * \verbatim
+ * Number of FTDI devices detected: 1
+ * Device 0
+ * The device is closed
+ * The device is full speed
+ * Type: 5
+ * Device id: 67330049
+ * Location: 0
+ * Serial number: A2001mHr
+ * Description: FT232R USB UART
+ * 
+ * ****************** FTDI Devices Info ***********************
+ * Device name: FTDI_A2001mHr
+ * Type: 5
+ * Device id: 67330049
+ * Serial Number: A2001mHr
+ * Description: FT232R USB UART
+ * \endverbatim
+ *
+ * This example program does not try to read and write from and to the device
+ * because it highly depends on the particular device connected. This example
+ * only scans for available devices and displays its information. For a full
+ * example of this driver, see the segwayRMP200 driver.
+ */
+int main(int argc, char *argv[])
+{
+  CFTDIServer *ftdi_server=CFTDIServer::instance();
+  CFTDI *ftdi_device=NULL;
+  TFTDIconfig ftdi_config;
+
+  ftdi_config.baud_rate = 115200;
+  ftdi_config.word_length = 8;
+  ftdi_config.stop_bits = 2;
+  ftdi_config.parity = 0;
+  ftdi_config.read_timeout = 1000;
+  ftdi_config.write_timeout = 1000;
+  ftdi_config.latency_timer = 16;
+
+  std::cout << (*ftdi_server) << std::endl;
+  if(ftdi_server->get_num_devices()>0)
+  {
+    ftdi_device=ftdi_server->get_device(ftdi_server->get_serial_number(0));
+    ftdi_device->config(&ftdi_config);
+    std::cout << (*ftdi_device) << std::endl;
+    ftdi_device->close();
+  }
+  if(ftdi_device!=NULL)
+    delete ftdi_device;
+}
+
+
diff --git a/src/examples/test_multiple_client.cpp b/src/examples/test_multiple_client.cpp
new file mode 100644
index 0000000..bfc5a00
--- /dev/null
+++ b/src/examples/test_multiple_client.cpp
@@ -0,0 +1,51 @@
+#include "socketclient.h"
+#include "socketexceptions.h"
+#include "eventexceptions.h"
+
+std::string server_ip="147.83.76.185";
+int server_port=2012;
+
+int main(int argc,char *argv[])
+{
+  CEventServer *event_server=CEventServer::instance();
+  CSocketClient client("client");
+  std::list<std::string> events;
+  unsigned char msg[5]="hola";
+  int i=0,event_id=0;
+  TSocket_info info;
+
+  try{
+    info.address=server_ip;
+    info.port=server_port;
+    std::cout << "connecting ... " << std::endl;
+    while(!client.is_connected())
+    {
+      try{
+        client.open(&info);
+      }catch(CSocketNoConnectionException &e){
+        std::cout << "  nobody is listening" << std::endl;
+        sleep(1);
+      }
+    }
+    client.config();
+    std::cout << "connected." << std::endl;
+    events.push_back(client.get_rx_event_id());
+    events.push_back(client.get_connection_closed_event());
+    for(i=0;i<10;i++)
+    {
+      try{
+        event_id=event_server->wait_first(events,1000);
+        if(event_id==1)
+          break;
+      }catch(CEventTimeoutException &e){
+        std::cout << "sending data ..." << std::endl;
+        client.write(msg,5);
+      }
+    }
+    sleep(1);
+    std::cout << "closing connection ..." << std::endl;
+    client.close();
+  }catch(CException &e){
+    std::cout << e.what() << std::endl;
+  }
+}
diff --git a/src/examples/test_multiple_server.cpp b/src/examples/test_multiple_server.cpp
new file mode 100644
index 0000000..376491d
--- /dev/null
+++ b/src/examples/test_multiple_server.cpp
@@ -0,0 +1,84 @@
+#include "eventexceptions.h"
+#include "socketserver.h"
+#include "eventserver.h"
+
+std::string server_IP_address="147.83.76.185";
+int server_port=2012;
+int num_listen=1;
+
+int main(int argc,char *argv[])
+{
+  CEventServer *event_server=CEventServer::instance();
+  std::list<TClient_info *>::iterator it;
+  int event_id=0,num=0,client_index=0;
+  std::list<TClient_info *> info;
+  CSocketServer server("server");
+  std::list<std::string> events;
+  TClient_info *client_info;
+  TSocket_info sock_info;
+  unsigned char *data;
+
+  try{
+    sock_info.port=server_port;
+    sock_info.address=server_IP_address;
+    server.open(&sock_info);
+    server.config(&num_listen);
+    server.set_max_clients(5);
+    server.start_server();
+    while(1)
+    {
+      try{
+        events.clear();
+        events.push_back(server.get_new_connection_event_id());
+        for(it=info.begin();it!=info.end();it++)
+        {
+          events.push_back((*it)->rx_event_id);
+          events.push_back((*it)->disconnect_event_id);
+        }
+        event_id=event_server->wait_first(events,1000);
+        if(event_id==0)
+        {
+          std::cout << "new connection ..." << std::endl;
+          client_info=server.get_new_client();
+          info.push_back(client_info);
+          std::cout << "  client id: " << client_info->client_id << std::endl;
+          std::cout << "  IP address: " << client_info->IP << " at port " << client_info->port << std::endl;
+        }
+        else
+        {
+          event_id--;
+          for(it=info.begin();it!=info.end();it++)
+          {
+            if(event_id>=2)
+              event_id-=2;
+            else
+            {
+              if((event_id%2)==1)// disconnect event
+              {
+                std::cout << "client " << (*it)->client_id << " disconnected." << std::endl;
+                server.free_client((*it));
+                it=info.erase(it);
+              }
+              else// rx event
+              {
+                std::cout << "  data received from " << (*it)->client_id << " ";
+                num=server.get_num_data_from((*it)->client_id);
+                data=new unsigned char[num];
+                server.read_from((*it)->client_id,data,num);
+                std::cout << data << std::endl;
+                delete[] data;
+              }
+              break;
+            }
+          }
+        }
+      }catch(CEventTimeoutException &e){
+        std::cout << "waiting ..." << std::endl;
+      }catch(CException &e){
+        std::cout << e.what() << std::endl;
+      }
+    }
+  }catch(CException &e){
+    std::cout << e.what() << std::endl;
+  }    
+}
diff --git a/src/examples/test_rs232.cpp b/src/examples/test_rs232.cpp
new file mode 100644
index 0000000..4598df3
--- /dev/null
+++ b/src/examples/test_rs232.cpp
@@ -0,0 +1,119 @@
+#include "eventserver.h"
+#include "threadserver.h"
+#include "commexceptions.h"
+#include "rs232.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <string>
+#include <iostream>
+
+const std::string serial_dev="/dev/ttyS0";
+const std::string empty_string="";
+
+/**
+ * \example test_rs232.cpp
+ *
+ * This example shows how to use the serial port driver
+ *
+ * This example tries to use the first serial port available to send and 
+ * receive data. This example is intended to be used with the RX and TX signals
+ * of the serial cable connected, so that all the data sent by the driver is 
+ * recevived by itself.
+ *
+ * \verbatim
+ * RX --\
+ *      |
+ * TX --/
+ * \endverbatim
+ *
+ * The CComm base class provide events to signal the data reception and also
+ * some unexpected error on the port. These events are used in this example to
+ * wait for all the required data, instead of peridically polling it for new 
+ * data (the event server is used).
+ *
+ * By default the serial port used is the /dev/ttyS0. If more serial ports are 
+ * available or USB to serial adapters are present, the device name can be
+ * changed to the desired one: /dev/ttySn or /dev/ttyUSBn.
+ *
+ * The output of this example should be something like this:
+ *
+ * \verbatim
+ *
+ * \endverbatim
+ *
+ * Several error may be thrown either by the CComm class or the CRS232 class:
+ *
+ * * Invalid device name to open.
+ *
+ * * Trying to configure the serial port before being opened.
+ *
+ * * Trying to open the port once it is already opened.
+ *
+ * * Trying to send or receive data before configuring the serial port.
+ *
+ * * Trying to write invalid data-
+ *
+ * * Providing an invalid buffer to save all the received data. 
+ */
+int main(int argc,char *argv[])
+{
+  CEventServer *event_server=CEventServer::instance();
+  unsigned char msg[5]="hola";
+  std::string rx_event;
+  CRS232 serial_port("serial_port");
+  std::list<std::string> events;
+  int num=0,total_length=0,i=0;
+  TRS232_config serial_config;
+
+  serial_config.baud=9600;
+  serial_config.num_bits=8;
+  serial_config.parity=none;
+  serial_config.stop_bits=1;
+  try{
+    serial_port.open((void *)&empty_string);
+  }catch(CCommException &e){
+    std::cout << e.what() << std::endl;
+  }
+  try{
+    serial_port.config(&serial_config);
+  }catch(CCommException &e){
+    std::cout << e.what() << std::endl;
+  }
+  serial_port.open((void *)&serial_dev);
+  rx_event=serial_port.get_rx_event_id();
+  std::cout << "Data reception event id: " << rx_event << std::endl;
+  events.push_back(rx_event);
+  try{
+    serial_port.open((void *)"/dev/ttyS0");
+  }catch(CCommException &e){
+    std::cout << e.what() << std::endl;
+  }
+  try{
+    serial_port.write(msg,5);
+  }catch(CCommException &e){
+    std::cout << e.what() << std::endl;
+  }
+  serial_port.config(&serial_config);
+  try{
+    serial_port.write(NULL,5);
+  }catch(CCommException &e){
+    std::cout << e.what() << std::endl;
+  }
+  try{
+    for(i=0;i<10;i++)
+    {
+      serial_port.write(msg,5);
+      do{
+        event_server->wait_all(events,1000);
+        num=serial_port.get_num_data();
+        serial_port.read(&msg[total_length],num);
+        total_length+=num;
+      }while(total_length<5);
+      total_length=0;
+      printf("Message received: %s\n",msg);
+    }
+  }catch(CException &e){
+    std::cout << e.what() << std::endl;
+  }
+  serial_port.close();
+}
diff --git a/src/examples/test_simple_client.cpp b/src/examples/test_simple_client.cpp
new file mode 100644
index 0000000..488d181
--- /dev/null
+++ b/src/examples/test_simple_client.cpp
@@ -0,0 +1,44 @@
+#include "socketclient.h"
+#include "socketexceptions.h"
+
+std::string server_ip="192.168.0.13";
+int server_port=6653;
+
+int main(int argc,char *argv[])
+{
+  TSocket_info info;
+  CSocketClient client("client");
+  unsigned char msg[5]="hola",answer[5];
+
+  try{
+    while(1)
+    {
+      info.address=server_ip;
+      info.port=server_port;
+      std::cout << "connecting ... " << std::endl;
+      while(!client.is_connected())
+      {
+        try{
+          client.open(&info);
+        }catch(CSocketNoConnectionException &e){
+          std::cout << "  nobody is listening" << std::endl;
+          sleep(1);
+        }
+      }
+      client.config(NULL);
+      std::cout << "connected." << std::endl;
+      sleep(1);
+      std::cout << "sending data ..." << std::endl;
+      client.write(msg,5);
+      sleep(1);
+      client.read(answer,5);
+      std::cout << "data received ... " << answer << std::endl;
+      sleep(1);
+      std::cout << "closing connection ..." << std::endl;
+      client.close();
+      sleep(1);
+    }
+  }catch(CException &e){
+    std::cout << e.what() << std::endl;
+  }
+}
diff --git a/src/examples/test_simple_server.cpp b/src/examples/test_simple_server.cpp
new file mode 100644
index 0000000..4c6a7a2
--- /dev/null
+++ b/src/examples/test_simple_server.cpp
@@ -0,0 +1,65 @@
+#include "eventexceptions.h"
+#include "socketserver.h"
+#include "eventserver.h"
+
+std::string server_IP_address="127.0.0.1";
+int server_port=2012;
+int num_listen=1;
+
+int main(int argc,char *argv[])
+{
+  CEventServer *event_server=CEventServer::instance();
+  CSocketServer server("server");
+  std::list<std::string> con_events,client_events;
+  TSocket_info sock_info;
+  unsigned char *data;
+  TClient_info *info;
+  int event_id,num;
+
+  try{
+    sock_info.port=server_port;
+    sock_info.address=server_IP_address;
+    server.open(&sock_info);
+    server.config(&num_listen);
+    server.set_max_clients(5);
+    con_events.push_back(server.get_new_connection_event_id());
+    server.start_server();
+    while(1)
+    {
+      try{
+        event_server->wait_first(con_events,1000);
+        std::cout << "new connection ..." << std::endl;
+        info=server.get_new_client();
+        std::cout << "  client id: " << info->client_id << std::endl;
+        std::cout << "  IP address: " << info->IP << " at port " << info->port << std::endl;
+        client_events.clear();
+        client_events.push_back(info->rx_event_id);
+        client_events.push_back(info->disconnect_event_id);
+        do{
+          event_id=event_server->wait_first(client_events);
+          if(event_id==0)
+          {
+            std::cout << "  data received ... ";
+            num=server.get_num_data_from(info->client_id);
+            data=new unsigned char[num];
+            server.read_from(info->client_id,data,num);
+            std::cout << data << std::endl;
+            delete[] data;
+          }
+          if(event_id==1)
+          {
+            server.free_client(info);
+            std::cout << "client disconnected ... " << std::endl;
+          }
+        }while(event_id!=1);
+      }catch(CEventTimeoutException &e){
+        std::cout << "waiting ..." << std::endl;
+      }catch(CException &e){
+        std::cout << e.what() << std::endl;
+      }
+    }
+    server.stop_server();
+  }catch(CException &e){
+    std::cout << e.what() << std::endl;
+  }    
+}
diff --git a/src/serial/rs232.cpp b/src/serial/rs232.cpp
new file mode 100644
index 0000000..ceffa0a
--- /dev/null
+++ b/src/serial/rs232.cpp
@@ -0,0 +1,357 @@
+#include "rs232.h"
+#include "rs232exceptions.h"
+#include "ctime.h"
+
+CRS232::CRS232(const std::string& comm_id) : CComm(comm_id)
+{
+  this->serial_fd=-1;
+}
+
+void CRS232::set_baudrate(int baud)
+{
+  struct termios config;
+  int baudrate;
+
+  if(tcgetattr(this->serial_fd,&config)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the attribute structure.\n",this->comm_id);
+  }
+  else
+  {
+    switch(baud)
+    {
+      case 50: baudrate=B50;
+               break; 
+      case 75: baudrate=B75;
+               break;
+      case 110: baudrate=B110;
+                break;
+      case 134: baudrate=B134;
+                break;
+      case 150: baudrate=B150;
+                break;
+      case 200: baudrate=B200;
+                break;
+      case 300: baudrate=B300;
+                break;
+      case 600: baudrate=B600; 
+                break;
+      case 1200: baudrate=B1200;
+                 break;
+      case 1800: baudrate=B1800;
+                 break;
+      case 2400: baudrate=B2400;
+                 break;
+      case 4800: baudrate=B4800;
+                 break;
+      case 9600: baudrate=B9600;
+                 break;
+      case 19200: baudrate=B19200;
+                  break;
+      case 38400: baudrate=B38400;
+                  break;
+      case 57600: baudrate=B57600;
+                  break;
+      case 115200: baudrate=B115200;
+                   break;
+      default: /* handle exception */
+               throw CRS232Exception(_HERE_,"Invalid baudrate. See the documentation for the possible values.\n",this->comm_id);
+               break;
+    }
+    cfsetispeed(&config,baudrate);
+    cfsetospeed(&config,baudrate);
+    if(tcsetattr(this->serial_fd,TCSANOW,&config)==-1)
+    {
+      /* handle exceptions */
+      throw CRS232Exception(_HERE_,"Impossible to set up the new attribute structure.\n",this->comm_id);
+    }
+  }
+}
+
+void CRS232::set_num_bits(char num_bits)
+{
+  struct termios config;
+  int bits;
+
+  if(tcgetattr(this->serial_fd,&config)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the attribute structure.\n",this->comm_id);
+  }
+  else
+  {
+    switch(num_bits)
+    {
+      case 5: bits=CS5;
+              break;
+      case 6: bits=CS6;
+              break;
+      case 7: bits=CS7;
+              break;
+      case 8: bits=CS8;
+              break;
+      default: /* handle exceptions */
+               throw CRS232Exception(_HERE_,"Invalid number of bits per packet. See the documentation for the possible values.\n",this->comm_id);
+               break;
+    }
+    config.c_cflag&=~CSIZE;
+    config.c_cflag|=bits;
+    if(tcsetattr(this->serial_fd,TCSANOW,&config)==-1)
+    {
+      /* handle exceptions */
+      throw CRS232Exception(_HERE_,"Impossible to set up the new attribute structure.\n",this->comm_id);
+    }
+  }
+}
+
+void CRS232::set_parity(parity_type parity)
+{
+  struct termios config;
+
+  if(tcgetattr(this->serial_fd,&config)==-1)
+  {
+    /* handle exception */
+    throw CRS232Exception(_HERE_,"Impossible to get the attribute structure.\n",this->comm_id);
+  }
+  else
+  {
+    if(parity!=none)
+    {
+      config.c_cflag|=PARENB;
+      if(parity==odd) config.c_cflag|=PARODD;
+      else if(parity==even) config.c_cflag&=~PARODD;
+      else
+      {
+        /* handle exceptions */
+        throw CRS232Exception(_HERE_,"Invalid parity type. See the documentation for the possible values.\n",this->comm_id);
+      }
+    }
+    else config.c_cflag&=~PARENB;
+    if(tcsetattr(this->serial_fd,TCSANOW,&config)==-1)
+    {
+      /* handle exceptions */
+      throw CRS232Exception(_HERE_,"Impossible to set up the new attribute structure.\n",this->comm_id);
+    }
+  }
+}
+
+void CRS232::set_stop_bits(char stop_bits)
+{
+  struct termios config;
+
+  if(tcgetattr(this->serial_fd,&config)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the attribute structure.\n",this->comm_id);
+  } 
+  else
+  {
+    if(stop_bits==2) config.c_cflag|=CSTOPB;
+    else if(stop_bits==1)config.c_cflag&=~CSTOPB;
+    else
+    {
+      /* handle execptions */
+      throw CRS232Exception(_HERE_,"Invalid number of stop bits. See the documentation for the possible values.\n",this->comm_id);
+    }
+    if(tcsetattr(this->serial_fd,TCSANOW,&config)==-1)
+    {
+      /* handle exception */
+      throw CRS232Exception(_HERE_,"Impossible to set up the new attribute structure.\n",this->comm_id);
+    }
+  }
+}
+
+void CRS232::hard_open(void *comm_dev)
+{
+  std::string *serial_dev=(std::string *)comm_dev;
+  struct termios config;
+
+  if(serial_dev->size()==0)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Invalid serial port device name (empty string).\n",this->comm_id);
+  }
+  else
+  {
+    this->serial_fd=::open(serial_dev->c_str(),O_RDWR | O_NOCTTY | O_NONBLOCK | O_ASYNC);
+    if(this->serial_fd==-1)
+    {
+      /* handle exceptions */
+      throw CRS232Exception(_HERE_,"Impossible to open the serial port.\n",this->comm_id);
+    }
+    else
+    {
+      if(tcgetattr(this->serial_fd,&config)==-1)
+      {
+        /* handle exceptions */
+        throw CRS232Exception(_HERE_,"Impossible to get the attribute structure.\n",this->comm_id);
+      }
+      else
+      {
+        config.c_cflag|=(CLOCAL | CREAD);
+        config.c_cflag&=~CRTSCTS;
+        config.c_lflag&=~(ICANON | ECHO | ECHOE | ISIG);
+        config.c_iflag&=~(IXON | IXOFF | IXANY);
+        config.c_iflag&=~(INLCR | IGNCR | ICRNL);
+        config.c_oflag&=~OPOST;
+        config.c_cc[VMIN]=0;
+        config.c_cc[VTIME]=100;
+        if(tcsetattr(this->serial_fd,TCSANOW,&config)==-1)
+        {
+          /* handle exceptions */
+          throw CRS232Exception(_HERE_,"Impossible to set up the new attribute structure.\n",this->comm_id);
+        }
+      }
+    }
+  }
+}
+
+void CRS232::hard_config(void *config)
+{
+  TRS232_config *serial_conf=(TRS232_config *)config;
+
+  this->set_baudrate(serial_conf->baud);
+  this->set_num_bits(serial_conf->num_bits);
+  this->set_parity(serial_conf->parity);
+  this->set_stop_bits(serial_conf->stop_bits);
+}
+
+int CRS232::hard_read(unsigned char *data, int len)
+{
+  int num_read=0;
+
+  if((num_read=::read(this->serial_fd,data,len))==-1)
+  {
+    return -1;
+  }
+
+  return num_read;
+}
+
+int CRS232::hard_write(unsigned char *data, int len)
+{
+  int num_written=0;
+
+  if((num_written=::write(this->serial_fd,data,len))==-1)
+  {
+    return -1;
+  } 
+
+  return num_written; 
+}
+
+int CRS232::hard_get_num_data(void)
+{
+  int num;
+
+  if(ioctl(this->serial_fd,FIONREAD,&num)==-1)
+  {
+    return -1;
+  }
+
+  return num;
+}
+
+int CRS232::hard_wait_comm_event(void)
+{
+  fd_set receive_set,error_set;
+  int max_fd,wait_result=0;
+
+  max_fd=this->serial_fd+1;
+  FD_ZERO(&receive_set);
+  FD_SET(this->serial_fd,&receive_set);
+  FD_ZERO(&error_set);
+  FD_SET(this->serial_fd,&error_set);
+  wait_result=select(max_fd,&receive_set,NULL,&error_set,NULL);
+  if(wait_result==-1)
+    return -1; 
+  else
+  {
+    if(FD_ISSET(this->serial_fd,&receive_set))/* data has been received */
+    {
+      return 1;
+    }
+    if(FD_ISSET(this->serial_fd,&error_set))
+    {
+      return 2;
+    }
+  }
+
+  return -1;
+}
+
+void CRS232::hard_close(void)
+{
+  if(this->serial_fd!=-1)
+  {
+    ::close(this->serial_fd);
+    this->serial_fd=-1;
+  }
+}
+
+void CRS232::set_control_signal(control_signals signal)
+{
+  int status;
+
+  if(ioctl(this->serial_fd, TIOCMBIS, (const int *)&signal)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the control signals status.\n",this->comm_id);
+  } 
+
+  return;
+
+  if(ioctl(this->serial_fd, TIOCMGET, &status)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the control signals status.\n",this->comm_id);
+  } 
+  status|=signal;
+  if(ioctl(this->serial_fd, TIOCMSET, status)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to set the control signals status.\n",this->comm_id);
+  }
+}
+
+void CRS232::clear_control_signal(control_signals signal)
+{
+  int status;
+
+  if(ioctl(this->serial_fd, TIOCMBIC, (const int *)&signal)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the control signals status.\n",this->comm_id);
+  } 
+
+  return;
+
+  if(ioctl(this->serial_fd, TIOCMGET, &status)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to get the control signals status.\n",this->comm_id);
+  } 
+  status&=~signal;
+  if(ioctl(this->serial_fd, TIOCMSET, status)==-1)
+  {
+    /* handle exceptions */
+    throw CRS232Exception(_HERE_,"Impossible to set the control signals status.\n",this->comm_id);
+  }
+}
+
+bool CRS232::get_control_signal(control_signals signal)
+{
+  int status;
+
+  ioctl(this->serial_fd, TIOCMGET, &status);
+  if(status&signal) 
+    return true;
+  else
+    return false;  
+}
+
+CRS232::~CRS232()
+{
+  this->close();
+}
diff --git a/src/serial/rs232.h b/src/serial/rs232.h
new file mode 100644
index 0000000..18d603d
--- /dev/null
+++ b/src/serial/rs232.h
@@ -0,0 +1,424 @@
+#ifndef _RS232_H
+#define _RS232_H
+
+#include "comm.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>  
+#include <fcntl.h>
+#include <errno.h>
+#include <string>
+
+/**
+ * \brief Available types of parity
+ *
+ * This enumeration provides all the possible parity types available on the
+ * RS-232 serial port. The parity types are:
+ *
+ * * none: no parity bit is used and there is no way to detect errors.
+ * * even: adds an additional bit such that the number of ones in each packet
+ *         is even.
+ * * odd: adds an additional bit such that the number of ones in each packet
+ *        is odd.
+ * * mark: adds an additional bit with the fixed value of one.
+ * * space: adds an additional bit with the fixed value of zero.
+ */
+typedef enum {none,even,odd,mark,space} parity_type;
+
+/**
+ * \brief Available control signals of the RS232 port
+ *
+ * This enumeration provides identifiers for all the control signals of
+ * an standard serial port. These signals include the following:
+ * 
+ * * DSR: Data Set Ready. This signal indicates the sending device that the
+ *        receiving device is ready.
+ * * DTR: Data Terminal Ready. This signal indicates the receiving device that
+ *        the sending device is ready.
+ * * RTS: Request To Send. This signal is used to perform hardware flow control
+ *        and is used by the sending device to check if it is possible to start 
+ *        sending data.
+ * * CTS: Clear To Send. This signal is used to perform hardware flow control
+ *        and it is used by the receiving device as an answer to the RTS signal
+ *        to notify that the receiving device is ready to receive data.
+ * * CD: Data Carrier Detect. This signal was used in modems to notify that the
+ *       modem was connected to the telephon line.
+ * * RI: Ring Indicator: This signal was used in modems to notify that the ring
+ *       signal was detected on the telephone line.
+ *
+ * This is the standard function for each of the control signals of a serial port,
+ * However they can be used for other purposes.
+ *
+ */
+typedef enum {rs232_dsr=TIOCM_DSR,
+              rs232_dtr=TIOCM_DTR,
+              rs232_rts=TIOCM_RTS,
+              rs232_cts=TIOCM_CTS,
+              rs232_cd=TIOCM_CD,
+              rs232_ri=TIOCM_RI} control_signals;
+
+/**
+ * \brief Configuration structure for the serial port
+ */
+typedef struct{
+  /**
+   * \brief Serial port baudrate
+   *
+   * This filed represents the desired speed in bits per second. Not all values are 
+   * valid. The valid values for this parameter are listed below:
+   *
+   * - 50
+   * - 75
+   * - 110
+   * - 134
+   * - 150
+   * - 200
+   * - 300
+   * - 600
+   * - 1200
+   * - 1800
+   * - 2400
+   * - 4800
+   * - 9600
+   * - 19200
+   * - 38400
+   * - 57600
+   * - 115200
+   *
+   */
+  int baud;
+  /**
+   * \brief Number of bits per paquet
+   *
+   * This field represents the number of bits of each packet. Not all values 
+   * are valid. The valid values for this parameter are 5,6,7 or 8.
+   */ 
+  char num_bits;
+  /**
+   * \brief Parity type
+   *
+   * This field represents the desired parity type for each packet. This parameter 
+   * must belong to the parity_type enumeration. See the documentation on this data
+   * type for more information on the possible values of this parameter.
+   */ 
+  parity_type parity;
+  /**
+   * \brief Number of stop bits
+   *
+   * This field represents the desired number of stop bits per packet. The possible 
+   * values for this parameter are only 1 or 2.
+   */ 
+  char stop_bits;
+}TRS232_config;
+
+/** 
+ * \brief RS-232 Serial port driver
+ *
+ * This class implements a driver to use the standard RS-232 ports on any
+ * computer. It inherits from the CComm class which provides the basic 
+ * interface to any communication device. 
+ *
+ * This class overloads the open() and config() functions of the base class 
+ * to suit the specific requirements of a serial port. Also, several 
+ * functions are declared to configure the features of the serial port:
+ * baudrate, number of bits, parity and number of stop bits.
+ *
+ * For a more detailed description of the behavior of this class, see the
+ * documentation for the CComm base class.
+ */
+class CRS232 : public CComm
+{
+  private:
+    /**
+     * \brief write file descriptor of the communication device
+     * 
+     * This variable is the write file descriptor associated to the communication 
+     * device. By default it is initialized to -1, and it is the inherited class
+     * that must initialize it when actually opening the communication device by
+     * calling the open() function. The read and write file descriptors can be
+     * the same or different, depending on the particular communication device.
+     */
+    int serial_fd;
+  protected:
+    /**
+     * \brief Function to set the desired baudrate
+     *
+     * This functions sets the speed of the srial port in bits per second.
+     * If the given speed is not valid this function throws a CRS232Exception.
+     *
+     * \param baud the desired speed in bits per second. Not all values are 
+     *             valid. The valid values for this parameter are listed below:
+     *
+     * - 50
+     * - 75
+     * - 110
+     * - 134
+     * - 150
+     * - 200
+     * - 300
+     * - 600
+     * - 1200
+     * - 1800
+     * - 2400
+     * - 4800
+     * - 9600
+     * - 19200
+     * - 38400
+     * - 57600
+     * - 115200
+     *
+     */  
+    void set_baudrate(int baud);
+    /**
+     * \brief Function to set the number of bits per packet
+     *  
+     * This function sets the number of data bits included in each packet. If
+     * the given number of bits is not valid, a CRS232Exception is thrown.
+     *
+     * \param num_bits The number of bits of each packet. Not all values are 
+     *                 valid. The valid values for this parameter are 5,6,7 or
+     *                 8.
+     *
+     */  
+    void set_num_bits(char num_bits);
+    /**
+     * \brief Function to set the parity
+     *
+     * This function sets the desired parity (if any) for each of the packets.
+     * If the given parity type is not valid, a CRS232Exception is thrown.
+     *
+     * \param parity the desired parity type for each packet. This parameter 
+     *               must belong to the parity_type enumeration. See the
+     *               documentation on this data type for more information
+     *               on the possible values of this parameter.
+     */  
+    void set_parity(parity_type parity);
+    /**
+     * \brief Function to set the number of stop bits
+     *
+     * This function sets the desired number of stop bits for each packet. If
+     * the given number of stop bits is not valid, a CRS232Exception is thrown.
+     *
+     * \param stop_bits the desired number of stop bits per packet. The possible 
+     *                  values for this parameter are only 1 or 2.
+     *
+     */  
+    void set_stop_bits(char stop_bits);
+    /**
+     * \brief Function to open an RS232 serial port
+     *
+     * This function is called automatically when the base class open() function
+     * is called. It must create a handle to the communication device in order to 
+     * be used in the future. The device handle must be provided by any inherited 
+     * class since it is device dependant.
+     *
+     * This class accepts a generic parameter (void *) to allow the user to pass 
+     * to the function any data structure. This parameter comes from the base
+     * class open() function, but no action is performed on it.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \param comm_dev a generic pointer (void *) to the data structure needed to
+     *                 identify the communication device to open and any other
+     *                 required information. This parameter may be NULL if not
+     *                 needed.
+     * \verbatim
+     * /dev/ttyS0
+     * \endverbatim
+     */
+     virtual void hard_open(void *comm_dev);
+     /**
+     * \brief Function to configure the serial port
+     * 
+     * This function is called automatically when the base class config() function
+     * is called. It must configure the communication device as required by the 
+     * application.
+     *
+     * This class accepts a generic parameter (void *) to allow the user to pass 
+     * to the function any data structure. This parameter comes from the base
+     * class open() function, but no action is performed on it.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \param config a pointer to a TRS232_config structure already initialized
+     *               with all teh configuration parameters of the serial port.
+     */ 
+     virtual void hard_config(void *config);
+     /**
+      * \brief Function to actually read from the device
+      *
+      * This function is automatically called when the new data received is activated.
+      * The read() function from the base class gets data from the internal queue, so
+      * this function is not used. It must try to read the ammount of data specified 
+      * and store it in the data buffer provided without blocking. Also, it must 
+      * return the number of bytes actually read from the devicve, since they may be
+      * different than the desired value.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      *  \param data a reference to the buffer where the received data must be 
+      *  copied. The necessary memory for this buffer must be allocated before 
+      *  calling this function and have enough size to store all the desired data.
+      *  If this buffer is not initialized, the function throws an exception.
+      *
+      *  \param len a positive interger that indicates the number of byte to be
+      *  read from the communication device. This value must be at most the length 
+      *  of the data buffer provided to the function.
+      *
+      *  \return an integer with the number of bytes actually read. These number 
+      *  coincide with the desired number if there is enough data in the internal 
+      *  queue, but it could be smaller if not.
+      */ 
+     virtual int hard_read(unsigned char *data, int len);
+     /**
+      * \brief Function to actually write to the device
+      *
+      * This function is automatically called either when the base class write()
+      * function is called or when the data transmission end event is activated.
+      * It must try to write the desired ammount of data to the communication 
+      * device without blocking. Also it must return the number of bytes actually
+      * written to the communication device since they may be different from the
+      * desired value.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      *  \param data a reference to the buffer with the data must be send to the 
+      *  device. The necessary memory for this buffer must be allocated before 
+      *  calling this function and have enough size to store all the desired data.
+      *  If this buffer is not initialized, the function throws an exception.
+      *
+      *  \param len a positive interger that indicates the number of byte to be
+      *  written to the communication device. This value must be at most the length 
+      *  of the data buffer provided to the function.
+      *
+      *  \return an integer with the number of bytes actually written. These number 
+      *  coincide with the desired number if there is enough data in the internal 
+      *  queue, but it could be smaller if not.
+      */
+     virtual int hard_write(unsigned char *data, int len);
+     /**
+      * \brief Function to actually get the number of bytes availables
+      *
+      * This function is called when the new data received event is activated.
+      * It must get the number of data bytes available from the communication
+      * device and return its value without blocking.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      * \return an integer with the number of bytes available in the receive queue. 
+      * This value can be 0 if there is no data available, but there is ni upper
+      * limit in its value.
+      */
+     virtual int hard_get_num_data(void);
+     /**
+      * \brief Function to actually wait for a given communication event
+      *
+      * This function is called in the internal communciation thread to wait for 
+      * any event on the communuication device. It must check for any event on the
+      * device (reception, end of transmission or error) and return the
+      * corresponding identifier. When an event is activated, this function must 
+      * return to allow the base class to handle it, and the it is called again
+      * to wait for the next event.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      *
+      * \return -1 if there has been any error or else the identifier of the 
+      *         communciation event:
+      *
+      * - 0 for the end of transmission event
+      * - 1 for the new data received event
+      * - 2 for the error event
+      */
+     virtual int hard_wait_comm_event(void);
+     /**
+      * \brief Function to actually close the device
+      *
+      * This function is called when the base class close() funciton is called. It
+      * must free the device handle initialized by the open() function and also free
+      * any other resource allocated by the inherited class.
+      *
+      * This function can throw any CCommException object exception or else any
+      * exception class defined by the inherited class.
+      */
+     virtual void hard_close(void);
+  public:
+    /**
+     * \brief Constructor
+     *
+     * This constructor creates a new CRS232 object initialized by default, 
+     * but it does not open any physical serial port. This constructor calls
+     * the base class constructor with the serial port unique identifier
+     * provided to the constructor.
+     *
+     * \param comm_id A null terminated string which identified the
+     *                communication device. This string is used to create a
+     *                unique identifier for all the threads and events of the
+     *                class.
+     *
+     */ 
+    CRS232(const std::string& comm_id);
+    /**
+     * \brief Function to set an RS232 control signal
+     * 
+     * This function changes the state of an output control signal to its 
+     * active state (negative voltage). See the documentation on the 
+     * control_signals enumeration type for information on the possible control
+     * signals.
+     *
+     * If the state of the control signal can not be changed, an CRS232Exception
+     * object is thrown.
+     *
+     * \param signal an identifier of the signal to be activated. This parameter
+     *               must be of the control_signals enumeration type. 
+     *
+     */
+    void set_control_signal(control_signals signal);
+    /**
+     * \brief Funciton to reset an RS232 control signal
+     *
+     * This function chnages the state of an output control signal to its inactive
+     * state (positive voltage). See the documentation on the control_signals
+     * enumeration type for information on the possible control signals.
+     *
+     * If the state of the control signal can not be changed, an CRS232Exception 
+     * object is thrown.
+     *
+     * \param signal an identifier of the signal to be activated. This parameter
+     *               must be of the control_signals enumeration type. 
+     *
+     */
+    void clear_control_signal(control_signals signal);
+    /**
+     * \brief Function to get the current state of an RS232 control signal
+     *
+     * This function returns the current state of an input control signal. See
+     * the documentation on the control_signals enumeration type for information
+     * on the possible control signals.
+     *
+     * If the state of the control signal can not be changed, an CRS232Exception 
+     * object is thrown.
+     *
+     * \return the current state of the desired control signal. True is the 
+     *         current state is active (negative voltage) and false id the 
+     *         current state is inactive (positive voltage).
+     */
+    bool get_control_signal(control_signals signal);
+    /**
+     * \brief destructor
+     *
+     * This destructor does nothing. The base class destructor is the one in 
+     * charge of freeing all the allocated resources.
+     */  
+    ~CRS232();
+};
+
+#endif
diff --git a/src/serial/rs232exceptions.cpp b/src/serial/rs232exceptions.cpp
new file mode 100644
index 0000000..369bdc7
--- /dev/null
+++ b/src/serial/rs232exceptions.cpp
@@ -0,0 +1,11 @@
+#include "rs232exceptions.h"
+#include <string.h>
+#include <stdio.h>
+
+const std::string rs232_exception_msg="[CRS232 class] - ";
+
+CRS232Exception::CRS232Exception(const std::string& where,const std::string& error_msg,const std::string& comm_id):CCommException(where,rs232_exception_msg,error_msg)
+{
+  this->error_msg+=" - ";
+  this->error_msg+=comm_id;
+}
diff --git a/src/serial/rs232exceptions.h b/src/serial/rs232exceptions.h
new file mode 100644
index 0000000..afacce0
--- /dev/null
+++ b/src/serial/rs232exceptions.h
@@ -0,0 +1,60 @@
+#ifndef RS232_EXCEPTIONS
+#define RS232_EXCEPTIONS
+
+#include "commexceptions.h"
+
+/**
+ * \brief RS232 exception class
+ *
+ * This class implements the exceptions for the CRS232 communication class. 
+ * In addition to the basic error message provided by the base class CException, 
+ * this exception class provides also the unique identifier of the communication
+ * device that generated the exception.
+ *
+ * Also, similarly to other exception classes, it appends a class identifer
+ * string ("[CRS232 class] - ") to the error message in order to identify 
+ * the class that generated the exception.
+ *
+ * The base class can be used to catch any exception thrown by the application
+ * or also, this class can be used in order to catch only exceptions generated 
+ * by CS232 objects.
+ *
+ */
+class CRS232Exception : public CCommException
+{
+  public:
+    /**
+     * \brief Constructor
+     *
+     * The constructor calls the base class constructor to add the general
+     * exception identifier and then adds the class identifier string 
+     * "[CRS232 class]" and the supplied error message. 
+     *
+     * It also appends the unique identifier of the communication device 
+     * that generated the exception. So, the total exception message will 
+     * look like this:
+     *
+     * \verbatim
+     * [Exception caught] - <where>
+     * Error: [CComm class] - [CRS232 class] - <error message> - <comm id>
+     * \endverbatim
+     *
+     * \param where a null terminated string with the information about the name
+     *              of the function, the source code filename and the line where
+     *              the exception was generated. This string must be generated 
+     *              by the _HERE_ macro.
+     *
+     * \param error_msg a null terminated string that contains the error message.
+     *                  This string may have any valid character and there is no
+     *                  limit on its length.
+     *
+     * \param comm_id a null terminated string that contains the communication
+     *                device unique identifier. This string must be the one used to 
+     *                create the object.
+     *
+     *
+     */  
+    CRS232Exception(const std::string& where,const std::string& error_msg,const std::string& comm_id);
+};
+
+#endif
diff --git a/src/sockets/socket.cpp b/src/sockets/socket.cpp
new file mode 100644
index 0000000..aaddeff
--- /dev/null
+++ b/src/sockets/socket.cpp
@@ -0,0 +1,182 @@
+#include "socket.h"
+#include "socketexceptions.h"
+#include <string.h>
+
+CSocket::CSocket(const std::string &comm_id) : CComm(comm_id)
+{
+  struct hostent *host;
+  char name[1024];
+
+  this->connection_closed_event.clear();
+  this->connection_closed_event+=comm_id;
+  this->connection_closed_event+="_closed";
+  this->event_server->create_event(this->connection_closed_event);
+  this->cloned=false;
+}
+
+CSocket::CSocket(const std::string &comm_id,const int fd) : CComm(comm_id)
+{
+  struct hostent *host;
+  char name[200];
+
+  if(fd<0)
+  {
+    /* handle exceptions */
+    throw CSocketException(_HERE_, "Invalid file descriptor",comm_id);
+  }
+  else
+  {
+    this->connection_closed_event.clear();
+    this->connection_closed_event+=comm_id;
+    this->connection_closed_event+="_closed";
+    this->event_server->create_event(this->connection_closed_event);
+    this->cloned=true;
+    this->socket_fd=fd;
+  }
+}
+
+CSocket *CSocket::create_socket(const std::string &comm_id, const int fd)
+{
+  CSocket *new_socket;
+
+  new_socket=new CSocket(comm_id,fd);
+
+  return new_socket;
+}
+
+std::string CSocket::get_connection_closed_event(void)
+{
+  return this->connection_closed_event;
+}
+
+void CSocket::hard_open(void *comm_dev)
+{
+  int yes=1;
+
+  if(!this->cloned)
+  {
+    if((this->socket_fd=socket(AF_INET,SOCK_STREAM,0))<0)
+    {
+      /* handle exceptions */
+      throw CSocketException(_HERE_,"Error opening socket", this->comm_id);
+    }
+    else
+    {
+      if(setsockopt(this->socket_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))<0)
+      {
+        /* handle exceptions */
+        throw CSocketException(_HERE_,"Impossible to change socket options",this->comm_id);
+      }
+    }
+  }
+}
+
+void CSocket::hard_config(void *config)
+{
+  /* do nothing */
+}
+
+int CSocket::hard_read(unsigned char *data, int len)
+{
+  int num_read=0;
+
+  if(!this->event_server->event_is_set(this->connection_closed_event))
+  {
+    if((num_read=::read(this->socket_fd,data,len))==-1)
+    {
+      /* handle exceptions */
+      throw CSocketException(_HERE_,"Error while reading from the socket.",this->comm_id);
+    }
+  }
+
+  return num_read;
+}
+
+int CSocket::hard_write(unsigned char *data, int len)
+{
+  int num_write=0;
+  
+  if(!this->event_server->event_is_set(this->connection_closed_event))
+  {
+    if((num_write=::write(this->socket_fd,data,len))==-1)
+    {
+      /* handle exceptions */
+      throw CSocketException(_HERE_,"Error while writing to the socket.",this->comm_id);
+    }
+  }
+
+  return num_write;
+}
+
+int CSocket::hard_get_num_data(void)
+{
+  int num;
+
+  if(!this->event_server->event_is_set(this->connection_closed_event))
+  {
+    if(ioctl(this->socket_fd,FIONREAD,&num)==-1)
+    {
+      /* handle exceptions */
+      throw CSocketException(_HERE_,"Error while getting the number of bytes available from the socket.",this->comm_id);
+    }
+  }
+
+  return num;
+}
+
+int CSocket::hard_wait_comm_event(void)
+{
+  fd_set receive_set,error_set;
+  int max_fd,wait_result=0;
+
+  max_fd=this->socket_fd+1;
+  FD_ZERO(&receive_set);
+  FD_SET(this->socket_fd,&receive_set);
+  FD_ZERO(&error_set);
+  FD_SET(this->socket_fd,&error_set);
+  wait_result=select(max_fd,&receive_set,NULL,&error_set,NULL);
+  if(wait_result==-1)
+  {
+    /* handle exceptions */
+    throw CSocketException(_HERE_,"Error while waiting for socket events",this->comm_id);
+  }
+  else
+  {
+    if(FD_ISSET(this->socket_fd,&receive_set))/* data has been received */
+    {
+      if(this->hard_get_num_data()==0)
+      {
+        if(!this->event_server->event_is_set(this->connection_closed_event))
+          this->event_server->set_event(this->connection_closed_event);
+        return -1;
+      }
+      else
+        return 1;
+    }
+    if(FD_ISSET(this->socket_fd,&error_set))
+      return 2;
+  }
+  
+  return -1;
+}
+
+void CSocket::hard_close(void)
+{
+  if(this->socket_fd!=-1)
+  {
+    if(::close(this->socket_fd)<0)
+      throw CSocketException(_HERE_,"Error while closing the socket",this->comm_id);
+    this->socket_fd=-1;
+  }
+  this->cloned=false;
+}
+
+CSocket::~CSocket()
+{
+  this->close();
+  if(this->connection_closed_event.size()!=0)
+  {
+    this->event_server->delete_event(this->connection_closed_event);
+    this->connection_closed_event.clear();
+  }
+}
diff --git a/src/sockets/socket.h b/src/sockets/socket.h
new file mode 100644
index 0000000..89f1763
--- /dev/null
+++ b/src/sockets/socket.h
@@ -0,0 +1,322 @@
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+#include "comm.h"
+#include "mutex.h"
+#include <iostream>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+
+/**
+ * \brief Socket information structure
+ *
+ * This structure holds information about the remote IP address and the port 
+ * where the socket is connected.  
+ *
+ */
+typedef struct {
+  /**
+   * \brief address of the socket
+   *
+   * A string with the desired IP addres to assign to the socket. The IP address 
+   * must be passed with the dot format, as shown here
+   *
+   * * 192.168.0.1
+   */
+  std::string address;
+  /**
+   * \brief port of the socket
+   *
+   * An integer with the port number to be assigned to the socket. This value can 
+   * be any positive interger which is not used by any other application.
+   */
+  int port;
+}TSocket_info;
+
+/**
+ * \brief Socket driver
+ *
+ * This class implements a driver to use generic sockets using the TCP/IP protocol.
+ * It inherits from CComm to include all the basic functionality of a communication
+ * device and reimplements the socket specific functions.
+ *
+ * This class provides an event which is activated when the connection is remotely
+ * closed. This event can be used by the application using the socket to monitor
+ * the state iof the connection. To get the identifier of this event, use the 
+ * get_connection_closed_event() function.
+ *
+ * A special constructor is provided to create a new CSocket object from an already
+ * initialized socket file descriptor provided by some system function of a 
+ * manufacturer library. However, this constructor is no public, and it can only
+ * be accessed by the create_socket() function.
+ *
+ * In case of any error, this class throws an exceptions of the CSocketException class.
+ *
+ * For more detailed description of the behavior of this class, see the documentation
+ * for the CComm base class.
+ */
+
+class CSocket : public CComm
+{
+  private:
+    /**
+     * \brief Connection closed event identifier
+     *
+     * This string holds the unique identifier of the event to indicate the
+     * closure of a connection. Its value is initialized at construction time
+     * with the name assigned to the class to create the unique identifier for
+     * the event. Use the get_connection_closed_event() function to get this
+     * string.
+     */
+    std::string connection_closed_event;
+    /**
+     * \brief cloned socket flag
+     *
+     * This attribute is used to differentiate between a CSocket object created
+     * by the default constructor and those created by the create_socket() function.
+     * This flag is mainly used to avoid opening a new socket when the hard_open()
+     * function is called. The value of this attribute is changed internally and can 
+     * not be accessed from the outside.
+     */ 
+    bool cloned;
+  protected:
+    /** 
+     * \brief socket file descriptor.
+     *
+     * This integer holds the file descriptor of the socket associated to the
+     * object. This attribute is initialized when either the open() or create_socket()
+     * functions are called and can not be modified afterwards. Its value is valid 
+     * until the close() function is called.
+     *
+     */
+    int socket_fd;
+    /**
+     * \brief Constructor with parameters 
+     *
+     * This constructor is used only by the create_socket() function to create a 
+     * new CSocket object and associate it to an already initialized socket file
+     * descriptor. Since it is protected, it can not be used as a regular 
+     * constructor.
+     *
+     * \param comm_id a string with the unique identifier of the socket object.
+     *                This identifier is used to create the unique identifiers 
+     *                of the internal threads and events, so it is imperative
+     *                that each socket has a unique name.
+     *
+     * \param fd a positive integer with the value of the file descriptor of
+     *           an already initialized socket. If the file descriptor provided
+     *           to this function is not valid or it is not properly initialized,
+     *           unexpected errors will ocurr.
+     */
+    CSocket(const std::string &comm_id,const int fd);
+    /**
+     * \brief function to create a new socket object from a file descriptor
+     *
+     * This function is used in a server side application to create a new
+     * CSocket object (or any inherited class) from an already initialized
+     * socket file descriptor. This is required when the server socket 
+     * accepts a new connection with the accept() function, which returns
+     * a file descriptor which is not associated to any CSocket object.
+     *
+     * This function is defined as static so that it can be called without
+     * having to create an object of this class, but it can not be accessed
+     * outside an object of this class, since it is defined as protected.
+     *
+     * The most common use of this function is in a server side of a connection
+     * just after the accept() function returns with a new client connection, in
+     * order to associate the new socket to a CSocket object.
+     *
+     * \param comm_id a string with the unique identifier of the socket object.
+     *                This identifier is used to create the unique identifiers 
+     *                of the internal threads and events, so it is imperative
+     *                that each socket has a unique name.
+     *
+     * \param fd a positive integer with the value of the file descriptor of
+     *           an already initialized socket. If the file descriptor provided
+     *           to this function is not valid or it is not properly initialized,
+     *           unexpected errors will ocurr.
+     *
+     * \return a new CSocket object initialized with the parameters passed as 
+     *         arguments to the function.
+     */
+    static CSocket *create_socket(const std::string &comm_id, const int fd);
+     /**
+      * \brief Function to actually open the device
+      *
+      * This function is called automatically when the base class open() function
+      * is called. By default, it initializes the socket file descriptor using the
+      * socket() system call. But, if the object has been created by the 
+      * create_socket() function, this function does nothing.
+      *
+      * This class does not need any parameter since the socket is created by 
+      * default using a TCP/IP protocol and the AF_INET socket family. So the
+      * argument passed to this function is ignored and may be NULL.
+      * 
+      * This function can throw any CSocketException object exception or the generic 
+      * CCommException class.
+      *
+      * \param comm_dev this parameter is ignored in this case and can be set to NULL.
+      *                 It is only keeped for backward compatiblity with the CComm
+      *                 class.
+      */
+    virtual void hard_open(void *comm_dev=NULL);
+    /**
+      * \brief Function to actually configure the device
+      * 
+      * This function is called automatically when the base class config() function
+      * is called. In this case this function does nothing, since all the necessay
+      * parameters are provide to the open() function. However, it is necessary to 
+      * call the config() function to successfully change the internal state of the
+      * communication device.
+      *
+      * The provided parameter in this case can be NULL since no configuration is
+      * needed. This function can throw any CCommException object exception or else 
+      * any exception class defined by the inherited class.
+      *
+      * \param config This parameter is not used since no configuration is required.
+      *               It is only keeped for backward compatiblity with the CComm
+      *               class.
+      */
+     virtual void hard_config(void *config=NULL);
+    /**
+     * \brief Function to actually read from the device
+     *
+     * This function is automatically called when the new data received event is 
+     * activated. The read() function from the base class gets data from the 
+     * internal queue, so this function is not used. It must try to read the 
+     * ammount of data specified and store it in the data buffer provided without 
+     * blocking. Also, it must return the number of bytes actually read from the 
+     * devicve, since they may be different than the desired value.
+     *
+     * In case of any error, this function throws a CSocketException exception.
+     *
+     * \param data a reference to the buffer where the received data must be 
+     * copied. The necessary memory for this buffer must be allocated before 
+     * calling this function and have enough size to store all the desired data.
+     * If this buffer is not initialized, the function throws an exception.
+     *
+     * \param len a positive interger that indicates the number of byte to be
+     * read from the communication device. This value must be at most the length 
+     * of the data buffer provided to the function.
+     *
+     * \return an integer with the number of bytes actually read. These number 
+     * coincide with the desired number if there is enough data in the internal 
+     * queue, but it could be smaller if not.
+     */
+    virtual int hard_read(unsigned char *data, int len);
+    /**
+     * \brief Hard write function
+     *
+     * This function is automatically called when the base class write() function
+     * is called. It must try to write the desired ammount of data to the communication 
+     * device without blocking. Also it must return the number of bytes actually
+     * written to the communication device since they may be different from the
+     * desired value.
+     *
+     * In case of any error, this function throws a CSocketException exception.
+     *
+     * \param data a reference to the buffer with the data must be send to the 
+     * device. The necessary memory for this buffer must be allocated before 
+     * calling this function and have enough size to store all the desired data.
+     * If this buffer is not initialized, the function throws an exception.
+     *
+     * \param len a positive interger that indicates the number of byte to be
+     * written to the communication device. This value must be at most the length 
+     * of the data buffer provided to the function.
+     *
+     * \return an integer with the number of bytes actually written. These number 
+     * coincide with the desired number if there is enough data in the internal 
+     * queue, but it could be smaller if not.
+     */
+    virtual int hard_write(unsigned char *data, int len);
+    /**
+     * \brief Function to actually get the number of bytes availables
+     *
+     * This function is called when the new data received event is activated.
+     * It must get the number of data bytes available from the communication
+     * device and return its value without blocking.
+     *
+     * In case of any error, this function throws a CSocketException exception.
+     *
+     * \return an integer with the number of bytes available in the receive queue. 
+     * This value can be 0 if there is no data available, but there is ni upper
+     * limit in its value.
+     */
+    virtual int hard_get_num_data(void);
+    /**
+     * \brief Function to actually wait for a given communication event 
+     *
+     * This function is called in the internal communciation thread to wait for 
+     * any event on the communuication device. It must check for any event on the
+     * device (reception, end of transmission or error) and return the
+     * corresponding identifier. When an event is activated, this function must 
+     * return to allow the base class to handle it, and the it is called again
+     * to wait for the next event.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \return -1 if there has been any error or else the identifier of the 
+     *         communciation event:
+     *
+     * - 1 for the new data received event
+     * - 2 for the error event
+     */
+    virtual int hard_wait_comm_event(void);
+    /** 
+     * \brief Hard close function
+     *
+     * This function is called when the base class close() funciton is called. It
+     * must free the device handle initialized by the open() function and also free
+     * any other resource allocated by the inherited class.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     */
+    virtual void hard_close(void);
+  public:
+    /**
+     * \brief default constructor
+     *
+     * This constructor creates a new CSocket object. It does not open a physical 
+     * socket, it only allocates the necessary resources to use it.
+     *
+     * \param comm_id  A null terminated string which identifies the 
+     *                 communications device. This string is used to created a
+     *                 unique identifier for all the threads and events of the
+     *                 class.
+     */
+    CSocket(const std::string &comm_id);
+    /** 
+     * \brief Function to get the name of Connection Closed Event
+     *  
+     * This function is used to retrieve the identifier of the connection closed
+     * event. This event is initialized at contruction time, and it can be
+     * retrieved at any time. 
+     *
+     * This function only returns a copy of the internal attribute, so the value
+     * returned by this function can be modified without affecting the proper
+     * function of the class.
+     *
+     * \return a string with a copy of the identifier of the connection closed
+     *         event. This identifier can be used in the wait_first() or wait_all()
+     *         functions of the CEventServer class to wait its activation.
+     */
+    std::string get_connection_closed_event(void);
+    /**
+     * \brief Destructor
+     *
+     * This destructor does nothing. The base class destructor is the one in 
+     * charge of freeing all the allocated resources.
+     */
+    virtual ~CSocket();
+};
+
+#endif
diff --git a/src/sockets/socketclient.cpp b/src/sockets/socketclient.cpp
new file mode 100644
index 0000000..efb63cf
--- /dev/null
+++ b/src/sockets/socketclient.cpp
@@ -0,0 +1,70 @@
+#include "socketclient.h"
+#include "socketexceptions.h"
+#include <string.h>
+
+CSocketClient::CSocketClient(const std::string &sock_id) : CSocket(sock_id)
+{
+  this->connected=false;
+  this->remote.address.clear();
+  this->remote.port=-1;
+}
+
+void CSocketClient::hard_close(void)
+{
+  CSocket::hard_close();
+  this->connected=false;
+  this->remote.address.clear();
+  this->remote.port=-1;
+}
+
+void CSocketClient::hard_open(void *comm_dev)
+{
+  TSocket_info *remote=(TSocket_info *)comm_dev;
+  sockaddr_in sock;
+   
+  CSocket::hard_open(NULL);
+  memset(&sock,0,sizeof(sock));
+  sock.sin_family=AF_INET;
+  if(inet_aton(remote->address.c_str(),&sock.sin_addr)==0)
+  {
+    /* handle exceptions */
+    throw CSocketException(_HERE_,"Impossible to convert IP address",this->comm_id);
+  }
+  else
+  {
+    sock.sin_port=htons(remote->port);
+    if((::connect(this->socket_fd,(struct sockaddr *)&sock,sizeof(sock)))<0)
+    {
+      /* handle exceptions */
+      if(errno==ECONNREFUSED)
+        throw CSocketNoConnectionException(_HERE_,"Nobody listening",this->comm_id);
+      else
+        throw CSocketException(_HERE_, "Error with connect function", this->comm_id);
+    }
+    else
+    {
+      this->connected=true;
+      this->remote.port=remote->port;
+      this->remote.address=remote->address;
+    }
+  }
+}
+
+int CSocketClient::get_remote_port(void)
+{
+  return this->remote.port;
+}
+
+std::string CSocketClient::get_remote_IP_address(void)
+{
+  return this->remote.address;
+}
+
+bool CSocketClient::is_connected(void)
+{
+  return this->connected;
+}
+
+CSocketClient::~CSocketClient()
+{
+}
diff --git a/src/sockets/socketclient.h b/src/sockets/socketclient.h
new file mode 100644
index 0000000..741a876
--- /dev/null
+++ b/src/sockets/socketclient.h
@@ -0,0 +1,152 @@
+#ifndef _SOCKETCLIENT_H
+#define _SOCKETCLIENT_H
+
+#include "socket.h"
+
+/**
+ * \brief Client side socket 
+ *
+ * This class implements the functionality of a client socket. It inherits from
+ * the socket class for the basic socket communication issues and adds the
+ * necessary resources to work as a client socket.
+ *
+ * The hard_open() function (called when the base class open() function is called)
+ * is extended to try to connect to the desired server. If the server is already 
+ * listening, the connection is set. However, if the server is not yet listening,
+ * a special exception (CSocketNoConnection) is thrown. This special exception can
+ * be catch and used to iterate until the server is ready.
+ *
+ * The general procedure to work with client sockets is as follows:
+ *
+ * 1. Create a new CSocketClient object with a unique identifier.
+ * 2. Call the open() function with the server IP address and port
+ * 3. If a CSocketNoConnection is throw, either repeat step 2 or exit.
+ * 4. On success, call the config() function without any parameters
+ * 5. Perform any read or write operations on the socket
+ * 6. Call the close() function to finish the socket connection.
+ *  
+ */
+class CSocketClient : public CSocket
+{
+  private:
+    /**
+     * \brief information on the remote server
+     *
+     * This structure holds the IP address and port of the remote server to which
+     * the client socket is connected. This structure is initialized after a 
+     * successfull call to the open() function and can not be modified afterwards,
+     * until the socket is closed and reconnected to an other server.
+     *
+     * By default, the IP address string is empty and the port is initialized to -1.
+     * Use the get_remote_port() and get_remote_IP_address() to get the values
+     * of this structure at any time.
+     */ 
+    TSocket_info remote;
+    /**
+     * \brief Connection flag
+     *
+     * This flag indicated whether the client socket is connected to a server or 
+     * not. By default it is set to false, and it is only set to true after a 
+     * successfull call to the open() function. When the socket is closed, and 
+     * therefore the connection is severed, this flag is set back to false.
+     *
+     * Use the is_connected() function to check whether the socket is connected
+     * or not.
+     */ 
+    bool connected;
+  protected:
+    /**
+     * \brief Connect function
+     *
+     * This function is called when the base class open() function is called, and 
+     * it is used to perform the connection to a server. This function requires a 
+     * TSocket_info structure as parameter which must be provided to the open() 
+     * call. It also calls the hard_open() function of the CSocket class to 
+     * initialize the base class attributes.
+     *
+     * The IP address and port within this structure are used to try to stablish
+     * a communication with a remote server. If the server is already listening,
+     * the function returns normally with all the internal attributes properly
+     * initialized.
+     *
+     * If the server is not yet listening, this function throws a CSocketNoConnection
+     * exception. This exception can be catched and used to iterate the process 
+     * until the server is available or terminate the applcation. In case of any
+     * other error, this function throws a CSocketException.
+     *
+     * After the connection is set, it is still necessary to call the config() 
+     * function to properly configure the object to send and receive information.
+     *
+     * \param comm_dev a valid pointer to a TSocket_info structure with the IP
+     *                 address and listening port of the desired server. See the
+     *                 documentation on the TSocket_info structure for more
+     *                 information on the IP address and port format.
+     */
+    virtual void hard_open(void *comm_dev=NULL);
+    /**
+     * \brief function to close the client socket
+     *
+     * This function is called when the close() function of the base class is 
+     * called. It calls the hard_close() function of the CSocket class to
+     * actually terminate the connection and change all the internal attributes 
+     * to reflect that (the connection flag is set back to false and the server
+     * IP address and port are set to the default values.
+     *
+     * In case of any error, this function throws a CSocketException.
+     */ 
+    virtual void hard_close(void);
+  public:
+    /** 
+     * \brief Constructor
+     *
+     * This constructor creates a new client socket object with the provided 
+     * identifier, but does not connect it to any server yet. To do that, it
+     * is necessary to call the open() function of the base class with a 
+     * pointer to a TSocket_info structure as a parameter.
+     *
+     * \param sock_id a null terminated string with the unique identifier for
+     *                the socket. This identifier is internally used to create
+     *                the unique indentifiers for all threads and events, so 
+     *                it is important that a single identifier is not used more
+     *                than once.
+     */
+    CSocketClient(const std::string &sock_id);
+    /**
+     * \brief function to return the remote server port
+     *
+     * This function returns the port number of the current connection if the
+     * socket is connected to a server. Otherwise it returns -1.
+     *
+     * \return the server's port number to which it is connected, or -1 if the
+     *         socket is not currently connected to any server.
+     */ 
+    int get_remote_port(void);
+    /**
+     * \brief function to return the remote server IP address
+     *
+     * This function returns the IP address of the current connection if the
+     * socket is connected to a server. Otherwise it returns an empty string.
+     *
+     * \return the server'a IP address to which it is connected, or an empty
+     *         string if the socket is not currently connected to any server.
+     */
+    std::string get_remote_IP_address(void); 
+    /**
+     * \brief function to check whether the socket is connected or not
+     *
+     * This function checks wether the client socket is connected to a server 
+     * or not.
+     *
+     * \return true is the socket is connected to a server and false otherwise.
+     */ 
+    bool is_connected(void);
+    /**
+      * \brief destructor
+      *
+      * When the object is destroyed, the connection to the server is lost (if
+      * it was not closed before) and all the resources of the client are freed. 
+      */
+    virtual ~CSocketClient();
+};
+
+#endif
diff --git a/src/sockets/socketexceptions.cpp b/src/sockets/socketexceptions.cpp
new file mode 100644
index 0000000..02e7485
--- /dev/null
+++ b/src/sockets/socketexceptions.cpp
@@ -0,0 +1,15 @@
+#include "socketexceptions.h"
+#include <string.h>
+#include <stdio.h>
+
+const string socket_exception_msg="[CSocket class] - ";
+
+CSocketException::CSocketException(const string& where,const string& error_msg,const string& comm_id):CCommException(where,socket_exception_msg,error_msg)
+{
+  this->error_msg+=" - ";
+  this->error_msg+=comm_id;
+}
+
+CSocketNoConnectionException::CSocketNoConnectionException(const string& where,const string& error_msg,const string& comm_id):CSocketException(where,error_msg,comm_id)
+{
+}
diff --git a/src/sockets/socketexceptions.h b/src/sockets/socketexceptions.h
new file mode 100644
index 0000000..75e97ac
--- /dev/null
+++ b/src/sockets/socketexceptions.h
@@ -0,0 +1,116 @@
+#ifndef SOCKET_EXCEPTIONS
+#define SOCKET_EXCEPTIONS
+
+#include "commexceptions.h"
+
+using namespace std;
+
+/**
+ * \brief Socket exception class
+ *
+ * This class implements the exceptions for the CSocket communication class. 
+ * In addition to the basic error message provided by the base class CException, 
+ * this exception class provides also the unique identifier of the communication
+ * device that generated the exception.
+ *
+ * Also, similarly to other exception classes, it appends a class identifer
+ * string ("[CSocket class] - ") to the error message in order to identify 
+ * the class that generated the exception.
+ *
+ * The base class can be used to catch any exception thrown by the application
+ * or also, this class can be used in order to catch only exceptions generated 
+ * by CSocket objects.
+ *
+ */
+class CSocketException : public CCommException
+{
+  public:
+    /** 
+     * \brief Constructor
+     *
+     * The constructor calls the base class constructor to add the general
+     * exception identifier and then adds the class identifier string 
+     * "[CSocket class]" and the supplied error message. 
+     *
+     * It also appends the unique identifier of the communication device 
+     * that generated the exception. So, the total exception message will 
+     * look like this:
+     *
+     * \verbatim
+     * [Exception caught] - <where>
+     * Error: [CComm class] - [CSocket class] - <error message> - <comm id>
+     * \endverbatim
+     *
+     * \param where a null terminated string with the information about the name
+     *              of the function, the source code filename and the line where
+     *              the exception was generated. This string must be generated 
+     *              by the _HERE_ macro.
+     *
+     * \param error_msg a null terminated string that contains the error message.
+     *                  This string may have any valid character and there is no
+     *                  limit on its length.
+     *
+     * \param comm_id a null terminated string that contains the communication
+     *                device unique identifier. This string must be the one used to 
+     *                create the object.
+     *
+     *
+     */  
+    CSocketException(const string& where,const string& error_msg,const string& comm_id);
+};
+
+/**
+ * \brief No connection exception class
+ *
+ * This class implements a special exception for the CSocket class and its inherited
+ * classes that indicates that the connection has been refused. This event ocurrs
+ * when a client side application is trying to connect to a server, and it is still
+ * not ready to accept connections.
+ *
+ * This exception can be caught to prevent the client application to close when the 
+ * server in not yet ready, and then used to iterate until it is. If functionality
+ * is not desired, this exception can be caught as a simple CSocketException or a
+ * basic CException.
+ *
+ * The error message and the information provided by this exception is the same as
+ * the information provided by the CSocketException class.
+ *
+ */
+class CSocketNoConnectionException : public CSocketException
+{
+  public:
+    /**
+     * \brief Constructor
+     *
+     * The constructor calls the base class constructor to add the general
+     * exception identifier and then adds the class identifier string 
+     * "[CSocket class]" and the supplied error message. 
+     *
+     * It also appends the unique identifier of the communication device 
+     * that generated the exception. So, the total exception message will 
+     * look like this:
+     *
+     * \verbatim
+     * [Exception caught] - <where>
+     * Error: [CComm class] - [CSocket class] - <error message> - <comm id>
+     * \endverbatim
+     *
+     * \param where a null terminated string with the information about the name
+     *              of the function, the source code filename and the line where
+     *              the exception was generated. This string must be generated 
+     *              by the _HERE_ macro.
+     *
+     * \param error_msg a null terminated string that contains the error message.
+     *                  This string may have any valid character and there is no
+     *                  limit on its length.
+     *
+     * \param comm_id a null terminated string that contains the communication
+     *                device unique identifier. This string must be the one used to 
+     *                create the object.
+     *
+     *
+     */  
+    CSocketNoConnectionException(const string& where,const string& error_msg,const string& comm_id);
+};
+
+#endif
diff --git a/src/sockets/socketserver.cpp b/src/sockets/socketserver.cpp
new file mode 100644
index 0000000..599e8d0
--- /dev/null
+++ b/src/sockets/socketserver.cpp
@@ -0,0 +1,525 @@
+#include "socketserver.h"
+#include "socketexceptions.h"
+#include "eventexceptions.h"
+#include <errno.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <string.h>
+
+int CSocketServer::counter=0;
+
+CSocketServer::CSocketServer(const std::string &sock_id) : CSocket(sock_id)
+{
+  this->current_clients=0;
+  this->max_clients=0;
+  this->state=server_created;
+  // clear the client list
+  this->client_list.clear();
+  // clear the new connection event id
+  this->new_connection_event_id.clear();
+  this->connect_thread_id.clear();
+  this->disconnect_thread_id.clear();
+  // set up server information
+  this->info.port=-1;
+  this->info.address.clear();
+  // create the threads
+  this->connect_thread_id=sock_id + "_connect_thread";
+  this->thread_server->create_thread(this->connect_thread_id);
+  this->thread_server->attach_thread(this->connect_thread_id,connect_thread,this);
+  this->disconnect_thread_id=sock_id + "_disconnect_thread";
+  this->thread_server->create_thread(this->disconnect_thread_id);
+  this->thread_server->attach_thread(this->disconnect_thread_id,disconnect_thread,this);
+  // create the events
+  this->new_connection_event_id=sock_id+"_new_connection_event";
+  this->event_server->create_event(this->new_connection_event_id);
+  this->update_list_event_id=sock_id+"_update_list_event";
+  this->event_server->create_event(this->update_list_event_id);
+  this->finish_connect_thread_event_id=sock_id+"_finish_connect_thread_event";
+  this->event_server->create_event(this->finish_connect_thread_event_id);
+  this->finish_disconnect_thread_event_id=sock_id+"_finish_disconnect_thread_event";
+  this->event_server->create_event(this->finish_disconnect_thread_event_id);
+}
+
+void CSocketServer::hard_open(void *comm_dev)
+{
+  TSocket_info *info=(TSocket_info *)comm_dev;
+  sockaddr_in sock;
+
+  memset(&sock,0,sizeof(sock));
+  CSocket::hard_open();
+  if(comm_dev==NULL)
+  {
+    /* handle exceptions */
+    throw CSocketException(_HERE_,"Invalid server information",this->comm_id);
+  }
+  else
+  {
+    // set non-blocking operation
+    if(fcntl(this->socket_fd,F_SETFL,O_NONBLOCK)<0)
+    {
+      /* handle exceptions */
+      std::cout << "error" << std::endl;
+    }
+    else
+    {
+      if(this->state==server_created)
+      {
+        sock.sin_family=AF_INET;
+        if(inet_aton(info->address.c_str(),&sock.sin_addr)==0)
+        {
+          /* handle exceptions */
+          throw CSocketException(_HERE_,"Impossible to convert IP address",this->comm_id);
+        } 
+        else
+        {
+          sock.sin_port=htons(info->port);
+          if(::bind(this->socket_fd,(struct sockaddr *)&sock,sizeof(sock))<0)
+          {
+            /* handle exceptions */
+            throw CSocketException(_HERE_,"Impossible to bind the socket to the desired IP address and port.",this->comm_id);
+          }
+          else
+          {
+            this->server_access.enter();
+            this->state=server_binded;
+            this->info.port=info->port;
+            this->info.address=info->address;
+            this->server_access.exit();
+          }
+        }
+      }
+      else
+      {
+        /* handle exceptions */
+        throw CSocketException(_HERE_,"The server is not created.",this->comm_id);
+      } 
+    }
+  }
+}
+void CSocketServer::hard_config(void *config)
+{
+  int num_listen=*((int *)config);
+
+  CSocket::hard_config();
+  if(config==NULL)
+  {
+    /* handle exceptions */
+    throw CSocketException(_HERE_,"Invalid configuration information",this->comm_id);
+  }
+  else
+  {
+    if(this->state==server_binded)
+    {
+      if(::listen(this->socket_fd,num_listen)<0)
+      {
+        /* handle exceptions */
+        throw CSocketException(_HERE_,"Impossible to listen to the desired IP address and port.",this->comm_id);
+      }
+      else
+      {
+        this->server_access.enter();
+        this->state=server_listening;
+        this->server_access.exit();
+      }
+    }
+    else
+    {
+      /* handle exceptions */
+      throw CSocketException(_HERE_,"The server is not binded",this->comm_id);
+    }
+  }
+}
+
+int CSocketServer::hard_wait_comm_event(void)
+{
+  std::string wait_event=this->comm_id+"_wait_event";
+  std::list<std::string> events;
+
+  this->event_server->create_event(wait_event);
+  events.push_back(wait_event);
+
+  this->event_server->wait_first(events);
+
+  return -1;
+}
+
+void CSocketServer::hard_close(void)
+{
+  CSocket::hard_close();
+  this->stop_server();
+  this->state=server_created;
+  this->info.port=-1;
+  this->info.address.clear();
+}
+
+void CSocketServer::set_max_clients(int max_clients)
+{
+  this->server_access.enter();
+  this->max_clients = max_clients;
+  this->server_access.exit();
+}
+
+int CSocketServer::get_max_clients(void)
+{
+  return this->max_clients;
+}
+
+int CSocketServer::get_num_current_clients(void)
+{
+  return this->current_clients;
+}
+
+void CSocketServer::start_server(void)
+{
+  if(this->state!=server_listening)
+  {
+    /* handle exceptions */
+    throw CSocketException(_HERE_,"The server is not properly configured to accept connections",this->comm_id);
+  }
+  else
+  { 
+    this->server_access.enter();
+    this->state=server_on;
+    this->server_access.exit();
+    this->thread_server->start_thread(this->connect_thread_id);
+    this->thread_server->start_thread(this->disconnect_thread_id);
+  }
+}
+
+void CSocketServer::stop_server(void)
+{
+  std::list<TClient_info_int *>::iterator it;
+
+  if (this->state==server_on)
+  {
+    this->event_server->set_event(this->finish_connect_thread_event_id);
+    this->thread_server->end_thread(this->connect_thread_id);
+    this->event_server->set_event(this->finish_disconnect_thread_event_id);
+    this->thread_server->end_thread(this->disconnect_thread_id);
+    this->server_access.enter();
+    this->state=server_listening;
+    this->server_access.exit();
+    // remove all clients from the list
+    // activate the disconnect event    
+    for(it=this->client_list.begin();it!=this->client_list.end();it++)
+      this->event_server->set_event((*it)->disconnect_event_id);
+  }
+  else
+  {
+    /* handle exceptions */ 
+    throw CSocketException(_HERE_,"Server is not running", this->comm_id);
+  }
+}
+
+void CSocketServer::broadcast(unsigned char *data, const int len)
+{
+  std::list<TClient_info_int *>::iterator it;
+  int written=0;
+
+  try{
+    this->server_access.enter();
+    for(it=this->client_list.begin();it!=this->client_list.end();it++)
+    {
+      if((written=(*it)->socket->write(data,len))!=len)
+      {
+        this->server_access.exit();
+        /* handle exceptions */
+        throw CSocketException(_HERE_,"Error while writing to the client",(*it)->client_id);
+      }
+    }
+    this->server_access.exit();
+  }catch(CException &e){
+    /* handle exceptions */
+    this->server_access.exit();
+    throw;
+  }
+}
+
+int CSocketServer::write_to(const std::string &sock_id, unsigned char *data, const int len)
+{
+  std::list<TClient_info_int *>::iterator it;
+
+  try{
+    this->server_access.enter();
+    for(it=this->client_list.begin();it!=this->client_list.end();it++)
+    {
+      if((*it)->client_id==sock_id)
+      {
+        this->server_access.exit();
+        return (*it)->socket->write(data,len);
+      }
+    }
+    this->server_access.exit();
+    throw CSocketException(_HERE_,"Unknown socket identifier",sock_id);
+  }catch(CException &e){
+    /* handle exceptions */
+    this->server_access.exit();
+    throw;
+  }
+}
+
+int CSocketServer::read_from(const std::string &sock_id, unsigned char *data, const int len)
+{
+  std::list<TClient_info_int *>::iterator it;
+
+  try{
+    this->server_access.enter();
+    for(it=this->client_list.begin();it!=this->client_list.end();it++)
+    {
+      if((*it)->client_id==sock_id)
+      {
+        this->server_access.exit();
+        return (*it)->socket->read(data,len);
+      }
+    }
+    this->server_access.exit();
+    throw CSocketException(_HERE_,"Unknown socket identifier",sock_id);
+  }catch(CException &e){
+    /* handle exceptions */
+    this->server_access.exit();
+    throw;
+  }
+}
+
+int CSocketServer::get_num_data_from(const std::string &sock_id)
+{
+  std::list<TClient_info_int *>::iterator it;
+
+  try{
+    this->server_access.enter();
+    for(it=this->client_list.begin();it!=this->client_list.end();it++)
+    {
+      if((*it)->client_id==sock_id)
+      {
+        this->server_access.exit();
+        return (*it)->socket->get_num_data();
+      }
+    }
+    this->server_access.exit();
+    throw CSocketException(_HERE_,"Unknown socket identifier",sock_id);
+  }catch(CException &e){
+    /* handle exceptions */
+    this->server_access.exit();
+    throw;
+  }
+}
+
+string CSocketServer::get_new_connection_event_id(void)
+{
+  return this->new_connection_event_id;
+}
+
+TClient_info *CSocketServer::get_new_client(void)
+{
+  TClient_info *client,*client_queue;
+
+  this->server_access.enter(); 
+  if(this->new_clients.size()>0)
+  {
+    client_queue=this->new_clients.front();
+    client=new TClient_info;
+    client->client_id=client_queue->client_id;
+    client->IP=client_queue->IP;
+    client->port=client_queue->port;
+    client->disconnect_event_id=client_queue->disconnect_event_id;
+    client->rx_event_id=client_queue->rx_event_id;
+    this->new_clients.pop();
+    this->server_access.exit(); 
+    return client;
+  }
+  else
+  { 
+    this->server_access.exit(); 
+    return NULL;
+  }
+}
+
+void CSocketServer::free_client(TClient_info *info)
+{
+  std::list<TClient_info_int *>:: iterator it;
+  sockaddr_in sock;
+
+  this->server_access.enter();
+  for(it=this->client_list.begin();it!=this->client_list.end();it++)
+  {
+    if((*it)->client_id==info->client_id)
+    {
+      (*it)->socket->close();
+      delete (*it)->socket;
+      this->current_clients--;
+      it=this->client_list.erase(it);
+      this->event_server->delete_event(info->disconnect_event_id);
+      delete info;
+      if(this->socket_fd==-1)
+      {
+        memset(&sock,0,sizeof(sock));
+        sock.sin_family=AF_INET;
+        inet_aton(this->info.address.c_str(),&sock.sin_addr);
+        sock.sin_port=htons(this->info.port);
+        this->CSocket::hard_open();
+        ::bind(this->socket_fd,(struct sockaddr *)&sock,sizeof(sock));
+        ::listen(this->socket_fd,1);
+        this->state=server_on;          
+      }
+      this->server_access.exit();
+      return;
+    }
+  }
+  this->server_access.exit();
+  throw CSocketException(_HERE_,"No such client",this->comm_id);
+}
+
+void *CSocketServer::connect_thread(void *param)
+{
+  CSocketServer *server=(CSocketServer *)param;
+  TClient_info_int *client_info_int;
+  std::stringstream sock_id;
+  TClient_info *client_info;
+  struct sockaddr_in client;
+  int new_fd,len=0;
+  bool end=false;
+
+  while(!end)
+  {
+    if(server->state==server_on)
+    {
+      server->server_access.enter();
+      try{
+        len=sizeof(client);
+        server->server_access.exit();
+        if((new_fd=::accept(server->socket_fd,(struct sockaddr *)&client,(socklen_t *)&len))<0)
+        {
+          if(errno==EWOULDBLOCK)
+          {
+            if(server->event_server->event_is_set(server->finish_connect_thread_event_id))
+              end=true;
+            else
+              usleep(100000);
+          }
+          else
+          {  
+            /* handle exceptions */
+            throw CSocketException(_HERE_,"Unexpected error while accepting new connections.",server->comm_id);
+          }
+        }
+        else
+        {
+          server->server_access.enter();
+          server->counter++;
+          sock_id.str("");
+          sock_id << "client_socket_" << server->counter;
+          // initialize the internal client info
+          client_info_int=new TClient_info_int; 
+          client_info_int->socket=CSocket::create_socket(sock_id.str(),new_fd);
+          client_info_int->socket->open(NULL);
+          client_info_int->socket->config(NULL);
+          client_info_int->client_id=sock_id.str();
+          client_info_int->disconnect_event_id=sock_id.str()+"_disconnect";
+          server->event_server->create_event(client_info_int->disconnect_event_id);
+          // initialize the external client info
+          client_info=new TClient_info;
+          client_info->client_id=sock_id.str();
+          client_info->IP=inet_ntoa(client.sin_addr);
+          client_info->port=(int)(ntohs(client.sin_port));
+          client_info->disconnect_event_id=client_info_int->disconnect_event_id;
+          client_info->rx_event_id=client_info_int->socket->get_rx_event_id();
+          // signal the connection of a new client
+          server->new_clients.push(client_info);
+          server->client_list.push_back(client_info_int);
+          server->current_clients++;
+          server->event_server->set_event(server->new_connection_event_id);
+          server->event_server->set_event(server->update_list_event_id);
+          if(server->current_clients==server->max_clients)
+          {
+            server->CSocket::hard_close(); 
+            server->state=server_created;
+          }
+          server->server_access.exit();
+        }
+      }catch(CException &e){
+        server->server_access.exit();
+        std::cout << e.what() << std::endl;
+      }
+    }
+    else
+    {
+      if(server->event_server->event_is_set(server->finish_connect_thread_event_id))
+        end=true;
+      sleep(1);
+    }
+  }
+  
+  pthread_exit(NULL);
+}
+
+void *CSocketServer::disconnect_thread(void *param)
+{
+  std::list<TClient_info_int *>:: iterator it_client;
+  CSocketServer *server=(CSocketServer *)param;
+  std::list<std::string>:: iterator it_event;
+  std::list<std::string> events;
+  int event_id=0,i=0;
+  bool end=false;
+
+  events.clear();
+  events.push_back(server->update_list_event_id);
+  events.push_back(server->finish_disconnect_thread_event_id);
+  while(!end)
+  {
+    try{
+      event_id=server->event_server->wait_first(events);
+      server->server_access.enter();
+      if(event_id==0)
+      {
+        events.clear();
+        events.push_back(server->update_list_event_id);
+        events.push_back(server->finish_disconnect_thread_event_id);
+        for(it_client=server->client_list.begin();it_client!=server->client_list.end();it_client++)
+          events.push_back((*it_client)->socket->get_connection_closed_event());
+      }
+      else
+      {
+        if(event_id==1)
+          end=true;
+        else
+        {
+          it_event=events.begin();
+          it_client=server->client_list.begin();
+          for(i=0;i<(event_id-2);i++) 
+          { 
+            it_event++;
+            it_client++;
+          }
+          it_event++;
+          if(!server->event_server->event_is_set((*it_client)->disconnect_event_id))
+            server->event_server->set_event((*it_client)->disconnect_event_id);
+          events.erase(it_event);
+        }
+      }
+      server->server_access.exit();
+    }catch(CException &e){
+      std::cout << e.what() << std::endl;
+    }
+  }
+
+  pthread_exit(NULL);
+}
+
+CSocketServer::~CSocketServer()
+{
+  this->event_server->delete_event(this->new_connection_event_id);
+  this->new_connection_event_id="";
+  this->event_server->delete_event(this->update_list_event_id);
+  this->update_list_event_id="";
+  this->event_server->delete_event(this->finish_connect_thread_event_id);
+  this->finish_connect_thread_event_id="";
+  this->event_server->delete_event(this->finish_disconnect_thread_event_id);
+  this->finish_disconnect_thread_event_id="";
+  this->thread_server->delete_thread(this->connect_thread_id);
+  this->connect_thread_id="";
+  this->thread_server->delete_thread(this->disconnect_thread_id);
+  this->disconnect_thread_id="";
+  this->info.port=-1;
+  this->info.address.clear();
+}
diff --git a/src/sockets/socketserver.h b/src/sockets/socketserver.h
new file mode 100644
index 0000000..228b23b
--- /dev/null
+++ b/src/sockets/socketserver.h
@@ -0,0 +1,677 @@
+#ifndef _SOCKETSERVER_H
+#define _SOCKETSERVER_H
+
+#include <queue>
+#include "socket.h"
+
+/**
+ * \brief possible server states
+ *
+ * This ennumeration type represents all the possible states of the server, which
+ * are used internally to control its flow. The possible states are:
+ *
+ * * server_created: It is the default state when it is created for the first time. 
+ * * server_binded: after successfully calling the open() function, the server is
+ *                  attached to an IP address and port.
+ * * server_listening: after successfully calling the config() function the server
+ *                     is ready to listen to incomming connections. If the server
+ *                     is closed, it returns to the server_created state.
+ * * server_on: It is the main state when the server is started using the start()
+ *              function. In this state, the server is capable of detecting new 
+ *              connections and disconnections, as well as send and receive data 
+ *              to and from any client. If the server is stopped it returns to the
+ *              server_listening state.
+ *
+ * The next figure shows the server states and the possible transitions between them:
+ *
+ * \image html server_states.png
+ *
+ */
+typedef enum {server_created, server_binded, server_listening, server_on} server_state;
+
+/**
+ * \brief client information
+ *
+ * This structure holds all the information required by the server side application 
+ * to handle a connection with a new client. This structure is initialized when
+ * a new connection is stablished, and the get_new_client() function should be used 
+ * to retrieve it. Once the connection is closed, the free_client() function should
+ * be used to properly free all the allocated resources.
+ */
+typedef struct 
+{
+  /**
+   * \brief client identifier 
+   *
+   * This ideintifier is automatically assigned by the server when the connection is
+   * stablished. This identifier is unique for each connection and it is used to 
+   * identify the client to which send or receive information. 
+   */ 
+  std::string client_id;
+  /**
+   * \brief client IP address
+   *
+   * This string holds the IP address of the client in dot format (192.168.0.1). This 
+   * value can be used by the server side application to identify the different 
+   * clients connected. This field is purely informative and it is not used for
+   * any operation.
+   */ 
+  std::string IP;
+  /**
+   * \brief client port
+   *  
+   * this integer is the port assigned to the client for the connection. This port
+   * is automatically assigned by the accept function and can not be modified 
+   * afterwards. This filed is purely informative and it is not used by any operation.
+   */ 
+  int port;
+  /**
+   * \brief identifier of the disconnect event.
+   *
+   * This events notifies the server side application that the connection to the
+   * client has been closed. This event is not the same as the connection closed
+   * event provided by the CSocket class. This event is used internally by the
+   * server, which activates this one when the disconnection is detected.
+   */ 
+  std::string disconnect_event_id;
+  /**
+   * \brief identifier of the reception event
+   *  
+   * This event notifies the reception of new data for the corresponding client.
+   * This event is the one provided by the CSocket base class and it is provided
+   * here because there is no direct access to the CSocket object from outside
+   * the class.
+   */ 
+  std::string rx_event_id;
+}TClient_info;
+
+/**
+ * \brief Internal client information
+ *
+ * This structure hold information of the client necessary for the server to
+ * manage the connection. This structure is only used internally by the server
+ * and can not be accessed from outside the class.
+ */
+typedef struct
+{
+  /**
+   * \brief client identifier 
+   *
+   * This ideintifier is automatically assigned by the server when the connection is
+   * stablished. This identifier is unique for each connection and it is used to 
+   * identify the client to which send or receive information. 
+   */ 
+  std::string client_id;
+  /**
+   * \brief identifier of the disconnect event.
+   *
+   * This events notifies the server side application that the connection to the
+   * client has been closed. This event is not the same as the connection closed
+   * event provided by the CSocket class. This event is used internally by the
+   * server, which activates this one when the disconnection is detected.
+   */ 
+  std::string disconnect_event_id;
+  /**
+   * \brief communication soket
+   *
+   * This field points to the CSocket object used to communicate with the client.
+   * This object can only be accessed internally by the class, and the server
+   * side application must use the unique identifier assigned to it in order to
+   * access it throw the public interface of the server.
+   *
+   * This object is created and initialized automatically when the connection is
+   * stablished, and it is only closed and freed when the free_client() function
+   * is called.
+   */ 
+  CSocket *socket;
+}TClient_info_int;
+
+/**
+ * \brief Server side socket
+ *
+ * This class implemnt the server side of a TCP/IP socket communication. It 
+ * inherits from the CSocket class for the basic socket communication issues and
+ * adds the necessary resources to provide connections to several clients 
+ * simultaneously.
+ *
+ * After creation, it is necessary to call the open() function with the IP address
+ * and the desired listining port of the server socket. Then, the config() function
+ * is provided with the number of connections that can be buffered waiting for
+ * a connection. 
+ *
+ * At this point the server is properly configured, but it will not accept new 
+ * connections until it is started by calling the start() function. After these 
+ * steps, the server socket is ready to accept connections and send and receive data 
+ * to and from them. The server can be stoped at any time calling the stop() function
+ * and restarted. When the server is stopped, all connections are lost.
+ *
+ * This class provides an event (which can be retrieved with the get_new_connection_event()
+ * function) which is activated each time a new client is connected to the server.
+ * At this point, a server side application can get all the new client information
+ * using the get_new_client() function.
+ *
+ * This information includes the unique identifier of the socket used to send
+ * and receive information because the CSocket object itself is not accessible
+ * from outisde the class. It also includes the identifier of an event which is
+ * activated when the connection is closed by the client side (disconnect_event_id).
+ *
+ * When a connection is closed, most of the associated resources in the server
+ * side are freed, but it is imperative to call the free_client() function in
+ * order to properly free all the resources and avoid memory leaks. This function
+ * should only be called after the connection is closed. calling it before, will
+ * terminate the connection from the server side.
+ *
+ * When the maximum number of connections are reached, the server does not accept
+ * any more until one of the existing connections is closed.
+ *
+ */
+class CSocketServer : public CSocket
+{
+  private:
+    /**
+     * \brief current state of the server
+     *
+     * This attribute keeps the current state of the server. By default it is 
+     * initialized to server_created, and its value automarically changes when
+     * the server is opend, configured and started. See the server_state 
+     * ennumeration documentation for more information about the possible states
+     * of the server and the state transitions.
+     */
+    server_state state;
+    /**
+     * \brief maximum number of simultaneous clients
+     *
+     * This integer has the maximum number of connections allowed for the server.
+     * By default its value is set to 1, but it can be changed at any time using
+     * the set_max_clients() function. The maximum number of clients can be 
+     * retrieved at nay time with the get_max_clients() function.
+     */
+    int max_clients;
+    /**
+     * \brief number of clients currently connected
+     *
+     * This integer has the current number of clients connected to the server. By
+     * default it is initialized to 0, and its value is automatically updated when
+     * new connections are created or existing connections are closed. The current
+     * number of connections can be retrieved with the set_num_current_connections()
+     * function.
+     */
+    int current_clients;
+    /**
+     * \brief connection counter
+     *
+     * This static integer is used to assign a unique identifier to each new 
+     * connection. This attribute is shared by all servers and it is incremented
+     * each time a new connection is set up in any of them, but it is never 
+     * decremented. This value is only used internally, and can not be modified 
+     * or accessed from outisde the class.
+     */
+    static int counter;
+    /**
+     * \brief list of clients connected
+     *
+     * This list holds the internal information required by the server to manage 
+     * each of the connections. By default the list is empty, and it is automatically
+     * updated each time a new connections is created or an existing connection
+     * is closed. This list is only used internally and can not be modified or 
+     * accessed from outside class.
+     *
+     * See the documentation on the TClient_info_int data type for more
+     * information about the elements fo this list.
+     */
+    std::list<TClient_info_int *> client_list;
+    /**
+     * \brief mutex object to access the server
+     *
+     * This object is used to avoid simultaneous access to the shared resources
+     * of the server (list of clients) from multiple threads (connection and 
+     * disconnection threads and the main thread). This object is initialized at 
+     * construction time and used internally in most of the functions.
+     */
+    CMutex server_access;
+    /**
+     * \brief identifier of the connection thread
+     *
+     * This string holds the unique identifier of the connection thread. This 
+     * identifier is created at contruction time attaching "_connect_thread" to 
+     * the identifier assigned to the server. This identifier can not be modified 
+     * or accessed outside the class.
+     */
+    std::string connect_thread_id;
+    /**
+     * \brief identifier of the disconnection thread
+     *
+     * This string holds the unique identifier of the disconnection thread. This 
+     * identifier is created at contruction time attaching "_disconnect_thread" to 
+     * the identifier assigned to the server. This identifier can not be modified 
+     * or accessed outside the class.
+     */
+    std::string disconnect_thread_id;
+    /**
+     * \brief identifier of the new connection event
+     *
+     * This string holds the identifier of the new connection event. This identifier
+     * is created at construction time attaching "new_connection_event" to the
+     * identifier assigned to the server. This identifier can not be modified outside 
+     * the class, but it can be retrieved with the get_new_connection_event_id()
+     * function to wait for new connections outside the class.
+     */
+    std::string new_connection_event_id;
+    /**
+     * \brief identifier of the update list event
+     *
+     * This string holds the identifier of the update list event. This identifier
+     * is created at construction time attaching "update_list_event" to the
+     * identifier assigned to the server. This event is only used inside the class
+     * and can not be accessed or modified outside it.
+     */
+    std::string update_list_event_id;
+    /**
+     * \brief identifier of the event to terminate the connect thread
+     *
+     * This event is used to signal the thread that is waiting for new connections
+     * to terminate its operation. This thread periodically checks the state of this
+     * event.
+     *
+     * This event is created when the server is created, but it is only activated 
+     * when the stop_server() function is called or the object is destroyed. This
+     * event is for internal use only, and can not be accessed outside the class.
+     */
+    std::string finish_connect_thread_event_id;
+    /**
+     * \brief identifier of the event to terminate the disconnect thread
+     *
+     * This event is used to signal the thread that is waiting for disconnections
+     * to terminate its operation. This thread periodically checks the state of this
+     * event.
+     *
+     * This event is created when the server is created, but it is only activated 
+     * when the stop_server() function is called or the object is destroyed. This
+     * event is for internal use only, and can not be accessed outside the class.
+     */
+    std::string finish_disconnect_thread_event_id;
+    /** 
+     * \brief queue of clients waiting to be retrieved
+     *
+     * This queue temporary holds the information of the new connections stablished
+     * to the server. This queue is empty by default and it is filled each time a new
+     * connection is created. The queue is emptied by calling the get_new_client()
+     * function when the new_connection_event_id event is activated, which returns 
+     * the necessary information to handle the connection to the server side 
+     * application. This queue can not be modified or accessed outside the class.
+     *
+     * This event can be activated several times if multiple connections are created
+     * without being acknowledge by the server side application.
+     */
+    std::queue<TClient_info *> new_clients;
+  protected:
+    /**
+     * \brief server connection IP address and port
+     *
+     * This strcture has the IP address and listening port of the server. By default
+     * the IP address uis empty and the port is set to -1. When the open() function is
+     * called successfully, this values are updated to the values provided to the
+     * function. 
+     *
+     * These values can not be directly modified nor accessed outside the class.
+     */
+    TSocket_info info;
+    /**
+     * \brief connection thread function
+     *
+     * This is the main function for the connection thread. This thread is waiting 
+     * for new connections continuously. When a new connection is created, it first
+     * created a new CSocket object associated to the new socket file descriptor
+     * to handle all the communication issues and generates both the internal 
+     * (TClient_info_int) and external (TClient_info) structures with all the 
+     * necessary information.
+     *
+     * The CSocket object can only be accessed from inside the class, and the
+     * server side application must use the client identifier provided by the
+     * get_new_client() function to access it. 
+     *
+     * After that, the new_connection event is activated to signal the server side
+     * application that a new connection is ready. If several connections are 
+     * created without being acknowledge by the server side application, this event
+     * remains active until all the new client information is retrieved.
+     *
+     * This thread does not throw any exception.
+     *
+     * \param param a pointer to the CSocketServer object to which the thread is
+     *              associated. This parameter is used to access the internal
+     *              attriobutes and functions of the class since the thread itself
+     *              is defined as static.
+     */
+    static void *connect_thread(void *param);
+    /**
+     * \brief disconnection thread function
+     *
+     * This is the main function of the disconnection thread. This thread waits 
+     * for the connection closed event of each of the active connections. When
+     * one or more of these events are activated, it frees most of the internal 
+     * resources associated with the connection and activates the corresponding
+     * disconnect event to signal the server side application that the
+     * connection is no longer available.
+     *
+     * It is the server side application the one responsible of freeing the rest
+     * of the resources associated with the closed connection by calling the
+     * free_client() function when the disconnect event is activated.
+     *
+     * If there are no active connections, this thread is inactive. As new 
+     * connections are created, this thread periodically updates the wait event 
+     * list to match the current active connections.
+     *
+     * This thread does not throw any exception.
+     *
+     * \param param a pointer to the CSocketServer object to which the thread is
+     *              associated. This parameter is used to access the internal
+     *              attriobutes and functions of the class since the thread itself
+     *              is defined as static.
+     */
+    static void *disconnect_thread(void *param);
+    /**
+     * \brief function to bind the desired IP address and port to the socket
+     *
+     * This function is automatically called when the base class open() function
+     * is called. This function binds the server socket to the desired IP
+     * address and port provided to the function. The IP address must coincide
+     * with the IP address assigned to the computer where the server is being
+     * executed, and the port can not be used by nay other application.
+     *
+     * If successful, the internal state of the server is changed to server_binded.
+     * After calling this function, it is necesary to call the config() function
+     * to properly configure the server.
+     *
+     * \param comm_dev a pointer to a valid TSocket_info strcture with the server
+     *                 IP address and desired listening port. See the documentation
+     *                 on the TSocket_info structure for more information on the
+     *                 IP address and port format.
+     */
+    virtual void hard_open(void *comm_dev=NULL);
+    /**
+     * \brief function to configure the length of the connection buffer
+     *
+     * This function is automatically called when the base class config() function
+     * is called. This function sets the maximum number of waiting connections 
+     * that are allowed. 
+     *
+     * If successfull, the internal state of the server is changed to server_listening.
+     * After calling this function, the server is properly configured, but it is still
+     * not ready to accept new connections or send and receive data. To do that, it
+     * is necessary to call the start() function.
+     *
+     * \param config a pointer to an integer value with the number of waiting
+     *               connections allowed. This value must be a positive integer.
+     */
+    virtual void hard_config(void *config=NULL);
+    /**
+     * \brief function to wait for communication events
+     *
+     * This function is only provided for compatibility since the server socket never 
+     * receives data from a client (a new socket is created for that purpose). However, 
+     * changes in the state of the socket may generate unexpected errors that the 
+     * CSocket hard_wait_comm_event() function can not handle.
+     *
+     * Therefore, this function overrides the base class function and block the thread
+     * indefinetely. When the server is destroyed, the thread is killed and this 
+     * function is aborted.
+     *
+     * \return this function will block and never return any value. 
+     */
+    virtual int hard_wait_comm_event(void);
+    /**
+     * \brief function to close the server
+     *
+     * This function will first stop the server and also close any connection with a 
+     * client which is still active, freeing all the associated resources. If 
+     * successfull this function changes the state of the server to server_created.
+     *
+     * If this function is called, in order to set up the server again, it will be
+     * necessary to call the open() and config() functions to reconfigure it and also
+     * the start() function to accept and handle new connections.
+     */
+    virtual void hard_close(void);
+  public:
+    /**
+     * \brief constructor
+     *
+     * This constructor created the connect and disconnect threads and attach them
+     * to the corresponding functions, but does not start any of them. It also
+     * creates the new connection event. The socket identifier is used to initialize
+     * the identifier for both threads and events, and each server must have a 
+     * different one.
+     *
+     * In this function all the internal attributes are initialized by default.
+     *
+     * \param sock_id a null terminated string with the identifier of the server.
+     *                This string must not have any white spaces and each server
+     *                must have a different one, since it is used to generate the
+     *                unique identifier for both threads and events.
+     */
+    CSocketServer(const std::string &sock_id);
+    /**
+     * \brief function to set the maximum number of simultaneous clients
+     *
+     * This function sets the maximum number of clients connected any given time.
+     * This function can be called any time, but if the new maximum value is 
+     * smaller than the current number of connections, this function will throw
+     * a CSocket exception instead of closing the exceeding connections.
+     *
+     * If the new value is bigger than the previous one, there is no problem.
+     * However, it is better to set this value before calling the start()
+     * function and, if it is necessary to change the maximum number of clients
+     * connected, first stop the server and then change it.
+     *
+     * \param max_clients a positive integer with the maximum number of clients
+     *                    connected any given time. 
+     */
+    void set_max_clients(int max_clients);
+    /**
+     * \brief function to get the maximum number of simultaneous clients
+     *   
+     * This function return the maximum number of clients that can be connected 
+     * to the server at any given time. 
+     *
+     * \return a positive integer with the maximum number of clients connected
+     *         to the server any given time.
+     */
+    int get_max_clients(void);
+    /**
+     * \brief function to get the number of clients currently connected
+     *
+     * This returns the number of clients currently connected to the server. This
+     * number is always between 0 and the maximum number of clients allowed. 
+     *
+     * \return a non-negative integer with the number of clients currently
+     *         connected to the server.
+     */
+    int get_num_current_clients(void);
+    /**
+     * \brief function to start the server
+     *
+     * This function starts the internal threads which allow the server to detect 
+     * new connections. Before calling this function, the server will not be able 
+     * to accept any new connections. If successfull this function changes the state
+     * of the server to server_on.
+     *
+     * After calling this function, the server side application should wait for the
+     * activation of the new connection event. Once activated, the new client
+     * information can be retrieved by calling the get_new_client() function. 
+     *
+     * At this point, the receive event and the disconnet event should be monitored to
+     * detect the reception of new data or the disconnection of the client, whatever
+     * happens first. If new data is received, the get_num_data_from() and read_from()
+     * functions can be used to get it. If the connection has been closed, the 
+     * free_client() function should be called to free all the associated resources.
+     *
+     * If successfull, this function changes the state of the server to server_on.
+     * This function throws a CSocketException in case of any error.
+     */
+    void start_server(void);
+    /**
+     * \brief function to stop the server
+     *
+     * This function stops the normal operation of the server. It closes any active
+     * connection that may still exists and frees all the associated resources. In 
+     * this case, however, the server does not loose its configuration, so in order
+     * to start accepting connections again, it is only necessary to call the 
+     * start() function.
+     *
+     * If successfull, this function changes the state of the server to 
+     * server_listening. This function throws a CSocketException in case of any 
+     * error;
+     */
+    void stop_server(void);
+    /**
+     * \brief function to send some data to all clients
+     *
+     * This function is used to send any data to all the active connections on the
+     * server. In this case no client identifier is provided because the data is
+     * sequentially sent to all available clients.
+     *
+     * This function throws a CSocketException in case of any error.
+     *
+     * \param data a pointer to an unsigned char vector with the information to
+     *             send. The memory for this parameter must be allocated before
+     *             calling this function, and its length must coincide with the
+     *             value of the second parameter.
+     *
+     * \param len a positive integer with the length of the data to be sent. This
+     *            value must coincide with the actual length of the vector passed
+     *            as first argument.
+     */
+    void broadcast(unsigned char *data, const int len);
+    /**
+     * \brief function to send data to a single client
+     *
+     * This function is used to send data to a single client. It uses the client
+     * identifier returned by the get_new_client() function to identify the
+     * desired client connection. If the specified client does not exist, this
+     * function throws a CSocketException.
+     *
+     * \param sock_id the identifier of the desired client. This value must be
+     *                one of the identifier returned by the get_new_client()
+     *                function which is still active.
+     *
+     * \param data a pointer to an unsigned char vector with the information to
+     *             send. The memory for this parameter must be allocated before
+     *             calling this function, and its length must coincide with the
+     *             value of the third parameter.
+     *
+     * \param len a positive integer with the length of the data to be sent. This
+     *            value must coincide with the actual length of the vector passed
+     *            as second argument.
+     *
+     * \return the number of bytes actually written to the client. In a standard 
+     *         case, this value should be equal to the value of the third parameter,
+     *         otherwise, and error has ocurred.
+     */
+    int write_to(const std::string &sock_id, unsigned char *data, const int len);
+    /**
+     * \brief function to get data from a single client
+     *
+     * This function is used to get data from a single client. It uses the client
+     * identifier returned by the get_new_client() function to identify the 
+     * desired client connection. If the specified client does not exist, this
+     * function throws a CSocketException.
+     *
+     * \param sock_id the identifier of the desired client. This value must be
+     *                one of the identifier returned by the get_new_client()
+     *                function which is still active.
+     *
+     * \param data a pointer to an unsigned char vector where the information 
+     *             received is stored. The memory for this parameter must be 
+     *             allocated before calling this function, and its length must 
+     *             coincide with the value of the third parameter.
+     *
+     * \param len a positive integer with the length of the data to be read. This
+     *            value must coincide with the actual length of the vector passed
+     *            as second argument. The get_num_data_from() function can be 
+     *            used to know how many data is available at any time.
+     *
+     * \return the number of bytes actually read from the client. In a standard 
+     *         case, this value should be equal to the value of the third parameter,
+     *         otherwise, and error has ocurred.
+     */
+    int read_from(const std::string &sock_id, unsigned char *data, const int len);
+    /**
+     * \brief function to get the number of available data from a single client
+     *
+     * This function is used to get the ammount of data available from a single 
+     * client. It uses the client identifier returned by the get_new_client() 
+     * function to identify the desired client connection. If the specified 
+     * client does not exist, this function throws a CSocketException.
+     *
+     * \param sock_id the identifier of the desired client. This value must be
+     *                one of the identifier returned by the get_new_client()
+     *                function which is still active.
+     *
+     * \return the number of bytes currently available at the desired client. This
+     *         value can be used as the third parameter to the read_from()
+     *         function.
+     */
+    int get_num_data_from(const std::string &sock_id);
+    /**
+     * \brief function to get the identifier of the new connection event
+     *
+     * This function return the identifier of the new connection event. This
+     * event can be used to wait for new connections once the server has been
+     * started with the start() function. There exist a single event for each 
+     * server that is activated as many times as new clients are pending to be
+     * retrieved.
+     *
+     * After this event is active, the server side application should call the
+     * get_new_client() function in order ro get all the necessary information
+     * to handle the new connection. 
+     *
+     * \return a string with a copy of the new connection event identifier. 
+     */
+    std::string get_new_connection_event_id(void);
+    /**
+     * \brief function to get the information about the new connection
+     *
+     * This function is used to get all the important information about a new
+     * connection. See the documentation on the TClient_info structure for more
+     * information about the parameters returned by this function. 
+     *
+     * This function should be called as long as the new connection event is 
+     * active, which means that there is a new client connected and waiting.
+     *
+     * \return a pointer to an structure with information about the new 
+     *         connection. The memory necessary for this structure is allocated
+     *         in this function, and it is necessary to call the free_client()
+     *         function to properly free it and all its associated resources.
+     */
+    TClient_info *get_new_client(void);
+    /**
+     * \brief function to free the resources associated with a client
+     *
+     * This function is used to properly free all the resources associated with a
+     * connection once is has been closed. This function should only be called 
+     * after the disconnect event for the corresponding connection has been
+     * activated. Otherwise, the connection will be closed and the client will be
+     * disconnected.
+     *
+     * This function actually closes the socket accociated to the connection and 
+     * destroys it, and also destroys all the events associated with it. Finally, 
+     * it also, frees the information structure passed as an argument so the
+     * server side of the application does not have to do that.
+     *
+     * \param info a pointer to a valid TClient_info structure which corresponds 
+     *             to an active connection. This structure should be the one
+     *             returned by the get_new_client() function.
+     */
+    void free_client(TClient_info *info);
+    /**
+     * \brief destructor
+     *
+     * This destructor first stops the server, if it is running, and closes all 
+     * the active connections with clients. This destructor does not free all the
+     * resources associated with each connection, so its better to first close all
+     * the connections using the free_client() function and then destroy the 
+     * server object.
+     */
+    virtual ~CSocketServer();
+};
+
+#endif
diff --git a/src/usb_ftdi/ftdiexceptions.cpp b/src/usb_ftdi/ftdiexceptions.cpp
new file mode 100644
index 0000000..e12766c
--- /dev/null
+++ b/src/usb_ftdi/ftdiexceptions.cpp
@@ -0,0 +1,37 @@
+#include "ftdiexceptions.h"
+#include <string.h>
+#include <stdio.h>
+
+const std::string ftdi_exception_msg="[CFTDI class] - ";
+const std::string ftdiserver_exception_msg="[CFTDIServer class] - ";
+
+const std::string error_messages[]={"ok.\n",
+                                    "Invalid handle.\n",
+                                    "Device not found.\n", 
+                                    "Device not opened.\n",
+                                    "Input/output error.\n",
+                                    "Insufficient resources.\n",
+                                    "Invalid parameter.\n",
+                                    "Invalid baudrate.\n",
+                                    "Device not opened for erase.\n",
+                                    "Device not opened for write.\n",
+                                    "Failed to write device.\n",
+                                    "EEPROM read failed.\n",
+                                    "EEPROM write failed.\n",
+                                    "EEPROM erase failed.\n",
+                                    "EEPROM not present.\n",
+                                    "EEPROM not programmes.\n"
+                                    "Invalid arguments.\n",
+                                    "Not supported.\n",
+                                    "Unknown error.\n"};
+
+CFTDIException::CFTDIException(const std::string& where,const std::string& error_msg,const std::string& comm_id) : CCommException(where,ftdi_exception_msg,error_msg)
+{
+  this->error_msg+=" - ";
+  this->error_msg+=comm_id;
+}
+
+CFTDIServerException::CFTDIServerException(const std::string& where,const std::string& error_msg) : CException(where,ftdi_exception_msg)
+{
+  this->error_msg+=error_msg;
+}
diff --git a/src/usb_ftdi/ftdiexceptions.h b/src/usb_ftdi/ftdiexceptions.h
new file mode 100644
index 0000000..53387f0
--- /dev/null
+++ b/src/usb_ftdi/ftdiexceptions.h
@@ -0,0 +1,116 @@
+#ifndef FTDI_EXCEPTIONS
+#define FTDI_EXCEPTIONS
+
+#include "commexceptions.h"
+#include "ftd2xx.h"
+#include <string>
+
+/**
+ * \brief vector with all the error messages
+ *
+ * This constant vector has the strings corresponding to all possible errors
+ * of any FTDI device. The value of the error can be used to index the table
+ * and get the corresponding string.
+ */
+extern const std::string error_messages[];
+
+/**
+ * \brief FTDI exception class
+ *
+ * This class implements the exceptions for the FTDI communication class. 
+ * In addition to the basic error message provided by the base class CException, 
+ * this exception class provides also the unique identifier of the communication
+ * device that generated the exception.
+ *
+ * Also, similarly to other exception classes, it appends a class identifer
+ * string ("[CFTDI class] - ") to the error message in order to identify 
+ * the class that generated the exception.
+ *
+ * The base class can be used to catch any exception thrown by the application
+ * or also, this class can be used in order to catch only exceptions generated 
+ * by CFTDI objects.
+ *
+ */
+class CFTDIException : public CCommException
+{
+  public:
+    /**
+     * \brief Constructor
+     *
+     * The constructor calls the base class constructor to add the general
+     * exception identifier and then adds the class identifier string 
+     * "[CFTDI class]" and the supplied error message. 
+     *
+     * It also appends the unique identifier of the communication device 
+     * that generated the exception. So, the total exception message will 
+     * look like this:
+     *
+     * \verbatim
+     * [Exception caught] - <where>
+     * Error: [CComm class] - [CFTDI class] - <error message> - <comm id>
+     * \endverbatim
+     *
+     * \param where a null terminated string with the information about the name
+     *              of the function, the source code filename and the line where
+     *              the exception was generated. This string must be generated 
+     *              by the _HERE_ macro.
+     *
+     * \param error_msg a null terminated string that contains the error message.
+     *                  This string may have any valid character and there is no
+     *                  limit on its length.
+     *
+     * \param comm_id a null terminated string that contains the communication
+     *                device unique identifier. This string must be the one used to 
+     *                create the object.
+     *
+     *
+     */  
+    CFTDIException(const std::string& where,const std::string& error_msg,const std::string& comm_id);
+};
+
+/**
+ * \brief FTDIServer exception class
+ *
+ * This class implements the exceptions for the FTDI Server class. 
+ * Similarly to other exception classes, it appends a class identifer
+ * string ("[CFTDIServer class] - ") to the error message in order to identify 
+ * the class that generated the exception.
+ *
+ * The base class can be used to catch any exception thrown by the application
+ * or also, this class can be used in order to catch only exceptions generated 
+ * by CFTDIServer objects.
+ *
+ */
+class CFTDIServerException : public CException
+{
+  public:
+    /**
+     * \brief Constructor
+     *
+     * The constructor calls the base class constructor to add the general
+     * exception identifier and then adds the class identifier string 
+     * "[CFTDIServer class]" and the supplied error message. 
+     *
+     * It also appends the unique identifier of the communication device 
+     * that generated the exception. So, the total exception message will 
+     * look like this:
+     *
+     * \verbatim
+     * [Exception caught] - <where>
+     * [CComm class] - [CFTDIServer class] - <error message>
+     * \endverbatim
+     *
+     * \param where a null terminated string with the information about the name
+     *              of the function, the source code filename and the line where
+     *              the exception was generated. This string must be generated 
+     *              by the _HERE_ macro.
+     *
+     * \param error_msg a null terminated string that contains the error message.
+     *                  This string may have any valid character and there is no
+     *                  limit on its length.
+     *
+     */  
+    CFTDIServerException(const std::string& where,const std::string& error_msg);
+};
+
+#endif
diff --git a/src/usb_ftdi/ftdimodule.cpp b/src/usb_ftdi/ftdimodule.cpp
new file mode 100644
index 0000000..7520d5d
--- /dev/null
+++ b/src/usb_ftdi/ftdimodule.cpp
@@ -0,0 +1,210 @@
+
+#include "ftdimodule.h"
+#include "ftdiexceptions.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <string>
+#include <iostream>
+
+CFTDI::CFTDI(const std::string& comm_id) : CComm(comm_id)
+{
+  this->ft_handle = NULL;		
+}
+	
+void CFTDI::hard_open(void* comm_dev)
+{
+  FT_STATUS ft_status;
+  std::string *serial_desc=(std::string *)comm_dev;
+
+  ft_status=FT_OpenEx((void *)serial_desc->c_str(),FT_OPEN_BY_SERIAL_NUMBER,&(this->ft_handle));
+  ft_status=FT_OpenEx((void *)serial_desc->c_str(),FT_OPEN_BY_DESCRIPTION,&(this->ft_handle));
+  if(this->ft_handle==NULL)
+  { 
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,"Impossible to open the device.\n",this->comm_id); 
+  }
+  if((ft_status=FT_ResetDevice(this->ft_handle))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id); 
+  }
+  if((ft_status=FT_Purge(this->ft_handle,FT_PURGE_RX|FT_PURGE_TX))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id); 
+  }
+  if((ft_status=FT_ResetDevice(this->ft_handle))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id); 
+  }
+  pthread_cond_init(&this->event_handle.eCondVar, NULL); 
+  pthread_mutex_init(&this->event_handle.eMutex, NULL); 		
+  pthread_mutex_lock(&this->event_handle.eMutex);
+
+  //Set condition event here, before thread is started
+  if((ft_status = FT_SetEventNotification(this->ft_handle,FT_EVENT_RXCHAR,(PVOID)&this->event_handle))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+  }
+}
+
+void CFTDI::hard_config(void *config)
+{
+  FT_STATUS ft_status;
+  TFTDIconfig *ftdi_config = (TFTDIconfig*) config;		 
+
+  if(config==NULL)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,"Invalid configuration structure",this->comm_id);
+  }
+  //1. Set Baud Rate
+  if(ftdi_config->baud_rate!=-1)
+  {
+    if((ft_status=FT_SetBaudRate(this->ft_handle,ftdi_config->baud_rate))!=FT_OK)
+    {
+      /* handle exceptions */
+      throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+    }
+  }
+  //2. Set the data characteristics
+  if(ftdi_config->word_length!=-1 && ftdi_config->stop_bits!=-1 && ftdi_config->parity!=-1)
+  {
+    if((ft_status=FT_SetDataCharacteristics(this->ft_handle,ftdi_config->word_length,ftdi_config->stop_bits,ftdi_config->parity))!=FT_OK)
+    {
+      /* handle exceptions */
+      throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+    }
+  }
+  //3. set read and write timeouts
+  if((ft_status=FT_SetTimeouts(this->ft_handle,ftdi_config->read_timeout,ftdi_config->write_timeout))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+  } 
+  //4. Set latency timer
+  if((ft_status=FT_SetLatencyTimer(this->ft_handle,ftdi_config->latency_timer))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+  }
+  if((ft_status=FT_SetFlowControl(this->ft_handle,FT_FLOW_NONE,0x00,0x00))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+  }
+}
+
+void CFTDI::hard_close(void)
+{
+  FT_STATUS ft_status;
+
+  if(this->ft_handle!=NULL)
+  {
+    if(pthread_mutex_trylock(&this->event_handle.eMutex)==EBUSY)
+      pthread_mutex_unlock(&this->event_handle.eMutex);
+    if((ft_status = FT_Close(this->ft_handle))!=FT_OK)
+    {
+      /* handle exceptions */
+      throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+    }
+    this->ft_handle = NULL; // assing no opened value
+  }
+}
+
+int CFTDI::hard_read(unsigned char *data, int len)
+{
+  FT_STATUS ft_status;
+  DWORD bytes_received;
+
+  if((ft_status = FT_Read(this->ft_handle,data,len,&bytes_received))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+  }
+
+  return bytes_received;
+}
+
+int CFTDI::hard_write(unsigned char *data, int len)
+{
+  FT_STATUS ft_status;
+  DWORD bytes_written;
+
+  if((ft_status = FT_Write(this->ft_handle, data,len,&bytes_written))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+  }	
+  
+  return bytes_written;
+}
+
+int CFTDI::hard_get_num_data(void)
+{
+  FT_STATUS ft_status;
+  DWORD bytes_rx;	
+
+  if((ft_status=FT_GetQueueStatus(this->ft_handle,&bytes_rx))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);	
+  }
+ 
+  return bytes_rx;
+}
+
+int CFTDI::hard_wait_comm_event(void)
+{
+  FT_STATUS ft_status;
+  DWORD rx_bytes=0,tx_bytes=0,event_id=0;
+
+  while(rx_bytes < 1)
+  {
+    pthread_cond_wait(&event_handle.eCondVar, &event_handle.eMutex);		
+    if((ft_status = FT_GetStatus(this->ft_handle,&rx_bytes,&tx_bytes,&event_id))!=FT_OK)
+    {
+      /* handle exceptions */
+      throw CFTDIException(_HERE_,error_messages[ft_status],this->comm_id);
+    }
+  }
+  if(rx_bytes < 1) 			
+    return 2; // error	
+  else 		
+  {
+    return 1; // ready to read data	
+  }
+}
+
+std::ostream& operator<< (std::ostream& out,CFTDI& ftdi)
+{
+  FT_STATUS ft_status;
+  DWORD type;
+  DWORD id;
+  char serial_number[16];
+  char description[64];
+
+  out << "****************** FTDI Devices Info ***********************" << std::endl;
+  if((ft_status=FT_GetDeviceInfo(ftdi.ft_handle,&type,&id,serial_number,description,NULL))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIException(_HERE_,error_messages[ft_status],ftdi.comm_id);
+  } 
+  out << "Device name: " << ftdi.comm_id << std::endl;
+  out << "Type: " << type << std::endl;
+  out << "Device id: " << id << std::endl;
+  out << "Serial Number: " << serial_number << std::endl;
+  out << "Description: " << description << std::endl;
+
+  return out;
+}
+
+CFTDI::~CFTDI(){
+  this->close();
+}
+
+
diff --git a/src/usb_ftdi/ftdimodule.h b/src/usb_ftdi/ftdimodule.h
new file mode 100644
index 0000000..720d56e
--- /dev/null
+++ b/src/usb_ftdi/ftdimodule.h
@@ -0,0 +1,334 @@
+#ifndef _FTDI_MODULE_H
+#define _FTDI_MODULE_H
+
+#include <ostream>
+#include "ftd2xx.h"
+#include "comm.h"
+
+/**
+  \brief structure to hold the configuration parameters of the FTDI device
+
+  The necessary configuration parameters of the supported FTDI devices are:
+
+  - baud rate (only used on serial devices)
+  - number of bits per word (only used on serial devices)
+  - number of stop bits (only used on serial devices)
+  - parity type (only used on serial devices)
+  - read and write timeouts (used on all devices)
+  - latency timer (used on all devices)
+
+  These parameters must be configured after the device has been opened by
+  calling the open() function and before reading or writing data from or to
+  the device.
+ 
+*/
+typedef struct TFTDIconfig 
+{
+  /**
+   * \brief desired baud rate
+   *
+   * This parameter specifies the desired baud rate in bits per second of the
+   * device. This parameter only has meaning when the FTDI device is of the
+   * serial kind. Otherwise it is not used and it can be set to -1 to avoid 
+   * its initialization on the driver.
+   *
+   * The possible values for this parameter are:
+   *
+   * - 300
+   * - 600
+   * - 1200
+   * - 2400
+   * - 4800
+   * - 9600
+   * - 14400
+   * - 19200
+   * - 38400
+   * - 57600
+   * - 115200
+   * - 230400
+   * - 460800
+   * - 921600
+   *
+   */ 
+  int baud_rate; 
+  /**
+   * \brief desired word length
+   *
+   * This parameter specifies the desired length of each packet in bits. This
+   * parameter only has meaning when the FTDI device is of the serial kind.
+   * Otherwise it is not used and it can be set to -1 to avoid its initialization
+   * on the driver.
+   *
+   * The possible values for this parameter are integers from 5 to 8.
+   */ 
+  int word_length;
+  /**
+   * \brief desired number of stop bits
+   *
+   * This parameter specifies the desired number of stop bits of each packet. This
+   * parameter only has meaning when the FTDI device is of the serial kind.
+   * Otherwise it is not used and it can be set to -1 to avoid its initialization
+   * on the driver.
+   *
+   * The possible values for this parameter are:
+   *
+   * - 0 for 1 stop bits
+   * - 1 for 1.5 stop bits
+   * - 2 for 2 stop bits
+   */ 
+  int stop_bits;
+  /**
+   * \brief desired parity type
+   *
+   * This parameter specifies the desired kind of parity to be used. This parameter
+   * only has meaning when the FTDI device is of the serial kind. Otherwise it is 
+   * not used and it can be set to -1 to avoid its initialization on the driver.
+   *
+   * The possible values for this parameter are:
+   *
+   * - 0 for no parity
+   * - 1 for odd parity
+   * - 2 for even parity
+   * - 3 for mark parity
+   * - 4 for space parity 
+   */  
+  int parity;
+  /**
+   * \brief desired timeout for read operations
+   *
+   * This parameter specifies the maximum time to wait for a read operation to end in
+   * miliseconds. 
+   */  
+  int read_timeout;
+  /**
+   * \brief desired timeout for write operations
+   *
+   * This parameter specifies the maximum time to wait for a write operation to end in
+   * miliseconds. 
+   */  
+  int write_timeout;
+  /**
+   * \brief rate at which the receive buffer is flushed
+   *
+   * This parameter specifies the period in miliseconds at which the receive buffer is 
+   * flushed. This value can be set to any value between 2 and 255.
+   */
+  unsigned char latency_timer;
+}TFTDIconfig;
+
+/**
+ * \brief FTDI driver class using the D2XX library
+ *
+ * This class implements the access to any FTDI device supported by the D2XX driver
+ * provided by the manufacturer. This class inherits from the CComm class to have 
+ * all the basic communications issues solved and it only has to implement the
+ * specific functions to open the FTDI device (hatd_open()), config (hard_config()),
+ * read from (hard_read()), write to (hard_write()) and close it (hard_close()).
+ * Also it implements a function to assynchronously wait for communication events
+ * to happen (hard_wait_comm_event()). 
+ *
+ * This class allows access to any external device that uses one of the supported
+ * FTDI chips. The supported FTDI devices are:
+ *
+ * - FT2232H
+ * - FT4232H
+ * - FT232R
+ * - FT245R
+ * - FT2232
+ * - FT232B
+ * - FT245B
+ * - FT8U232AM
+ * - FT8U245AM
+ *
+ * This driver must be used when the used VID and PID values do not coincide with 
+ * the dafualt values used by the manufacturer. Otherwise, it is possible to use
+ * a standard serial port driver (CRS232) to access the device. However, this 
+ * driver provides faster transfer rates to and from the device than the virtual
+ * COM port option.
+ *
+ * This class uses exceptions to report errors. The name of the exception 
+ * class associated to this class is CFTDIException. For a more detailes
+ * descritpion, see the corresponding documentation.
+ * 
+ */
+class CFTDI : public CComm 
+{
+  private:
+    /**
+     * \brief Handle to the FTDI device
+     *
+     * This handle is used to access the associated FTDI device. By default it is set
+     * to NULL when the object is created, and it is initialized when the device is 
+     * opened by calling the open() function. This handle remains valid until the object
+     * is destroyed or the FTDI device is disconnected
+     */ 
+    FT_HANDLE ft_handle; 
+
+    EVENT_HANDLE event_handle;
+  protected:
+    /**
+     * \brief Function to actually open the device
+     *
+     * This function is called automatically when the base class open() function
+     * is called. It must create a handle to the communication device in order to 
+     * be used in the future. The device handle must be provided by any inherited 
+     * class since it is device dependant.
+     *
+     * This class accepts a generic parameter (void *) to allow the user to pass 
+     * to the function any data structure. This parameter comes from the base
+     * class open() function, but no action is performed on it.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \param comm_dev a generic pointer (void *) to the data structure needed to
+     *                 identify the communication device to open and any other
+     *                 required information. This parameter may be NULL if not
+     *                 needed.
+     */ 
+    void hard_open(void* comm_dev);
+    /**
+     * \brief Function to actually configure the device
+     * 
+     * This function is called automatically when the base class config() function
+     * is called. It must configure the communication device as required by the 
+     * application.
+     *
+     * This class accepts a generic parameter (void *) to allow the user to pass 
+     * to the function any data structure. This parameter comes from the base
+     * class open() function, but no action is performed on it.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \param config a generic pointer (void *) to the data structure needed to
+     *               configure the communicationd device. This parameter may be
+     *               NULL if not needed.
+     */ 
+    void hard_config(void *config);
+    /**
+     * \brief Function to actually read from the device
+     *
+     * This function is automatically called when the new data received is activated.
+     * The read() function from the base class gets data from the internal queue, so
+     * this function is not used. It must try to read the ammount of data specified 
+     * and store it in the data buffer provided without blocking. Also, it must 
+     * return the number of bytes actually read from the devicve, since they may be
+     * different than the desired value.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     *  \param data a reference to the buffer where the received data must be 
+     *  copied. The necessary memory for this buffer must be allocated before 
+     *  calling this function and have enough size to store all the desired data.
+     *  If this buffer is not initialized, the function throws an exception.
+     *
+     *  \param len a positive interger that indicates the number of byte to be
+     *  read from the communication device. This value must be at most the length 
+     *  of the data buffer provided to the function.
+     *
+     *  \return an integer with the number of bytes actually read. These number 
+     *  coincide with the desired number if there is enough data in the internal 
+     *  queue, but it could be smaller if not.
+     */ 
+    int hard_read(unsigned char *data, int len);
+    /**
+     * \brief Function to actually write to the device
+     *
+     * This function is automatically called when the base class write() function
+     * is called. It must try to write the desired ammount of data to the communication 
+     * device without blocking. Also it must return the number of bytes actually
+     * written to the communication device since they may be different from the
+     * desired value.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     *  \param data a reference to the buffer with the data must be send to the 
+     *  device. The necessary memory for this buffer must be allocated before 
+     *  calling this function and have enough size to store all the desired data.
+     *  If this buffer is not initialized, the function throws an exception.
+     *
+     *  \param len a positive interger that indicates the number of byte to be
+     *  written to the communication device. This value must be at most the length 
+     *  of the data buffer provided to the function.
+     *
+     *  \return an integer with the number of bytes actually written. These number 
+     *  coincide with the desired number if there is enough data in the internal 
+     *  queue, but it could be smaller if not.
+     */ 
+    int hard_write(unsigned char *data, int len);
+    /**
+     * \brief Function to actually get the number of bytes availables
+     *
+     * This function is called when the new data received event is activated.
+     * It must get the number of data bytes available from the communication
+     * device and return its value without blocking.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \return an integer with the number of bytes available in the receive queue. 
+     * This value can be 0 if there is no data available, but there is ni upper
+     * limit in its value.
+     */ 
+    void hard_close(void);
+    /**
+     * \brief Function to actually wait for a given communication event
+     *
+     * This function is called in the internal communciation thread to wait for 
+     * any event on the communuication device. It must check for any event on the
+     * device (reception or error) and return the corresponding identifier. When 
+     * an event is activated, this function must return to allow the base class 
+     * to handle it, and the it is called again to wait for the next event.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     *
+     * \return -1 if there has been any error or else the identifier of the 
+     *         communciation event:
+     *
+     * - 1 for the new data received event
+     * - 2 for the error event
+     */ 
+    int hard_get_num_data(void);
+    /**
+     * \brief Function to actually close the device
+     *
+     * This function is called when the base class close() funciton is called. It
+     * must free the device handle initialized by the open() function and also free
+     * any other resource allocated by the inherited class.
+     *
+     * This function can throw any CCommException object exception or else any
+     * exception class defined by the inherited class.
+     */ 
+    int hard_wait_comm_event(void);
+  public:
+    /**
+     * \brief Constructor
+     *
+     * 
+     */ 
+    CFTDI(const std::string& comm_id);
+    /**
+     * \brief operator << overloading
+     *
+     * This operator allow to show important information about the FTDI device 
+     * associated to the class on any ostream obejct (the standard output, a file, 
+     * etc...).
+     *
+     * \param out A reference to an output device in which to show the desired 
+     *            information.
+     *
+     * \param ftdi A reference to a CFTDIServer object with the information to be
+     *               displayed.
+     *
+     * \return an object with the desired information already formatted. In this case
+     *         it has the number of devices and the information on each of them.
+     */
+    friend std::ostream& operator<< (std::ostream& out,CFTDI& ftdi);
+    ~CFTDI();
+};
+
+#endif
diff --git a/src/usb_ftdi/ftdiserver.cpp b/src/usb_ftdi/ftdiserver.cpp
new file mode 100644
index 0000000..a0c9acc
--- /dev/null
+++ b/src/usb_ftdi/ftdiserver.cpp
@@ -0,0 +1,247 @@
+#include "ftdiexceptions.h"
+#include "ftdiserver.h"
+#include "ftd2xx.h"
+
+CFTDIServer *CFTDIServer::pinstance=NULL;
+
+CFTDIServer::CFTDIServer()
+{
+  this->scan_bus();
+}
+
+CFTDIServer::CFTDIServer(const CFTDIServer& object)
+{
+}
+
+CFTDIServer& CFTDIServer::operator = (const CFTDIServer& object)
+{
+  return *this->pinstance;
+}
+
+CFTDIServer *CFTDIServer::instance(void)
+{
+  if (CFTDIServer::pinstance == NULL)
+  {
+    CFTDIServer::pinstance = new CFTDIServer(); // Creamos la instancia
+  }
+  return CFTDIServer::pinstance; // Retornamos la dirección de la instancia
+}
+
+void CFTDIServer::add_custom_PID(int PID)
+{
+  FT_STATUS status;
+  int i=0;
+
+  for(i=0;i<this->custom_PID.size();i++)
+  {
+    if(this->custom_PID[i]==PID)
+      return;
+  }
+  this->custom_PID.push_back(PID);
+  if((status=FT_SetVIDPID(FTDI_VID,PID))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,error_messages[status]);
+  }
+  else
+  {
+    this->scan_bus();
+  }
+}
+
+void CFTDIServer::scan_bus(void)
+{
+  FT_DEVICE_LIST_INFO_NODE *dev;
+  TDevice_info dev_info;
+  DWORD num_devs=0;
+  FT_STATUS status;
+  int i=0;
+
+  if((status=FT_CreateDeviceInfoList(&num_devs))!=FT_OK)
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,error_messages[status]);
+  }
+  else
+  {
+    this->devices.clear();
+    if(num_devs>0)
+    {
+      dev=new FT_DEVICE_LIST_INFO_NODE[num_devs];
+      if((status=FT_GetDeviceInfoList(dev,&num_devs))!=FT_OK)
+      {
+        /* handle exceptions */
+        throw CFTDIServerException(_HERE_,error_messages[status]);
+      }
+      for(i=0;i<num_devs;i++)
+      {
+        if(dev[i].Flags&0x00000001)
+          dev_info.opened=true;
+        else
+          dev_info.opened=false;
+        if(dev[i].Flags&0x00000002)
+          dev_info.high_speed=true;
+        else
+          dev_info.high_speed=false;
+        dev_info.type=dev[i].Type;
+        dev_info.id=dev[i].ID;
+        dev_info.location=dev[i].LocId;
+        dev_info.serial_number=dev[i].SerialNumber;
+        dev_info.description=dev[i].Description;
+        this->devices.push_back(dev_info);
+      }
+    }
+  }
+}
+
+int CFTDIServer::get_num_devices(void)
+{
+  return this->devices.size();
+}
+
+std::vector<int> CFTDIServer::get_ids_by_description(const std::string& desc)
+{
+  std::string description;
+  std::vector<int> ids;
+  int num=0,i=0;
+
+  num=this->get_num_devices();
+  for(i=0;i<num;i++)
+  {
+    description=this->get_description(i);
+    if(description.find(desc)!=std::string::npos)
+      ids.push_back(i);
+  }
+
+  return ids;
+}
+
+bool CFTDIServer::is_opened(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].opened;
+}
+
+bool CFTDIServer::is_high_speed(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].high_speed;
+}
+
+unsigned long int CFTDIServer::get_type(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].type;
+}
+
+unsigned long int CFTDIServer::get_id(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].id;
+}
+
+unsigned long int CFTDIServer::get_location(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].location;
+}
+
+std::string& CFTDIServer::get_serial_number(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].serial_number;
+}
+
+std::string& CFTDIServer::get_description(int index)
+{
+  if(index<0 || index>this->devices.size())
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"There exist no USB device with the given index.\n");
+  }
+  else
+    return this->devices[index].description;
+}
+
+CFTDI *CFTDIServer::get_device(std::string& serial_desc)
+{
+  std::string dev_name;
+  CFTDI *ftdi_device;
+  int i=0;
+
+  if(serial_desc.size()==0)
+  {
+    /* handle exceptions */
+    throw CFTDIServerException(_HERE_,"Invalid serial number or description - empty string.\n");
+  }
+  else
+  {
+    for(i=0;i<this->devices.size();i++)
+    {
+      if(this->devices[i].serial_number==serial_desc || this->devices[i].description==serial_desc)
+      {
+        dev_name="FTDI_";
+        dev_name+=serial_desc;
+        ftdi_device=new CFTDI(dev_name);
+        ftdi_device->open((void *)&serial_desc);
+        return ftdi_device;
+      }
+    }
+  }
+}
+
+std::ostream& operator<< (std::ostream &out,CFTDIServer &server)
+{
+  int i=0;
+
+  out << "Number of FTDI devices detected: " << server.devices.size() << std::endl;
+  for(i=0;i<server.devices.size();i++)
+  { 
+    out << "Device " << i << std::endl;
+    if(server.devices[i].opened)
+      out << "The device is opened" << std::endl;
+    else
+      out << "The device is closed" << std::endl;
+    if(server.devices[i].high_speed)
+      out << "The device is high speed" << std::endl;
+    else
+      out << "The device is full speed" << std::endl;
+    out << "Type: " << server.devices[i].type << std::endl;
+    out << "Device id: " << server.devices[i].id << std::endl;
+    out << "Location: " << server.devices[i].location << std::endl;
+    out << "Serial number: " << server.devices[i].serial_number << std::endl;
+    out << "Description: " << server.devices[i].description << std::endl;
+  }
+
+  return out;
+}
diff --git a/src/usb_ftdi/ftdiserver.h b/src/usb_ftdi/ftdiserver.h
new file mode 100644
index 0000000..0897388
--- /dev/null
+++ b/src/usb_ftdi/ftdiserver.h
@@ -0,0 +1,395 @@
+#ifndef _FTDI_SERVER
+#define _FTDI_SERVER
+
+#include <vector>
+#include <iostream>
+#include "ftdimodule.h"
+#include "mutex.h"
+
+const int FTDI_VID=0x0403;
+const int DEFAULT_FTDI_PID[]={0x6001,0x6010,0x6006};
+
+/**
+ * \brief structure with information about a device
+ *
+ * This structure holds information about one of the devices available on a system.
+ * This strcucture is first initialized when the device is first detected, and then
+ * it is stored in the internal device list.
+ */
+typedef struct
+{
+  /**
+   * \brief opened flag
+   *
+   * This flag indicates if the USB device is opened by the system or any other
+   * application. It will anly be possible to use devices that are not currently
+   * opened by any othe process. Trying to do so will throw an exception.
+   */ 
+  bool opened;
+  /**
+   * \brief High speed flag
+   *
+   * This flag indicates if the corresponding device supports the high speed 
+   * specification of the USB (true) or not (false).
+   */ 
+  bool high_speed;
+  /**
+   * \brief Type of the USB device
+   */  
+  unsigned long int type;
+  /**
+   * \brief USB device identification
+   */  
+  unsigned long int id;
+  /**
+   * \brief location of the device on the bus
+   */
+  unsigned long int location;
+  /**
+   * \brief Serial number of the device
+   */ 
+  std::string serial_number;
+  /**
+   * \brief Decription of the device
+   */  
+  std::string description;
+}TDevice_info;
+
+/**
+ * \brief Global event server
+ *
+ * This class implements an FTDI device server which is global to the application
+ * and also only one instance exist that is shared by all obects requiring 
+ * access to an FTDI device.
+ *
+ * Each FTDI device is identified by a VID and PID combination. At construction
+ * time this server searches the system for devices with the default VID and PID
+ * combinations provided by the manufacturer. If there exist devices with 
+ * different VID and PID combinations it is necessary to call the add_custom_PID()
+ * function, which rescans the system for available devices and adds them into
+ * the internal list.
+ *
+ * The get_num_devices() function can be used to retrieve the number of FTDI
+ * devices available on the system. To actually get one CFTDI class object to
+ * handle the desired device it is necessary to call the get_device() function,
+ * either providing the location of the desired device, the serial number or
+ * its description. It will be impossible to get an object of a device that it 
+ * is already in use by the system or any other application. 
+ *
+ * This class uses exceptions to report errors. The name of the exception 
+ * class associated to this class is CFTDIServerException. For a more detailes
+ * descritpion, see the corresponding documentation.
+ *
+ */
+class CFTDIServer
+{
+  private:
+    /**
+     * \brief Reference to the unique instance of this class
+     *
+     * This reference points to the unique instance of this class. By default 
+     * it is set to NULL, and it is initialized in the first call to the 
+     * instance() function. after that, successive calls to rhat function
+     * will return the pointer to the previously created object.
+     */ 
+    static CFTDIServer* pinstance;
+    /**
+     * \brief Information on all FTDI devices available
+     *
+     * This list have important information on all FTDI devices available on
+     * the system with one of the default or custom PID values. The information
+     * include:
+     *
+     * - Opened flag (as a boolean)
+     * - High speed flag (as a boolean) 
+     * - Device type (as an unsigned long int)
+     * - Device id (as an unisgned long int)
+     * - Device location (as an int)
+     * - Serial number (as a string)
+     * - Description (as a string)
+     *
+     * This list if first initialized at construction time with the devices
+     * presnt with the default PID values. When new PID values are added and
+     * FTDI devices with those PID values are present, the list is expanded
+     * wiht the information of the new devices.
+     *
+     */ 
+    std::vector<TDevice_info> devices;
+    /**
+     * \brief List of all custom PID values
+     *
+     * This list has all the PID added by the user. Initially it is empty,
+     * and it is updated any time the add_custom_PID() function is called.
+     */  
+    std::vector<int> custom_PID;
+  protected:
+    /**
+     * \brief Default constructor
+     *
+     * This constructor initializes the available FTDI devices list with the 
+     * default VID and PID combinations. This list can only be modified when
+     * a new PID is added and the sustem is rescaned for new devices.
+     *
+     * The reference to the newly created object is not modified. This constructor 
+     * is only called once and from inside the instance() function. It can not
+     * be called directly by the user since it is declared as protected.
+     *
+     */ 
+    CFTDIServer();
+    /** 
+     * \brief Copy constructor
+     *
+     * This constructor is used to initialize a new object with the contents of
+     * an existing one. Since there could be only one instance of this class,
+     * only the pinstance attribute must be copied, but since it is static
+     * nothing is to be done in this constructor.
+     *
+     * \param object an existing instance of a CFTDIServer class which has been
+     *               already initialized.
+     *
+     */ 
+    CFTDIServer(const CFTDIServer& object);
+    /**
+     * \brief assign operator overloading
+     *
+     * This function overloads the assign operator for this class. Since there 
+     * could be only one instance of this class, only the pinstance attribute 
+     * must be copied, but since it is static, nothing is to be done.
+     *
+     * \param object an existing instance of a CFTDIServer class which has been
+     *               already initialized.
+     */ 
+    CFTDIServer& operator = (const CFTDIServer& object);
+  public:
+    /**
+     * \brief Function to get a reference to the unique instance
+     *
+     * This function returns a reference to the only instance of the singleton.
+     * When it is first called, a new object of this class is created, and the
+     * successive calls only return a reference to the previously created
+     * object.
+     *
+     * Since this function is static, it can be call anywhere in the code so
+     * all objects can access the unique event handler. 
+     *
+     * \return A reference to the only instance of this class. This reference 
+     *         must not be freed until the application ends. 
+     *  
+     */ 
+    static CFTDIServer* instance(void);
+    /**
+     * \brief Function to add new PID values
+     *
+     * This function can be used to include new VID and PID combinations. By
+     * default the FTDI driver only recognises devices with the default PID
+     * values. Some manufacturers provide their own PID, so it is necessary to 
+     * include it using this function.
+     *
+     * When this function is called, the system is rescaned to find any available
+     * FTDI device with the provided PID. In case there is any, the internal 
+     * device list is expanded with the new information. If there is no device
+     * with the provided PID this function does nothing.
+     *
+     * This class uses exceptions to report errors. The name of the exception 
+     * class associated to this class is CFTDIServerException. For a more detailes
+     * descritpion, see the corresponding documentation.
+     *
+     * \param PID a positive value corresponding to the desired PID value. This
+     *            value must be the one provided by the manufacturer.
+     */ 
+    void add_custom_PID(int PID);
+    /**
+     * \brief function to scan the bus
+     * 
+     * This function can be used to scan the USB bus for FTDI devices. When it is
+     * called, it automatically updates all the internal information regarding any
+     * FTDI devices connected to the USB port of a computer. 
+     *
+     * This function is also called internally at contruction time and when a new
+     * PID is added. It can also be called by the user to update the local information
+     * when there are changes on the bus (a device has been connected or disconnected).
+     *
+     * This class uses exceptions to report errors. The name of the exception 
+     * class associated to this class is CFTDIServerException. For a more detailes
+     * descritpion, see the corresponding documentation.
+     */
+    void scan_bus(void);
+    /**
+     * \brief Function to get the number of available FTDI devices
+     *
+     * This function return to total number of FTDI devices present in the system
+     * with any of the default or custom PID values. Some of the devices may be
+     * used by the system or other applications. In this case, they can not be 
+     * accessed, but they are listed anyway.
+     *
+     * This class uses exceptions to report errors. The name of the exception 
+     * class associated to this class is CFTDIServerException. For a more detailes
+     * descritpion, see the corresponding documentation.
+     *
+     * \return The number of devices available on the system
+     */ 
+    int get_num_devices(void);
+    /**
+     * \brief Function to check if teh device is opened
+     *
+     * This function returna wether the device located at the given position of the
+     * internal list is opened or not.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     *
+     * \return a boolean that indicates if the device is opened (true) or not (false).
+     */ 
+    bool is_opened(int index);
+    /**
+     * \brief Function to check the maximum speed supported
+     *
+     * This function returns wether the device located at the given position of the
+     * internal list supports high speed modes or not.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     *
+     * \return a boolean that indicates if the device supports high speed modes (true)
+     *         or not (false).
+     */ 
+    bool is_high_speed(int index);
+    /**
+     * \brief Function to get the type of the device
+     *
+     * This function returns the type of the USB device located at the given position
+     * of the internal list.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     * 
+     * \return an integer which represents the type of the USB device.
+     */ 
+    unsigned long int get_type(int index);
+    /**
+     * \brief Function to get the identifiers of the devices with a given description
+     *
+     * This function returns a vector with the absolute indexes of all the FTDI 
+     * devices that matches a given description or that contain a given string in 
+     * their description. This function is usefull when trying to access devices
+     * of a given type.
+     *
+     * If no devices are present with the given description, an empty vector is
+     * returned
+     *
+     * \param desc a null terminated string with the whole desired description or
+     *             a subset of it. 
+     *
+     * \return a vector with the absolute indexes of all the devices matching 
+     *         the given description. If no devices are found, the returned 
+     *         vector is empty.
+     *
+     */
+    std::vector<int> get_ids_by_description(const std::string &desc);
+    /**
+     * \brief Function to get the device identifier
+     *
+     * This function returns the identifier of the USB device located at the given 
+     * position of the internal list.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     * 
+     * \return an integer which represents the identifier of the USB device.
+     *
+     */ 
+    unsigned long int get_id(int index);
+    /**
+     * \brief Function to get the location of the device on teh bus
+     *
+     * This function returns the location of the USB device on the USB bus, which is 
+     * located at the given position of the internal list.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     * 
+     * \return an integer which represents the location of the USB device on the bus.
+     */ 
+    unsigned long int get_location(int index);
+    /**
+     * \brief Function to get the serial number of the device
+     *
+     * This function return the serial number of the USB device located at the given 
+     * position of the internal list as a string.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     * 
+     * \return a string with the serial number of the USB device.
+     */
+    std::string& get_serial_number(int index);
+    /**
+     * \brief Function to get the description of the device
+     *
+     * This function return the description of the USB device located at the given 
+     * position of the internal list as a string.
+     *
+     * \param index is an integer with the position on the internal list of the 
+     *              desired device. This value must be between 0 and the number of
+     *              devices on the list -1.
+     * 
+     * \return a string with the description of the USB device.
+     */ 
+    std::string& get_description(int index);
+    /**
+     * \brief Function to get a new CFTDI object
+     *
+     * Function to get a CFTDI class object associated to the desired FTDI device.
+     * In this case the desired FTDI device is identified by either its serial number
+     * or its description. The serial number is in general the best way to select
+     * the desired device since it is unique. 
+     *
+     * However, the description may be shared by several devices of the same kind. 
+     * In this second case, the first device found on the list is returned.
+     * If the desired device is already being used by the system or any other 
+     * application this function will fail throwing an exception of the
+     * CFTDIServer class. Otherwise, a new CFTDI object is returned. 
+     *
+     * The CFTDI object returned by this function is already opened using the
+     * serial number or description provided to the function. The name of the 
+     * returned object is created by appending the provided serial number or
+     * description to "FTDI_" to have a unique identifier for each device.
+     *
+     * This class uses exceptions to report errors. The name of the exception 
+     * class associated to this class is CFTDIServerException. For a more detailes
+     * descritpion, see the corresponding documentation.
+     *
+     * \param serial_desc a string with either the serial number or the description
+     *                    of the desired FTDI device.
+     *
+     * \return on success this function returns a pointer a newly created CFTDI
+     *         object associated to the desired FTDI device. The calling process
+     *         is responsible for destroying the returned obejct.
+     */ 
+    CFTDI *get_device(std::string& serial_desc);
+    /**
+     * \brief operator << overloading
+     *
+     * This operator allow to show important information about the class and the
+     * information of all FTDI devices available on any ostream obejct (the standard
+     * output, a file, etc...).
+     *
+     * \param out A reference to an output device in which to show the desired 
+     *            information.
+     *
+     * \param server A reference to a CFTDIServer object with the information to be
+     *               displayed.
+     *
+     * \return an object with the desired information already formatted. In this case
+     *         it has the number of devices and the information on each of them.
+     */ 
+    friend std::ostream& operator<< (std::ostream &out,CFTDIServer &server);
+};
+
+#endif
-- 
GitLab