From 4b90f2818ee1922ab7b9313660d8ff566dc4a875 Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 3 Oct 2013 16:04:02 +0400 Subject: [PATCH] 3.10.2013 - PIPeer release, PIConsole now can work as server and remote client. Remote console test program in directory "remote_console" --- .gitignore | 2 + .kdev4/_custom.kdev4 | 2 + CMakeLists.txt | 81 +- Doxyfile | 1905 +++++++++++++++++++++++++++++++++ clean | 5 + clean.bat | 14 + main.cpp | 119 +- make.sh | 0 make_rc_win.bat | 1 + pibytearray.h | 31 +- picli.cpp | 14 +- picli.h | 39 +- piconsole.cpp | 325 +++++- piconsole.h | 146 ++- picontainers.h | 2 +- pidiagnostics.cpp | 99 ++ pidiagnostics.h | 85 ++ piethernet.cpp | 221 ++-- piethernet.h | 68 +- pievaluator.cpp | 30 + pievaluator.h | 3 +- pifile.cpp | 2 +- pifile.h | 54 +- piincludes.cpp | 153 ++- piincludes.h | 216 +++- piiodevice.cpp | 22 +- piiodevice.h | 121 ++- pikbdlistener.cpp | 4 +- pimath.cpp | 596 +++++++++-- pimath.h | 195 +++- pimultiprotocol.h | 4 +- piobject.cpp | 35 + piobject.h | 23 +- pip.cbp | 333 ++++++ pip.pro | 2 +- pip.pro.user | 215 ++++ pip_export.h | 35 + pip_resource.rc | 38 + pip_resource_win.rc | 34 + pip_test_resource.rc | 38 + pipeer.cpp | 411 ++++++- pipeer.h | 131 ++- piprocess.cpp | 4 +- piprotocol.cpp | 82 +- piprotocol.h | 3 +- piserial.cpp | 38 +- pistatemachine.h | 318 ++++++ pistring.cpp | 156 ++- pistring.h | 46 +- pisystemmonitor.cpp | 2 +- pitimer.cpp | 21 +- pitimer.h | 5 + piusb.cpp | 357 ++++++ piusb.h | 137 +++ remote_console/CMakeLists.txt | 6 + remote_console/main.cpp | 66 ++ 56 files changed, 6422 insertions(+), 673 deletions(-) create mode 100644 .gitignore create mode 100644 Doxyfile create mode 100755 clean.bat mode change 100644 => 100755 make.sh create mode 100755 make_rc_win.bat create mode 100644 pidiagnostics.cpp create mode 100644 pidiagnostics.h create mode 100644 pip.cbp create mode 100644 pip.pro.user create mode 100644 pip_export.h create mode 100644 pip_resource.rc create mode 100644 pip_resource_win.rc create mode 100644 pip_test_resource.rc create mode 100644 pistatemachine.h create mode 100644 piusb.cpp create mode 100644 piusb.h create mode 100644 remote_console/CMakeLists.txt create mode 100644 remote_console/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..da8eb4f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +doc + diff --git a/.kdev4/_custom.kdev4 b/.kdev4/_custom.kdev4 index 0c4c24e1..30da0ced 100644 --- a/.kdev4/_custom.kdev4 +++ b/.kdev4/_custom.kdev4 @@ -5,6 +5,7 @@ activity=pip activityId= desktop=-1 formfactor=0 +geometry=0,0,1644,997 immutability=1 lastDesktop=-1 lastScreen=0 @@ -14,6 +15,7 @@ plugin=newspaper screen=0 wallpaperplugin=color wallpaperpluginmode= +zvalue=0 [Containments][1][Wallpaper][color] backgroundMode=0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 30e5156b..3c20124c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,86 @@ project(pip) cmake_minimum_required(VERSION 2.6) include_directories(${CMAKE_CURRENT_SOURCE_DIR} .) -set(VERSION "0.0304") +include(CheckFunctionExists) +set(VERSION "0.0305") set(SOVERSION ${VERSION}) set(CMAKE_BUILD_TYPE "Release") +set(LIBS) file(GLOB CPPS "pi*.cpp") + + +# Check Bessel functions +set(CMAKE_REQUIRED_INCLUDES math.h) +set(CMAKE_REQUIRED_LIBRARIES m) +CHECK_FUNCTION_EXISTS(j0 PIP_MATH_J0) +CHECK_FUNCTION_EXISTS(j1 PIP_MATH_J1) +CHECK_FUNCTION_EXISTS(jn PIP_MATH_JN) +CHECK_FUNCTION_EXISTS(y0 PIP_MATH_Y0) +CHECK_FUNCTION_EXISTS(y1 PIP_MATH_Y1) +CHECK_FUNCTION_EXISTS(yn PIP_MATH_YN) +if (DEFINED PIP_MATH_J0) + add_definitions("-DPIP_MATH_J0") +endif () +if (DEFINED PIP_MATH_J1) + add_definitions("-DPIP_MATH_J1") +endif () +if (DEFINED PIP_MATH_JN) + add_definitions("-DPIP_MATH_JN") +endif () +if (DEFINED PIP_MATH_Y0) + add_definitions("-DPIP_MATH_Y0") +endif () +if (DEFINED PIP_MATH_Y1) + add_definitions("-DPIP_MATH_Y1") +endif () +if (DEFINED PIP_MATH_YN) + add_definitions("-DPIP_MATH_YN") +endif () + + +# Check if USB is on (to enable use "-DUSB=" argument of cmake) +if (DEFINED USB) + message(STATUS "Building with USB support") + unset(USB) + add_definitions("-DPIP_USB") + list(APPEND LIBS usb) +else () + message(STATUS "Building without USB support") +endif () + + if (${WIN32}) + list(APPEND LIBS ws2_32 Iphlpapi) include(GenerateExportHeader) - execute_process(COMMAND "make_rc_win.bat") - add_definitions("-O2") - #add_definitions(-DEF) + execute_process(COMMAND "make_rc_win.bat" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) list(APPEND CPPS "pip_resource_win.o") add_library(pip SHARED ${CPPS}) generate_export_header(pip) - target_link_libraries(pip ws2_32 Iphlpapi) -else (${WIN32}) - if(DEFINED ENV{QNX_HOST}) + if (${CMAKE_C_COMPILER} STREQUAL "cl") + add_definitions("/O2 /Ob2 /Ot") + else () add_definitions("-O2") + endif () +else () + add_definitions("-O2") + if (DEFINED ENV{QNX_HOST}) + list(APPEND LIBS socket) add_library(pip STATIC ${CPPS}) - else() - add_definitions("-Wall -O2 -g3") + else () + list(APPEND LIBS pthread rt) + include(GenerateExportHeader) + add_definitions("-Wall -g3") add_library(pip SHARED ${CPPS}) - endif() - target_link_libraries(pip pthread rt) -endif (${WIN32}) + generate_export_header(pip) + endif () +endif () +target_link_libraries(pip ${LIBS}) + + +# Test program add_executable(pip_test "main.cpp") target_link_libraries(pip_test pip) + + add_subdirectory(system_test) +add_subdirectory(remote_console) diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..104605f6 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1905 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = PIP + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.3.6 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Platform-Independent Primitives" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = "handlers=\name Handlers" \ + "vhandlers=\name Virtual handlers" \ + "events=\name Events" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = NO + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = doc/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 246 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 79 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 75 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = YES + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = pip.qhp + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = PIP + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = NO + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN \ + PIP_EXPORT + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = NO + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 8 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 12 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = svg + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/clean b/clean index 395a12c3..36c280aa 100755 --- a/clean +++ b/clean @@ -5,3 +5,8 @@ rm -vf ./CMakeCache.txt ./Makefile ./cmake_install.cmake ./install_manifest.txt cd system_test rm -rvf ./CMakeFiles rm -vf ./CMakeCache.txt ./Makefile ./cmake_install.cmake ./install_manifest.txt ./*~ ./*cxx ./moc_* ./*.o ./core +cd .. +cd remote_console +rm -rvf ./CMakeFiles +rm -vf ./CMakeCache.txt ./Makefile ./cmake_install.cmake ./install_manifest.txt ./*~ ./*cxx ./moc_* ./*.o ./core +cd .. diff --git a/clean.bat b/clean.bat new file mode 100755 index 00000000..9e0579b0 --- /dev/null +++ b/clean.bat @@ -0,0 +1,14 @@ +#make clean +del /q /f /s CMakeFiles +rmdir /q /s CMakeFiles +del /q /f CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *~ *cxx moc_* *.o *.exe *.a *.dll *.lib core +cd system_test +del /q /f /s CMakeFiles +rmdir /q /s CMakeFiles +del /q /f CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *~ *cxx moc_* *.o *.exe *.a *.dll *.lib core +cd .. +cd remote_console +del /q /f /s CMakeFiles +rmdir /q /s CMakeFiles +del /q /f CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *~ *cxx moc_* *.o *.exe *.a *.dll *.lib core +cd .. diff --git a/main.cpp b/main.cpp index d07f29e4..7e496bf8 100644 --- a/main.cpp +++ b/main.cpp @@ -202,95 +202,68 @@ struct msg2105base { float tangaj_CP; }; - -struct InpuData { - InpuData() {header.msg_id = 2113;} - msgHeader header; - uint first_number; - struct { - uchar packet_number: 7; - uchar packet_last : 1; - }; - struct { - uchar antenna_number : 3; - uchar frequency_number: 3; - uchar data_type : 2; - }; - uchar data[122]; -}; - #pragma pack(pop) class RC: public PIObject { PIOBJECT(RC) public: + EVENT_HANDLER2(void, peer, const PIString & , from, const PIByteArray &, data) {piCout << "received from" << from << "\"" << PIString(data) << "\"";} + EVENT_HANDLER2(void, slot, void * , data, int, size) {piCout << "read" << PIString((char*)data, size);} EVENT_HANDLER2(void, re, ullong, id, int, size) {piCout << "written id =" << id << "size =" << size;} }; +int main (int argc, char * argv[]) { + //PIKbdListener kbd; + //kbd.enableExitCapture(); + + /*PIEthernet::InterfaceList il = PIEthernet::interfaces(); + //const PIEthernet::Interface & i(*(il.getByName("lo"))); + piForeachC (PIEthernet::Interface & i, il) + piCout << NewLine << i.name << NewLine + << "index" << i.index << NewLine + << "address" << i.address << NewLine + << "netmask" << i.netmask << NewLine + << "mac" << i.mac << NewLine + << "broadcast" << i.broadcast << NewLine + << "isActive" << i.isActive() << NewLine + << "isRunning" << i.isRunning() << NewLine + << "isBroadcast" << i.isBroadcast() << NewLine + << "isMulticast" << i.isMulticast() << NewLine + << "isLoopback" << i.isLoopback() << NewLine + << "isPTP" << i.isPTP();*/ + //return 0; -int main(int argc, char ** argv) { - InpuData data; - PIEthernet eth(PIEthernet::UDP); - data.antenna_number = data.frequency_number = data.data_type = 0; - float t = 0.f; - int pc = 20, cnt = 0; - for (int p = 0; p < pc; ++p) { - data.packet_number = p; - data.packet_last = (p == pc - 1 ? 1 : 0); - data.first_number = p * 97; - PIBitArray ba_; - int ci = 0; - for (int i = 0; i < 97; ++i) { - t += M_PI/4; - ci = sin(t) * 511 + 511; - if (cnt % 500 > 80) ci = 512; - cnt++; - for (int b = 0; b < 10; ++b) - ba_.push_back(((ci >> b) & 1) == 1); - } - //cout << ba_.byteSize() << ", " << ba_.bitSize() << endl; - memcpy(data.data, ba_.data(), 122); - eth.send("127.0.0.1:5000", &data, sizeof(data)); - } - /*cout << ba_ << endl; - for (int i = 0; i < 122; ++i) - cout << int(data.data[i]) << ", "; - cout << endl;*/ + /*RC rc__; + PIPeer peer(argv[1]); + CONNECT2(void, const PIString & , const PIByteArray &, &peer, dataReceivedEvent, &rc__, peer); + msleep(1000); + peer.send("a", ("hello a from '" + PIString(argv[1]) + "'!").data()); + peer.send("b", ("hello b from '" + PIString(argv[1]) + "'!").data()); + peer.send("c", ("hello c from '" + PIString(argv[1]) + "'!").data());*/ + int a__, b__; + PIConsole console(false); + console.enableExitCapture(); + PIProtocol p("/home/peri4/work/ISPUM/nosit_VM6/protocols.conf", "gas", 0, 0, &a__, 4, &b__, 4); + p.start(); + console.addVariable("service", &p); + console.start(); + console.startServer("cons"); + console.waitForFinish(); return 0; - /*PIKbdListener kbd; - kbd.enableExitCapture(); - PIEthernet * _eth; - PIByteArray ba_(16); - if (PIString(argv[argc - 1]) == "s") { - _eth = new PIEthernet(PIEthernet::TCP_Server); - piCout << "listen" << _eth->listen("127.0.0.1:1111"); - int cc = 0; - while (!PIKbdListener::exiting) { - if (cc != _eth->clientsCount()) { - piCout << "new client"; - _eth->clients().back()->startThreadedRead(); - cc++; - } - msleep(1); - } - } else { - _eth = new PIEthernet(PIEthernet::TCP_Client); - piCout << "connection" << _eth->connect("127.0.0.1:1111"); - _eth->send(PIString("0123456789101112").data(), 16); - while (!PIKbdListener::exiting) { - msleep(1); - } + PIFile f("picontainers.h"); + PIString all = f.readAll(); + while (!all.isEmpty()) { + PIString line = all.takeLine(); + if (line.takeWord() == "class") + piCout << "class" << line.trim(); } - delete _eth; return 0; - + msg2105base msg; msg.header.size = sizeof(msg); msg.header.msg_id = 2105; msg.msgTime = 1; - //PIKbdListener kbd; - //kbd.enableExitCapture(); RC rc_; PIEthernet eth; CONNECT2(void, ullong, int, ð, threadedWriteEvent, &rc_, re); @@ -313,7 +286,7 @@ int main(int argc, char ** argv) { piMSleep(50); if (PIKbdListener::exiting) break; } - return 0;*/ + return 0; PISignals::setSlot(signalFunc); //PISignals::grabSignals(PISignals::Interrupt); diff --git a/make.sh b/make.sh old mode 100644 new mode 100755 diff --git a/make_rc_win.bat b/make_rc_win.bat new file mode 100755 index 00000000..f6c1b22a --- /dev/null +++ b/make_rc_win.bat @@ -0,0 +1 @@ +windres -i pip_resource_win.rc -o pip_resource_win.o --include-dir=. \ No newline at end of file diff --git a/pibytearray.h b/pibytearray.h index 05c4b6c3..32389dde 100644 --- a/pibytearray.h +++ b/pibytearray.h @@ -117,19 +117,20 @@ public: PIByteArray & compressRLE(uchar threshold = 192); PIByteArray & decompressRLE(uchar threshold = 192); - PIByteArray compressedRLE(uchar threshold = 192) {PIByteArray ba(*this); ba.compressedRLE(threshold); return ba;} - PIByteArray decompressedRLE(uchar threshold = 192) {PIByteArray ba(*this); ba.decompressedRLE(threshold); return ba;} + PIByteArray compressedRLE(uchar threshold = 192) {PIByteArray ba(*this); ba.compressRLE(threshold); return ba;} + PIByteArray decompressedRLE(uchar threshold = 192) {PIByteArray ba(*this); ba.decompressRLE(threshold); return ba;} PIByteArray & compressHuffman() {*this = huffman.compress(*this); return *this;} PIByteArray & append(void * data, int size) {for (int i = 0; i < size; ++i) push_back(((uchar*)data)[i]); return *this;} + PIByteArray & append(const PIByteArray & data) {for (int i = 0; i < data.size_s(); ++i) push_back(data[i]); return *this;} /*PIByteArray & operator <<(short v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;} PIByteArray & operator <<(ushort v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;} PIByteArray & operator <<(int v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;} PIByteArray & operator <<(uint v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;} PIByteArray & operator <<(llong v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;} PIByteArray & operator <<(ullong v) {for (uint i = 0; i < sizeof(v); ++i) push_back(((uchar*)(&v))[i]); return *this;}*/ - PIByteArray & operator <<(const PIByteArray & v) {for (uint i = 0; i < v.size(); ++i) push_back(v[i]); return *this;} + //PIByteArray & operator <<(const PIByteArray & v) {for (uint i = 0; i < v.size(); ++i) push_back(v[i]); return *this;} uchar checksumPlain8(); uint checksumPlain32(); @@ -141,6 +142,7 @@ public: private: union base64HelpStruct { + base64HelpStruct() {memset(this, 0, sizeof(base64HelpStruct));} struct { uchar ascii0: 6; uchar ascii1: 6; @@ -174,15 +176,16 @@ inline PIByteArray & operator <<(PIByteArray & s, const ulong & v) {PBA_OPERATOR inline PIByteArray & operator <<(PIByteArray & s, const ullong & v) {PBA_OPERATOR_TO return s;} inline PIByteArray & operator <<(PIByteArray & s, const float v) {PBA_OPERATOR_TO return s;} inline PIByteArray & operator <<(PIByteArray & s, const double & v) {PBA_OPERATOR_TO return s;} -inline PIByteArray & operator <<(PIByteArray & s, const PIByteArray::RawData & v) {int os = s.size_s(); s.enlarge(v.s); memcpy(s.data(os), v.d, v.s); return s;} +inline PIByteArray & operator <<(PIByteArray & s, const PIByteArray & v) {s << v.size_s(); int os = s.size_s(); s.enlarge(v.size_s()); if (v.size_s() > 0) memcpy(s.data(os), v.data(), v.size()); return s;} +inline PIByteArray & operator <<(PIByteArray & s, const PIByteArray::RawData & v) {int os = s.size_s(); s.enlarge(v.s); if (v.s > 0) memcpy(s.data(os), v.d, v.s); return s;} template inline PIByteArray & operator <<(PIByteArray & s, const PIVector & v) {s << v.size_s(); for (uint i = 0; i < v.size(); ++i) s << v[i]; return s;} template inline PIByteArray & operator <<(PIByteArray & s, const PIList & v) {s << v.size_s(); for (uint i = 0; i < v.size(); ++i) s << v[i]; return s;} template inline PIByteArray & operator <<(PIByteArray & s, const PIDeque & v) {s << v.size_s(); for (uint i = 0; i < v.size(); ++i) s << v[i]; return s;} -template -inline PIByteArray & operator <<(PIByteArray & s, const T & v) {PBA_OPERATOR_TO return s;} +//template +//inline PIByteArray & operator <<(PIByteArray & s, const T & v) {PBA_OPERATOR_TO return s;} #undef PBA_OPERATOR_TO #define PBA_OPERATOR_FROM memcpy(&v, s.data(), sizeof(v)); s.remove(0, sizeof(v)); @@ -198,16 +201,20 @@ inline PIByteArray & operator >>(PIByteArray & s, ulong & v) {assert(s.size() >= inline PIByteArray & operator >>(PIByteArray & s, ullong & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;} inline PIByteArray & operator >>(PIByteArray & s, float & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;} inline PIByteArray & operator >>(PIByteArray & s, double & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;} -inline PIByteArray & operator >>(PIByteArray & s, PIByteArray::RawData v) {assert(s.size_s() >= v.s); memcpy(v.d, s.data(), v.s); s.remove(0, v.s); return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIByteArray & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); if (sz > 0) memcpy(v.data(), s.data(), v.size()); s.remove(0, v.size()); return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIByteArray::RawData v) {assert(s.size_s() >= v.s); if (v.s > 0) memcpy(v.d, s.data(), v.s); s.remove(0, v.s); return s;} template -inline PIByteArray & operator >>(PIByteArray & s, PIVector & v) {int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIVector & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} template -inline PIByteArray & operator >>(PIByteArray & s, PIList & v) {int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIList & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} template -inline PIByteArray & operator >>(PIByteArray & s, PIDeque & v) {int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} -template -inline PIByteArray & operator >>(PIByteArray & s, T & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIDeque & v) {assert(s.size_s() >= 4); int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} +//template +//inline PIByteArray & operator >>(PIByteArray & s, T & v) {assert(s.size() >= sizeof(v)); PBA_OPERATOR_FROM return s;} #undef PBA_OPERATOR_FROM +inline bool operator ==(PIByteArray & f, PIByteArray & s) {if (f.size_s() != s.size_s()) return false; for (int i = 0; i < f.size_s(); ++i) if (f[i] != s[i]) return false; return true;} +inline bool operator !=(PIByteArray & f, PIByteArray & s) {if (f.size_s() != s.size_s()) return true; for (int i = 0; i < f.size_s(); ++i) if (f[i] != s[i]) return true; return false;} + #endif // PIBYTEARRAY_H diff --git a/picli.cpp b/picli.cpp index 07e766ba..aab3307d 100644 --- a/picli.cpp +++ b/picli.cpp @@ -20,6 +20,18 @@ #include "picli.h" +/*! \class PICLI + * \brief Command-line arguments parser + * + * \section PICLI_sec0 Synopsis + * This class provide handy parsing of command-line arguments. First you should add + * arguments to PICLI with function \a addArgument(). Then you can check if there + * is some argument in application command-line with function \a hasArgument(); + * \section PICLI_sec1 Example + * \snippet picli.cpp main + */ + + PICLI::PICLI(int argc, char * argv[]) { needParse = true; _prefix_short = "-"; @@ -69,7 +81,7 @@ void PICLI::parse() { _args_opt << cra; continue; } - piCout << "[PICli] Arguments overflow, \"" << cra << "\" ignored"; + piCoutObj << "[PICli] Arguments overflow, \"" << cra << "\" ignored"; } if (last == 0 ? false : last->has_value) { last->value = cra; diff --git a/picli.h b/picli.h index 435a3310..65ca228e 100644 --- a/picli.h +++ b/picli.h @@ -1,3 +1,6 @@ +/*! \file picli.h + * \brief Command-Line parser +*/ /* PIP - Platform Independent Primitives Command-Line Parser @@ -20,26 +23,44 @@ #ifndef PICLI_H #define PICLI_H -#include "pistring.h" +#include "piobject.h" -class PIP_EXPORT PICLI +class PIP_EXPORT PICLI: protected PIObject { + PIOBJECT(PICLI) public: + //! Constructor PICLI(int argc, char * argv[]); + + //! Add argument with name "name", short key = name first letter, full key = name void addArgument(const PIString & name, bool value = false) {_args << Argument(name, name[0], name, value); needParse = true;} + + //! Add argument with name "name", short key = "shortKey", full key = name void addArgument(const PIString & name, const PIChar & shortKey, bool value = false) {_args << Argument(name, shortKey, name, value); needParse = true;} + + //! Add argument with name "name", short key = "shortKey", full key = name void addArgument(const PIString & name, const char * shortKey, bool value = false) {_args << Argument(name, PIChar(shortKey), name, value); needParse = true;} + + //! Add argument with name "name", short key = "shortKey", full key = "fullKey" void addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value = false) {_args << Argument(name, shortKey, fullKey, value); needParse = true;} + + //! Add argument with name "name", short key = "shortKey", full key = "fullKey" void addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value = false) {_args << Argument(name, PIChar(shortKey), fullKey, value); needParse = true;} - PIString rawArgument(int index) {return _args_raw[index];} - PIString mandatoryArgument(int index) {return _args_mand[index];} - PIString optionalArgument(int index) {return _args_opt[index];} - const PIStringList & rawArguments() const {return _args_raw;} - const PIStringList & mandatoryArguments() const {return _args_mand;} - const PIStringList & optionalArguments() const {return _args_opt;} - const PIString programCommand() const {return _args_raw.size() > 0 ? _args_raw.front() : PIString();} + + //! Returns unparsed command-line argument by index "index". Index 0 is program execute command. + PIString rawArgument(int index) {parse(); return _args_raw[index];} + PIString mandatoryArgument(int index) {parse(); return _args_mand[index];} + PIString optionalArgument(int index) {parse(); return _args_opt[index];} + + //! Returns unparsed command-line arguments + const PIStringList & rawArguments() {parse(); return _args_raw;} + const PIStringList & mandatoryArguments() {parse(); return _args_mand;} + const PIStringList & optionalArguments() {parse(); return _args_opt;} + + //! Returns program execute command without arguments + const PIString programCommand() {parse(); return _args_raw.size() > 0 ? _args_raw.front() : PIString();} bool hasArgument(const PIString & name) {parse(); piForeach (Argument & i, _args) if (i.name == name && i.found) return true; return false;} PIString argumentValue(const PIString & name) {parse(); piForeach (Argument &i, _args) if (i.name == name && i.found) return i.value; return PIString();} PIString argumentShortKey(const PIString & name) {piForeach (Argument &i, _args) if (i.name == name) return i.short_key; return PIString();} diff --git a/piconsole.cpp b/piconsole.cpp index 509782fe..cf8d94a4 100644 --- a/piconsole.cpp +++ b/piconsole.cpp @@ -18,6 +18,7 @@ */ #include "piconsole.h" +#include "pipeer.h" extern PIMutex __PICout_mutex__; @@ -28,6 +29,7 @@ PIConsole::PIConsole(bool startNow, KBFunc slot): PIThread() { needLockRun(true); ret_func = slot; num_format = 0; + vid = 0; cur_tab = width = height = pwidth = pheight = max_y = 0; def_align = Nothing; #ifdef WINDOWS @@ -40,13 +42,20 @@ PIConsole::PIConsole(bool startNow, KBFunc slot): PIThread() { GetConsoleMode(hOut, &smode); GetConsoleCursorInfo(hOut, &curinfo); #endif + tabs.reserve(16); addTab("main"); listener = new PIKbdListener(key_event, this); + peer = 0; + server_mode = false; + state = Disconnected; + peer_timer.addDelimiter(20); + CONNECT2(void, void * , int, &peer_timer, timeout, this, peerTimer); if (startNow) start(); } PIConsole::~PIConsole() { + stopPeer(); if (isRunning()) stop(); clearTabs(false); @@ -227,6 +236,7 @@ PIString PIConsole::fstr(PIFlags f) { attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; } if (f[PIConsole::Bold]) attr |= FOREGROUND_INTENSITY; + if (f[PIConsole::Underline]) attr |= COMMON_LVB_UNDERSCORE; SetConsoleTextAttribute(hOut, attr); return PIString(); @@ -317,6 +327,7 @@ void PIConsole::run() { width = ws.ws_col; height = ws.ws_row; #endif + //fflush(0); return; __PICout_mutex__.lock(); if (pwidth != width || pheight != height) { clearScreen(); @@ -336,27 +347,37 @@ void PIConsole::run() { if (j > height - 3) continue; j++; moveRight(cx); - if (tv.type == 0 && tv.s == 0) { + if (tv.type == 15) { newLine(); continue; } moveRight(tv.offset); + PIString rstr; + const void * ptr = 0; + if (tv.remote) { + if (tv.type == 0) { + rstr << PIByteArray(tv.rdata); + ptr = &rstr; + } else + ptr = tv.rdata.data(); + } else + ptr = tv.ptr; switch (tv.type) { - case 0: clen = printValue(tv.s != 0 ? *tv.s : PIString(), tv.format); break; - case 1: clen = printValue(tv.b != 0 ? *tv.b : false, tv.format); break; - case 2: clen = printValue(tv.i != 0 ? *tv.i : 0, tv.format); break; - case 3: clen = printValue(tv.l != 0 ? *tv.l : 0l, tv.format); break; - case 4: clen = printValue(tv.c != 0 ? *tv.c : char(0), tv.format); break; - case 5: clen = printValue(tv.f != 0 ? *tv.f : 0.f, tv.format); break; - case 6: clen = printValue(tv.d != 0 ? *tv.d : 0., tv.format); break; - case 7: clen = printValue(tv.sh != 0 ? *tv.sh : short(0), tv.format); break; - case 8: clen = printValue(tv.ui != 0 ? *tv.ui : 0u, tv.format); break; - case 9: clen = printValue(tv.ul != 0 ? *tv.ul : 0ul, tv.format); break; - case 10: clen = printValue(tv.ush != 0 ? *tv.ush : ushort(0), tv.format); break; - case 11: clen = printValue(tv.uc != 0 ? *tv.uc : uchar(0), tv.format); break; - case 12: clen = printValue(tv.ll != 0 ? *tv.ll : 0l, tv.format); break; - case 13: clen = printValue(tv.ull != 0 ? *tv.ull : 0ull, tv.format); break; - case 14: clen = printValue(bitsValue(tv.ptr, tv.bitFrom, tv.bitCount), tv.format); break; + case 0: clen = printValue(ptr != 0 ? *(const PIString*)ptr : PIString(), tv.format); break; + case 1: clen = printValue(ptr != 0 ? *(const bool*)ptr : false, tv.format); break; + case 2: clen = printValue(ptr != 0 ? *(const int*)ptr : 0, tv.format); break; + case 3: clen = printValue(ptr != 0 ? *(const long*)ptr : 0l, tv.format); break; + case 4: clen = printValue(ptr != 0 ? *(const char*)ptr : char(0), tv.format); break; + case 5: clen = printValue(ptr != 0 ? *(const float*)ptr : 0.f, tv.format); break; + case 6: clen = printValue(ptr != 0 ? *(const double*)ptr : 0., tv.format); break; + case 7: clen = printValue(ptr != 0 ? *(const short*)ptr : short(0), tv.format); break; + case 8: clen = printValue(ptr != 0 ? *(const uint*)ptr : 0u, tv.format); break; + case 9: clen = printValue(ptr != 0 ? *(const ulong*)ptr : 0ul, tv.format); break; + case 10: clen = printValue(ptr != 0 ? *(const ushort*)ptr : ushort(0), tv.format); break; + case 11: clen = printValue(ptr != 0 ? *(const uchar*)ptr : uchar(0), tv.format); break; + case 12: clen = printValue(ptr != 0 ? *(const llong*)ptr : 0l, tv.format); break; + case 13: clen = printValue(ptr != 0 ? *(const ullong*)ptr: 0ull, tv.format); break; + case 14: clen = printValue(bitsValue(ptr, tv.bitFrom, tv.bitCount), tv.format); break; } if (clen + tv.offset < (uint)col_wid) { #if defined(QNX) || defined(FREE_BSD) @@ -401,7 +422,7 @@ void PIConsole::fillLabels() { if (ccol.alignment != Nothing) { mx = 0; piForeachC (Variable & j, cvars) - if (j.s != 0) + if (!j.isEmpty()) if (mx < j.name.size()) mx = j.name.size(); mx += 2; @@ -416,7 +437,7 @@ void PIConsole::fillLabels() { Variable & tv(cvars[j]); cvars[j].nx = cx; cvars[j].ny = cy; - if (tv.name.size() == 0) { + if (tv.name.isEmpty()) { cvars[j].offset = 0; clearLine(); newLine(); @@ -424,7 +445,8 @@ void PIConsole::fillLabels() { continue; } clearLine(); - if (tv.type == 0 && tv.s == 0) { + //piCout << tv.name << tv.type << tv.ptr; + if (tv.type == 15) { cvars[j].offset = cvars[j].name.length(); cvars[j].nx += cvars[j].offset; printLine(tv.name, cx, tv.format); @@ -432,7 +454,7 @@ void PIConsole::fillLabels() { cy++; continue; } - if (tv.s != 0) { + if (!tv.isEmpty()) { switch (ccol.alignment) { case Nothing: cvars[j].offset = (tv.name + ": ").length(); @@ -489,7 +511,7 @@ void PIConsole::status() { } -int PIConsole::bitsValue(const void * src, int offset, int count) { +int PIConsole::bitsValue(const void * src, int offset, int count) const { int ret = 0, stbyte = offset / 8, cbit = offset - stbyte * 8; char cbyte = reinterpret_cast(src)[stbyte]; for (int i = 0; i < count; i++) { @@ -522,38 +544,38 @@ const char * PIConsole::toBin(const void * d, int s) { } -#define ADD_VAR_BODY tv.name = name; tv.bitFrom = tv.bitCount = 0; tv.format = format; checkColumn(col); +#define ADD_VAR_BODY vid++; tv.id = vid; tv.name = name; tv.bitFrom = tv.bitCount = 0; tv.format = format; tv.remote = false; checkColumn(col); void PIConsole::addString(const PIString & name, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 0; tv.s = 0; column(col).push_back(tv);} -void PIConsole::addVariable(const PIString & name, const PIString* ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 0; tv.s = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 15; tv.size = 0; tv.ptr = 0; column(col).push_back(tv);} +void PIConsole::addVariable(const PIString & name, const PIString * ptr, int col, PIFlags format) { + ADD_VAR_BODY tv.type = 0; tv.size = 0; tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const bool * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 1; tv.b = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 1; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const int * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 2; tv.i = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 2; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const long * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 3; tv.l = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 3; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const char * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 4; tv.c = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 4; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const float * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 5; tv.f = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 5; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const double * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 6; tv.d = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 6; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const short * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 7; tv.sh = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 7; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const uint * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 8; tv.ui = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 8; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const ulong * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 9; tv.ul = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 9; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const ushort * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 10; tv.ush = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 10; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const uchar * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 11; tv.uc = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 11; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const llong * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 12; tv.ll = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 12; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const ullong * ptr, int col, PIFlags format) { - ADD_VAR_BODY tv.type = 13; tv.ull = ptr; column(col).push_back(tv);} + ADD_VAR_BODY tv.type = 13; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const PIProtocol * ptr, int col, PIFlags format) { addString("protocol " + name, col, format | PIConsole::Bold); addVariable("Rec - " + ptr->receiverDeviceName(), ptr->receiverDeviceState_ptr(), col, format); @@ -592,10 +614,10 @@ void PIConsole::addVariable(const PIString & name, const PISystemMonitor * ptr, addVariable("cpu load", &(ptr->statistic().cpu_load_system), col, format); } void PIConsole::addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitCount, int col, PIFlags format) { - tv.name = name; tv.bitFrom = fromBit; tv.bitCount = bitCount; tv.type = 14; tv.ptr = ptr; tv.format = format; + vid++; tv.id = vid; tv.size = sizeof(ullong); tv.name = name; tv.bitFrom = fromBit; tv.bitCount = bitCount; tv.type = 14; tv.ptr = ptr; tv.format = format; checkColumn(col); column(col).push_back(tv);} void PIConsole::addEmptyLine(int col, uint count) { - tv.name = ""; tv.type = 0; tv.d = 0; tv.format = Normal; + tv.id = 0; tv.size = 0; tv.name = ""; tv.type = 0; tv.ptr = 0; tv.format = Normal; for (uint i = 0; i < count; ++i) { checkColumn(col); column(col).push_back(tv); @@ -670,3 +692,226 @@ inline int PIConsole::printValue(const ushort value, PIFlags inline int PIConsole::printValue(const uint value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ulong value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ullong value, PIFlags format) {PRINT_VAR_BODY} + + + +void PIConsole::startServer(const PIString & name) { + stopPeer(); + server_mode = true; + peer = new PIPeer("_rcs_:" + name); + CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived); + peer_timer.start(50.); + serverSendInfo(); +} + + +void PIConsole::stopPeer() { + remote_clients.clear(); + peer_timer.stop(); + if (peer != 0) delete peer; + peer = 0; + state = Disconnected; +} + + +PIStringList PIConsole::clients() const { + PIStringList sl; + if (peer == 0) return sl; + piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) { + if (i.name.left(6) != "_rcc_:") continue; + sl << i.name.right(i.name.length() - 6); + } + return sl; +} + + +void PIConsole::listenServers() { + stopPeer(); + server_mode = false; + server_name.clear(); + srand(currentSystemTime().nanoseconds); + peer = new PIPeer("_rcc_:" + currentDateTime().toString("hhmmssddMMyy_") + PIString::fromNumber(rand())); + CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived); + peer_timer.start(100.); +} + + +PIStringList PIConsole::availableServers() const { + PIStringList sl; + if (peer == 0) return sl; + piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) { + if (i.name.left(6) != "_rcs_:") continue; + sl << i.name.right(i.name.length() - 6); + } + return sl; +} + + +void PIConsole::connectToServer(const PIString & name) { + if (peer == 0) listenServers(); + server_name = name; +} + + +void PIConsole::disconnect() { + stopPeer(); +} + + +void PIConsole::serverSendInfo() { + if (peer == 0) return; + PIByteArray ba; + ba << int(0xAA); + peer->sendToAll(ba); +} + + +void PIConsole::serverSendData() { + if (peer == 0) return; + PIByteArray ba; + PIVector content; + piForeach (Tab & t, tabs) + piForeach (Column & c, t.columns) + piForeach (Variable & v, c.variables) + if (!v.isEmpty() && v.id > 0) { + VariableContent vc; + vc.id = v.id; + v.writeData(vc.rdata); + content << vc; + } + piForeach (RemoteClient & rc, remote_clients) { + ba.clear(); + switch (rc.state) { + case FetchingData: + ba << int(0xCC) << tabs; + //piCout << "server send const data" << rc.name << ba.size_s(); + break; + case Committing: + ba << int(0xDD); + break; + case Connected: + ba << int(0xEE) << content; + break; + default: break; + } + if (!ba.isEmpty()) + peer->send(rc.name, ba); + } +} + + +PIConsole::RemoteClient & PIConsole::remoteClient(const PIString & fname) { + piForeach (RemoteClient & i, remote_clients) + if (i.name == fname) + return i; + remote_clients << RemoteClient(fname); + return remote_clients.back(); +} + + +void PIConsole::peerReceived(const PIString & from, const PIByteArray & data) { + int type; + PIByteArray ba(data); + ba >> type; + //piCout << "rec packet from" << from << "type" << PICoutManipulators::Hex << type; + if (server_mode) { + if (from.left(5) != "_rcc_") return; + //PIString rcn = from.right(from.length() - 6); + RemoteClient & rc(remoteClient(from)); + switch (type) { + case 0xBB: // fetch const data request + //piCout << "fetch data request from" << from << rc.state; + if (rc.state != Connected) + rc.state = FetchingData; + break; + case 0xCC: // const data commit + //piCout << "commit from" << from; + if (rc.state != Connected) + rc.state = Connected; + break; + default: break; + } + } else { + PIVector content; + PIMap vids; + if (from.left(5) != "_rcs_") return; + PIString rcn = from.right(from.length() - 6); + switch (type) { + case 0xAA: // new server + //piCout << "new server" << rcn; + break; + case 0xCC: // const data + //piCout << "received const data"; + state = Committing; + ba >> tabs; + cur_tab = tabs.isEmpty() ? -1 : 0; + piForeach (Tab & t, tabs) + piForeach (Column & c, t.columns) + piForeach (Variable & v, c.variables) + v.remote = true; + break; + case 0xDD: // const data commit + //piCout << "received commit"; + state = Connected; + break; + case 0xEE: // dynamic data + //piCout << "received data" << ba.size_s(); + piForeach (Tab & t, tabs) + piForeach (Column & c, t.columns) + piForeach (Variable & v, c.variables) + if (!v.isEmpty() && v.id > 0) + vids[v.id] = &v; + ba >> content; + piForeach (VariableContent & vc, content) { + if (vc.id <= 0) continue; + Variable * v = vids.at(vc.id); + if (v == 0) continue; + //piCout << "read" << v->name << vc.rdata.size_s(); + v->rdata = vc.rdata; + } + break; + default: break; + } + } +} + + +void PIConsole::peerTimer(void * data, int delim) { + if (peer == 0) return; + if (server_mode) { + if (delim == 20) + serverSendInfo(); + else + serverSendData(); + } else { + if (delim != 1 || server_name.isEmpty()) return; + const PIPeer::PeerInfo * p = peer->getPeerByName("_rcs_:" + server_name); + if (p == 0) return; + PIByteArray ba; + switch (state) { + case Disconnected: + peer_timer.reset(); + ba << int(0xBB); + //piCout << "send to" << server_name << "fetch request disc"; + peer->send(p, ba); + state = FetchingData; + break; + case FetchingData: + if (peer_timer.elapsed_s() < 3.) + return; + peer_timer.reset(); + ba << int(0xBB); + //piCout << "send to" << server_name << "fetch request fd"; + peer->send(p, ba); + break; + case Committing: + peer_timer.reset(); + ba << int(0xCC); + //piCout << "send to" << server_name << "committing"; + state = Connected; + peer->send(p, ba); + break; + default: break; + }; + } +} diff --git a/piconsole.h b/piconsole.h index c8975af6..3162a9eb 100644 --- a/piconsole.h +++ b/piconsole.h @@ -27,8 +27,12 @@ #ifndef WINDOWS # include # include +#else +# define COMMON_LVB_UNDERSCORE 0x8000 #endif +class PIPeer; + /// handlers: /// void clearVariables(bool clearScreen = true) /// void start(bool wait = false) @@ -134,15 +138,22 @@ public: void disableExitCapture() {listener->disableExitCapture();} bool exitCaptured() const {return listener->exitCaptured();} char exitKey() const {return listener->exitKey();} - -private: - void begin(); - void run(); - void fillLabels(); + + // Server functions + void startServer(const PIString & name); + void stopPeer(); + bool isServerStarted() const {return peer != 0;} + PIStringList clients() const; + + // Client functions + void listenServers(); + PIStringList availableServers() const; + PIString selectedServer() const {return server_name;} + void connectToServer(const PIString & name); + void disconnect(); + bool isConnected() const {return state == Connected;} #ifdef WINDOWS - void getWinCurCoord() {GetConsoleScreenBufferInfo(hOut, &csbi); ccoord = csbi.dwCursorPosition;} - COORD & getWinCoord(int dx = 0, int dy = 0) {getWinCurCoord(); ccoord.X += dx; ccoord.Y += dy; return ccoord;} void toUpperLeft() {SetConsoleCursorPosition(hOut, ulcoord);} void moveRight(int n = 1) {SetConsoleCursorPosition(hOut, getWinCoord(n));} void moveLeft(int n = 1) {SetConsoleCursorPosition(hOut, getWinCoord(-n));} @@ -168,9 +179,19 @@ private: void hideCursor() {printf("\e[?25l");} void showCursor() {printf("\e[?25h");} #endif + +private: +#ifdef WINDOWS + void getWinCurCoord() {GetConsoleScreenBufferInfo(hOut, &csbi); ccoord = csbi.dwCursorPosition;} + COORD & getWinCoord(int dx = 0, int dy = 0) {getWinCurCoord(); ccoord.X += dx; ccoord.Y += dy; return ccoord;} +#endif + + void begin(); + void run(); + void fillLabels(); void status(); void checkColumn(uint col) {while (columns().size() < col) columns().push_back(Column(def_align));} - int bitsValue(const void * src, int offset, int count); + int bitsValue(const void * src, int offset, int count) const; const char * toBin(const void * d, int s); inline void printLine(const PIString & str, int dx = 0, PIFlags format = PIConsole::Normal); inline int printValue(const PIString & str, PIFlags format = PIConsole::Normal); @@ -191,6 +212,16 @@ private: static void key_event(char key, void * t); struct Variable { + Variable() {nx = ny = type = offset = bitFrom = bitCount = size = 0; format = Normal; remote = false; ptr = 0; id = 1;} + bool isEmpty() const {return (remote ? false : ptr == 0);} + const void * data() {return (remote ? rdata.data() : ptr);} + void writeData(PIByteArray & ba) { + if (remote) ba << rdata; + else { + if (type == 0) ba << (*(PIString * )ptr); + else ba << PIByteArray::RawData(ptr, size); + } + } PIString name; PIFlags format; int nx; @@ -199,31 +230,24 @@ private: int offset; int bitFrom; int bitCount; - union { - const PIString * s; - const bool * b; - const short * sh; - const int * i; - const long * l; - const llong * ll; - const float * f; - const double * d; - const char * c; - const uchar * uc; - const ushort * ush; - const uint * ui; - const ulong * ul; - const ullong * ull; - const void * ptr; - }; - void operator =(const Variable & src) {name = src.name; format = src.format; type = src.type; offset = src.offset; - bitFrom = src.bitFrom; bitCount = src.bitCount; ptr = src.ptr; nx = src.nx; ny = src.ny;} + int size; + int id; + bool remote; + const void * ptr; + PIByteArray rdata; + void operator =(const Variable & src) {remote = src.remote; name = src.name; format = src.format; type = src.type; offset = src.offset; size = src.size; + bitFrom = src.bitFrom; bitCount = src.bitCount; ptr = src.ptr; nx = src.nx; ny = src.ny; rdata = src.rdata;} + }; + + struct VariableContent { + int id; + PIByteArray rdata; }; struct Column { + Column(Alignment align = PIConsole::Right) {variables.reserve(16); alignment = align;} PIVector variables; Alignment alignment; - Column(Alignment align = PIConsole::Right) { alignment = align;} uint size() const {return variables.size();} Variable & operator [](int index) {return variables[index];} const Variable & operator [](int index) const {return variables[index];} @@ -232,15 +256,27 @@ private: }; struct Tab { + Tab(PIString n = "", char k = 0) {columns.reserve(16); name = n; key = k;} PIVector columns; PIString name; PIString status; char key; - Tab() {} - Tab(PIString n, char k) {name = n; key = k;} - ~Tab() {;} }; + + enum ConnectedState {Disconnected, FetchingData, Committing, Connected}; + friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::VariableContent & v); + friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::VariableContent & v); + + friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Variable & v); + friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Variable & v); + + friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Column & v); + friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Column & v); + + friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Tab & v); + friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Tab & v); + PIVector & columns() {return tabs[cur_tab].columns;} Column & column(int index) {return tabs[cur_tab].columns[index - 1];} inline int couts(const PIString & v); @@ -258,6 +294,14 @@ private: inline int couts(const ullong v); inline int couts(const float v); inline int couts(const double v); + + struct RemoteClient; + + void serverSendInfo(); + void serverSendData(); + RemoteClient & remoteClient(const PIString & fname); + EVENT_HANDLER2(void, peerReceived, const PIString &, from, const PIByteArray &, data); + EVENT_HANDLER2(void, peerTimer, void * , data, int, delim); #ifdef WINDOWS void * hOut; @@ -277,8 +321,48 @@ private: KBFunc ret_func; int width, height, pwidth, pheight, ret, col_wid, num_format; uint max_y; + int vid; uint cur_tab, col_cnt; + + PIPeer * peer; + PITimer peer_timer; + PIString server_name; + bool server_mode; + ConnectedState state; + + /*struct RemoteData { + RemoteData() {msg_count = msg_rec = msg_send = 0;} + void clear() {msg_count = msg_rec = msg_send = 0; data.clear();} + bool isEmpty() const {return msg_count == 0;} + bool isReadyRec() const {return msg_count == msg_rec;} + bool isReadySend() const {return msg_count == msg_send;} + void setData(const PIByteArray & ba) {data = ba; msg_rec = msg_send = 0; msg_count = (data.size_s() - 1) / 4096 + 1;} + PIByteArray data; + int msg_count; + int msg_rec; + int msg_send; + };*/ + + struct RemoteClient { + RemoteClient(const PIString & n = "") {name = n; state = Disconnected;} + PIString name; + ConnectedState state; + }; + + PIVector remote_clients; }; +inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::VariableContent & v) {ba << v.id << v.rdata; return ba;} +inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::VariableContent & v) {ba >> v.id; ba >> v.rdata; return ba;} + +inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Variable & v) {ba << v.name << v.id << (int)v.format << v.type << v.size << v.bitFrom << v.bitCount; return ba;} +inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Variable & v) {ba >> v.name >> v.id >> (int & )v.format >> v.type >> v.size >> v.bitFrom >> v.bitCount; return ba;} + +inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Column & v) {ba << (int)v.alignment << v.variables; return ba;} +inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Column & v) {ba >> (int & )v.alignment >> v.variables; return ba;} + +inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Tab & v) {ba << v.name << v.status << (uchar)v.key << v.columns; return ba;} +inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Tab & v) {ba >> v.name >> v.status >> (uchar&)v.key >> v.columns; return ba;} + #endif // PICONSOLE_H diff --git a/picontainers.h b/picontainers.h index 810533f8..6d8d47d0 100644 --- a/picontainers.h +++ b/picontainers.h @@ -549,7 +549,7 @@ public: bool contains(const Type & v) const {for (uint i = 0; i < _stlc::size(); ++i) if (v == at(i)) return true; return false;} #else - _CVector & enlarge(uint size_) {int ns = size_s() + size_; if (ns <= 0) _stlc::clear(); else _stlc::resize(ns); return *this;} + _CVector & enlarge(int size_) {int ns = size_s() + size_; if (ns <= 0) _stlc::clear(); else _stlc::resize(ns); return *this;} _CVector & sort(CompareFunc compare = compare_func) {qsort(&at(0), _stlc::size(), sizeof(Type), (int(*)(const void * , const void * ))compare); return *this;} _CVector & fill(const Type & t) {_stlc::assign(_stlc::size(), t); return *this;} _CVector & pop_front() {_stlc::erase(_stlc::begin()); return *this;} diff --git a/pidiagnostics.cpp b/pidiagnostics.cpp new file mode 100644 index 00000000..8953f386 --- /dev/null +++ b/pidiagnostics.cpp @@ -0,0 +1,99 @@ +/* + PIP - Platform Independent Primitives + Speed and quality in/out diagnostics + Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "pidiagnostics.h" + + +PIDiagnostics::PIDiagnostics(bool start_): PITimer() { + PITimer::reset(); + reset(); + if (start_) start(); +} + + +void PIDiagnostics::reset() { + lock(); + qual = PIDiagnostics::Unknown; + speedIn = speedOut = PIString::readableSize(0) + "/s"; + ifreq = immediate_freq = integral_freq = 0.f; + packets[0] = packets[1] = 0; + cur_pckt = rec_once = 0; + wrong_count = receive_count = send_count = 0; + packets_in_sec = packets_out_sec = bytes_in_sec = bytes_out_sec = 0; + unlock(); +} + + +void PIDiagnostics::received(int size, bool correct) { + lock(); + packets[correct ? 1 : 0]++; + rec_once = 1; + if (correct) { + float el = elapsed_s(); + if (el > 0.f) immediate_freq = ifreq = 1.f / el; + else immediate_freq = ifreq = 0.f; + PITimer::reset(); + receive_count++; + packets_in_sec++; + bytes_in_sec += size; + } else { + immediate_freq = ifreq = 0.f; + wrong_count++; + } + unlock(); +} + + +void PIDiagnostics::sended(int size) { + lock(); + send_count++; + packets_out_sec++; + bytes_out_sec += size; + unlock(); +} + + +void PIDiagnostics::tick(void * data, int delimiter) { + lock(); + PIDiagnostics::Quality diag; + float fdel = 1. / (interval() / 1000.); + immediate_freq = ifreq; + ifreq = 0.f; + speedIn = PIString::readableSize(bytes_in_sec * fdel) + "/s"; + speedOut = PIString::readableSize(bytes_out_sec * fdel) + "/s"; + bytes_in_sec = bytes_out_sec = packets_in_sec = packets_out_sec = 0; + int arc = packets[0] + packets[1]; + float good_percents = 0.f; + if (arc > 0) good_percents = (float)packets[1] / arc * 100.f; + integral_freq = packets[1] * fdel; + if (rec_once == 0) { + diag = PIDiagnostics::Unknown; + } else { + if (good_percents == 0.f) diag = PIDiagnostics::Failure; + else if (good_percents <= 20.f) diag = PIDiagnostics::Bad; + else if (good_percents > 20.f && good_percents <= 80.f) diag = PIDiagnostics::Average; + else diag = PIDiagnostics::Good; + if (diag != qual) { + qualityChanged(diag, qual); + qual = diag; + } + } + packets[0] = packets[1] = 0; + unlock(); +} diff --git a/pidiagnostics.h b/pidiagnostics.h new file mode 100644 index 00000000..993bac9d --- /dev/null +++ b/pidiagnostics.h @@ -0,0 +1,85 @@ +/* + PIP - Platform Independent Primitives + Speed and quality in/out diagnostics + Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PIDIAGNOSTICS_H +#define PIDIAGNOSTICS_H + +#include "pitimer.h" + + +class PIP_EXPORT PIDiagnostics: private PITimer +{ + PIOBJECT(PIDiagnostics) +public: + PIDiagnostics(bool start_ = true); + virtual ~PIDiagnostics() {;} + + enum Quality {Unknown = 1, Failure = 2, Bad = 3, Average = 4, Good = 5}; + + EVENT_HANDLER0(void, start) {start(1000.);} + EVENT_HANDLER1(void, start, double, msecs) {if (msecs > 0.) PITimer::start(msecs);} + EVENT_HANDLER0(void, reset); + + EVENT_HANDLER1(void, received, int, size) {received(size, true);} + EVENT_HANDLER2(void, received, int, size, bool, correct); + EVENT_HANDLER1(void, sended, int, size); + + float immediateFrequency() const {return immediate_freq;} + float integralFrequency() const {return integral_freq;} + ullong receiveCountPerSec() const {return packets_in_sec;} + ullong sendCountPerSec() const {return packets_out_sec;} + ullong receiveBytesPerSec() const {return bytes_in_sec;} + ullong sendBytesPerSec() const {return bytes_out_sec;} + ullong receiveCount() const {return receive_count;} + ullong wrongCount() const {return wrong_count;} + ullong sendCount() const {return send_count;} + PIDiagnostics::Quality quality() const {return qual;} + PIString receiveSpeed() const {return speedIn;} + PIString sendSpeed() const {return speedOut;} + + const float * immediateFrequency_ptr() const {return &immediate_freq;} + const float * integralFrequency_ptr() const {return &integral_freq;} + const ullong * receiveCountPerSec_ptr() const {return &packets_in_sec;} + const ullong * sendCountPerSec_ptr() const {return &packets_out_sec;} + const ullong * receiveBytesPerSec_ptr() const {return &bytes_in_sec;} + const ullong * sendBytesPerSec_ptr() const {return &bytes_out_sec;} + const ullong * receiveCount_ptr() const {return &receive_count;} + const ullong * wrongCount_ptr() const {return &wrong_count;} + const ullong * sendCount_ptr() const {return &send_count;} + const int * quality_ptr() const {return (int * )&qual;} + const PIString * receiveSpeed_ptr() const {return &speedIn;} + const PIString * sendSpeed_ptr() const {return &speedOut;} + + EVENT2(qualityChanged, PIDiagnostics::Quality, new_quality, PIDiagnostics::Quality, old_quality) + +private: + void tick(void * data, int delimiter); + void changeDisconnectTimeout(); + + PIDiagnostics::Quality qual; + PIString speedIn, speedOut; + float ifreq, immediate_freq, integral_freq; + int packets[2]; + char cur_pckt, rec_once; + ullong wrong_count, receive_count, send_count; + ullong packets_in_sec, packets_out_sec, bytes_in_sec, bytes_out_sec; + +}; + +#endif // PIDIAGNOSTICS_H diff --git a/piethernet.cpp b/piethernet.cpp index 32f5b2bc..cd4c260d 100644 --- a/piethernet.cpp +++ b/piethernet.cpp @@ -83,7 +83,7 @@ bool PIEthernet::init() { closeSocket(sock); int st = 0, pr = 0;; #ifdef WINDOWS - int flags = 0; + int flags = WSA_FLAG_OVERLAPPED; #else int so = 1; #endif @@ -96,18 +96,21 @@ bool PIEthernet::init() { } #ifdef WINDOWS if (type_ == UDP) flags = WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF; - if (params[ReuseAddress]) flags |= WSA_FLAG_OVERLAPPED; sock = WSASocket(AF_INET, st, pr, NULL, 0, flags); #else sock = socket(AF_INET, st, pr); #endif if (sock == -1) { - piCout << "[PIEthernet] Cant`t create socket, " << ethErrorString(); + piCoutObj << "[PIEthernet] Can`t create socket, " << ethErrorString(); return false; } #ifndef WINDOWS if (params[PIEthernet::ReuseAddress]) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &so, sizeof(so)); if (params[PIEthernet::Broadcast]) setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so, sizeof(so)); +#else + BOOL bv = TRUE; + if (params[PIEthernet::ReuseAddress]) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char * )&bv, sizeof(bv)); + setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (const char * )&bv, sizeof(bv)); #endif //cout << "inited " << sock << ": bc = " << params << endl; //fcntl(sock, F_SETFL, 0/*O_NONBLOCK*/); @@ -128,7 +131,7 @@ bool PIEthernet::openDevice() { parseAddress(path_, &ip_, &port_); if (type_ != UDP) return true; - //cout << " bind to " << sock << ": " << (params[PIEthernet::Broadcast] ? "0.0.0.0" : path_) << ": " << port_ << " ..." <index; + //piCout << "join group" << group << "ip" << ip_ << "with index" << mreq.imr_ifindex; + mreq.imr_multiaddr.s_addr = inet_addr(group.data()); + int so = 1; + //setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so, sizeof(so)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &so, sizeof(so)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)); + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) { + piCoutObj << "[PIEthernet] Can`t join multicast group " << group << ", " << ethErrorString(); return false; } # endif @@ -218,7 +229,7 @@ bool PIEthernet::leaveMulticastGroup(const PIString & group) { if (sock == -1) init(); if (sock == -1) return false; if (type_ != UDP) { - piCout << "[PIEthernet] Only UDP sockets can leave multicast groups"; + piCoutObj << "[PIEthernet] Only UDP sockets can leave multicast groups"; return false; } #ifdef WINDOWS @@ -237,7 +248,7 @@ bool PIEthernet::leaveMulticastGroup(const PIString & group) { mreq.imr_multiaddr.s_addr = inet_addr(group.data()); mreq.imr_ifindex = 0; if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { - piCout << "[PIEthernet] Cant`t leave multicast group " << group << ", " << ethErrorString(); + piCoutObj << "[PIEthernet] Can`t leave multicast group " << group << ", " << ethErrorString(); return false; } #endif @@ -256,10 +267,10 @@ bool PIEthernet::connect() { #ifdef QNX addr_.sin_len = sizeof(addr_); #endif - //piCout << "[PIEthernet] connect to " << ip << ":" << port_; + //piCoutObj << "[PIEthernet] connect to " << ip << ":" << port_; connected_ = (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) == 0); if (!connected_) - piCout << "[PIEthernet] Cant`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString(); + piCoutObj << "[PIEthernet] Can`t connect to " << ip_ << ":" << port_ << ", " << ethErrorString(); opened_ = connected_; if (connected_) connected(); return connected_; @@ -283,14 +294,14 @@ bool PIEthernet::listen() { tries++; } if (tries == 10) { - piCout << "[PIEthernet] Cant`t bind to " << ip_ << ":" << port_ << ", " << ethErrorString(); + piCoutObj << "[PIEthernet] Can`t bind to " << ip_ << ":" << port_ << ", " << ethErrorString(); return false; } if (::listen(sock, 64) == -1) { - piCout << "[PIEthernet] Can`t listen on "<< ip_ << ":" << port_ << ", " << ethErrorString(); + piCoutObj << "[PIEthernet] Can`t listen on "<< ip_ << ":" << port_ << ", " << ethErrorString(); return false; } - //piCout << "[PIEthernet] listen on " << ip_ << ":" << port_; + //piCoutObj << "[PIEthernet] listen on " << ip_ << ":" << port_; server_thread_.start(server_func); return true; } @@ -303,13 +314,14 @@ int PIEthernet::read(void * read_to, int max_size) { int rs = 0, s = 0; sockaddr_in client_addr; socklen_t slen = sizeof(client_addr); - //piCout << "[PIEthernet] read from " << ip_ << ":" << port_ << endl; + //piCoutObj << "[PIEthernet] read from " << ip_ << ":" << port_ << endl; switch (type_) { case TCP_SingleTCP: ::listen(sock, 64); s = accept(sock, (sockaddr * )&client_addr, &slen); if (s == -1) { - piCout << "[PIEthernet] Cant`t accept new connection, " << ethErrorString(); + //piCoutObj << "[PIEthernet] Can`t accept new connection, " << ethErrorString(); + msleep(1); return -1; } rs = recv(s, (char * )read_to, max_size, 0); @@ -323,11 +335,11 @@ int PIEthernet::read(void * read_to, int max_size) { #else rs = recv(sock, read_to, max_size, 0); #endif - piCout << "eth" << path_ << "read return" << rs << errno; - if (rs <= 0) { + //piCout << "eth" << path_ << "read return" << rs << errno; + if (rs <= 0 && type_ == TCP_Client) { connected_ = false; disconnected(rs < 0); - piCout << "eth" << path_ << "disconnected"; + //piCoutObj << "eth" << path_ << "disconnected"; } if (rs > 0) received(read_to, rs); return rs; @@ -342,10 +354,10 @@ int PIEthernet::read(void * read_to, int max_size) { int PIEthernet::write(const void * data, int max_size) { if (sock == -1) init(); if (sock == -1 || !isWriteable()) { - //piCout << "[PIEthernet] Can`t send to uninitialized socket"; + //piCoutObj << "[PIEthernet] Can`t send to uninitialized socket"; return -1; } - //piCout << "[PIEthernet] sending to " << ip_s << ":" << port_s << " " << max_size << " bytes"; + //piCoutObj << "[PIEthernet] sending to " << ip_s << ":" << port_s << " " << max_size << " bytes"; int ret = 0; switch (type_) { case TCP_SingleTCP: @@ -356,21 +368,22 @@ int PIEthernet::write(const void * data, int max_size) { #ifdef QNX addr_.sin_len = sizeof(addr_); #endif - //piCout << "connect SingleTCP" << ip_s << ":" << port_s << "..."; + //piCoutObj << "connect SingleTCP" << ip_s << ":" << port_s << "..."; if (::connect(sock, (sockaddr * )&addr_, sizeof(addr_)) != 0) { - piCout << "[PIEthernet] Cant`t connect to " << ip_s << ":" << port_s << ", " << ethErrorString(); + //piCoutObj << "[PIEthernet] Can`t connect to " << ip_s << ":" << port_s << ", " << ethErrorString(); + msleep(1); return -1; } - //piCout << "ok, write SingleTCP" << int(data) << max_size << "bytes ..."; + //piCoutObj << "ok, write SingleTCP" << int(data) << max_size << "bytes ..."; ret = ::send(sock, (const char *)data, max_size, 0); - //piCout << "ok, ret" << ret; + //piCoutObj << "ok, ret" << ret; closeSocket(sock); init(); return ret; case UDP: saddr_.sin_port = htons(port_s); - if (params[PIEthernet::Broadcast]) saddr_.sin_addr.s_addr = INADDR_BROADCAST; - else saddr_.sin_addr.s_addr = inet_addr(ip_s.data()); + /*if (params[PIEthernet::Broadcast]) saddr_.sin_addr.s_addr = INADDR_BROADCAST; + else*/ saddr_.sin_addr.s_addr = inet_addr(ip_s.data()); saddr_.sin_family = AF_INET; #ifdef WINDOWS return sendto(sock, (const char * )data, max_size, 0, (sockaddr * )&saddr_, sizeof(saddr_)); @@ -391,7 +404,7 @@ void PIEthernet::server_func(void * eth) { socklen_t slen = sizeof(client_addr); int s = accept(ce->sock, (sockaddr * )&client_addr, &slen); if (s == -1) { - piCout << "[PIEthernet] Cant`t accept new connection, " << ethErrorString(); + if (ce->debug_) piCout << "[PIEthernet] Can`t accept new connection, " << ethErrorString(); return; } PIString ip(inet_ntoa(client_addr.sin_addr)); @@ -406,19 +419,60 @@ void PIEthernet::server_func(void * eth) { } -PIStringList PIEthernet::interfaces() { +PIEthernet::InterfaceList PIEthernet::interfaces() { #ifdef WINDOWS - piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" instead"; - return PIStringList(); -#else -# ifdef QNX - PIStringList il, sl; - /*struct if_nameindex * ni = if_nameindex(); - for (int i = 0; ; ++i) { - if (ni[i].if_name == 0 || ni[i].if_index == 0) break; - sl << PIString(ni[i].if_name); + PIEthernet::InterfaceList il; + Interface ci; + PIP_ADAPTER_INFO pAdapterInfo, pAdapter = 0; + int ret = 0; + ulong ulOutBufLen = sizeof(IP_ADAPTER_INFO); + pAdapterInfo = (IP_ADAPTER_INFO * ) HeapAlloc(GetProcessHeap(), 0, (sizeof (IP_ADAPTER_INFO))); + if (pAdapterInfo == 0) { + piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo"; + return il; } - if_freenameindex(ni);*/ + if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { + HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); + pAdapterInfo = (IP_ADAPTER_INFO *) HeapAlloc(GetProcessHeap(), 0, (ulOutBufLen)); + if (pAdapterInfo == 0) { + piCout << "[PIEthernet] Error allocating memory needed to call GetAdaptersinfo"; + return il; + } + } + if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { + pAdapter = pAdapterInfo; + while (pAdapter) { + ci.name = PIString(pAdapter->AdapterName); + ci.index = pAdapter->Index; + ci.address = PIString(pAdapter->IpAddressList.IpAddress.String); + if (ci.address == "0.0.0.0") { + pAdapter = pAdapter->Next; + continue; + } + ci.mac = macFromBytes(PIByteArray(pAdapter->Address, pAdapter->AddressLength)); + ci.netmask = PIString(pAdapter->IpAddressList.IpMask.String); + ci.flags = PIEthernet::ifActive | PIEthernet::ifRunning; + //if (ret->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast; + //if (ret->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast; + if (pAdapter->Type == MIB_IF_TYPE_PPP) ci.flags |= PIEthernet::ifPTP; + if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) ci.flags |= PIEthernet::ifLoopback; + ci.broadcast.clear(); + ci.ptp.clear(); + /*if (ci.flags[PIEthernet::ifBroadcast]) + ci.broadcast = getSockAddr(ret->ifa_broadaddr); + if (ci.flags[PIEthernet::ifPTP]) + ci.ptp = getSockAddr(ret->ifa_dstaddr);*/ + il << ci; + pAdapter = pAdapter->Next; + } + } else + piCout << "[PIEthernet] GetAdaptersInfo failed with error: " << ret; + if (pAdapterInfo) + HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); + return il; +#else +/*# ifdef QNX + PIStringList il, sl; PIProcess proc; proc.setGrabOutput(true); proc.exec(ifconfigPath.c_str(), "-l"); @@ -436,38 +490,70 @@ PIStringList PIEthernet::interfaces() { for (int j = 0; j < al; ++j) sl << i.trimmed() + ":" + PIString::fromNumber(j); } - //cout << out; - //cout << sl << endl; return sl; # else PIStringList sl; - /*struct if_nameindex * ni = if_nameindex(); - for (int i = 0; ; ++i) { - if (ni[i].if_name == 0 || ni[i].if_index == 0) break; - sl << PIString(ni[i].if_name); - } - if_freenameindex(ni);*/ PIProcess proc; proc.setGrabOutput(true); proc.exec(ifconfigPath.c_str(), "-s"); if (!proc.waitForFinish(1000)) return sl; PIString out(proc.readOutput()); - //cout << out << endl; out.cutLeft(out.find('\n') + 1); while (!out.isEmpty()) { sl << out.left(out.find(' ')); out.cutLeft(out.find('\n') + 1); } - //cout << sl << endl; return sl; -# endif +# endif*/ + PIEthernet::InterfaceList il; + Interface ci; + struct ifaddrs * ret; + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (getifaddrs(&ret) == 0) { + while (ret != 0) { + if (ret->ifa_addr->sa_family != AF_INET) { + ret = ret->ifa_next; + continue; + } + ci.name = PIString(ret->ifa_name); + ci.address = getSockAddr(ret->ifa_addr); + ci.netmask = getSockAddr(ret->ifa_netmask); + ci.mac.clear(); + if (s != -1) { + struct ifreq ir; + strcpy(ir.ifr_name, ret->ifa_name); + if (ioctl(s, SIOCGIFHWADDR, &ir) == 0) + ci.mac = macFromBytes(PIByteArray(ir.ifr_hwaddr.sa_data, 6)); + } + ci.flags = 0; + if (ret->ifa_flags & IFF_UP) ci.flags |= PIEthernet::ifActive; + if (ret->ifa_flags & IFF_RUNNING) ci.flags |= PIEthernet::ifRunning; + if (ret->ifa_flags & IFF_BROADCAST) ci.flags |= PIEthernet::ifBroadcast; + if (ret->ifa_flags & IFF_MULTICAST) ci.flags |= PIEthernet::ifMulticast; + if (ret->ifa_flags & IFF_LOOPBACK) ci.flags |= PIEthernet::ifLoopback; + if (ret->ifa_flags & IFF_POINTOPOINT) ci.flags |= PIEthernet::ifPTP; + ci.broadcast.clear(); + ci.ptp.clear(); + if (ci.flags[PIEthernet::ifBroadcast]) + ci.broadcast = getSockAddr(ret->ifa_broadaddr); + if (ci.flags[PIEthernet::ifPTP]) + ci.ptp = getSockAddr(ret->ifa_dstaddr); + ci.index = if_nametoindex(ret->ifa_name); + il << ci; + ret = ret->ifa_next; + } + freeifaddrs(ret); + } else + piCout << "[PIEthernet] Can`t get interfaces:" << errorString(); + if (s != -1) ::close(s); + return il; #endif } PIString PIEthernet::interfaceAddress(const PIString & interface_) { #ifdef WINDOWS - piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" instead"; + piCout << "[PIEthernet] Not implemented on Windows, use \"PIEthernet::allAddresses\" or \"PIEthernet::interfaces\" instead"; return PIString(); #else struct ifreq ifr; @@ -484,7 +570,7 @@ PIString PIEthernet::interfaceAddress(const PIString & interface_) { PIStringList PIEthernet::allAddresses() { -#ifdef WINDOWS +/*#ifdef WINDOWS PIStringList al; PIString ca; PIP_ADAPTER_INFO pAdapterInfo, pAdapter = 0; @@ -506,10 +592,6 @@ PIStringList PIEthernet::allAddresses() { if ((ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { pAdapter = pAdapterInfo; while (pAdapter) { - /*if (pAdapter->Type != MIB_IF_TYPE_ETHERNET && pAdapter->Type != MIB_IF_TYPE_LOOPBACK) { - pAdapter = pAdapter->Next; - continue; - }*/ ca = PIString(pAdapter->IpAddressList.IpAddress.String); if (ca != "0.0.0.0") al << ca; pAdapter = pAdapter->Next; @@ -519,10 +601,11 @@ PIStringList PIEthernet::allAddresses() { if (pAdapterInfo) HeapFree(GetProcessHeap(), 0, (pAdapterInfo)); return al; -#else - PIStringList il = interfaces(), al; - piForeachC (PIString & i, il) - al << interfaceAddress(i); +#else*/ + PIEthernet::InterfaceList il = interfaces(); + PIStringList al; + piForeachC (PIEthernet::Interface & i, il) + al << i.address; return al.removeStrings("0.0.0.0"); -#endif +//#endif } diff --git a/piethernet.h b/piethernet.h index c75d3fa0..ab7757e0 100644 --- a/piethernet.h +++ b/piethernet.h @@ -46,8 +46,10 @@ public: void setSendAddress(const PIString & ip_port) {parseAddress(ip_port, &ip_s, &port_s);} void setSendIP(const PIString & ip) {ip_s = ip;} void setSendPort(int port) {port_s = port;} + PIString readAddress() {return path_;} PIString readIP() {parseAddress(path_, &ip_, &port_); return ip_;} int readPort() {parseAddress(path_, &ip_, &port_); return port_;} + PIString sendAddress() {return ip_s + ":" + PIString::fromNumber(port_s);} PIString sendIP() {return ip_s;} int sendPort() {return port_s;} @@ -83,14 +85,57 @@ public: int read(void * read_to, int max_size); int write(const void * data, int max_size); int write(const PIByteArray & data) {return write(data.data(), data.size_s());} - - static PIStringList interfaces(); - static PIString interfaceAddress(const PIString & interface_); - static PIStringList allAddresses(); EVENT1(newConnection, PIEthernet * , client) EVENT0(connected) EVENT1(disconnected, bool, withError) + + enum InterfaceFlag { + ifActive = 0x1, + ifRunning = 0x2, + ifBroadcast = 0x4, + ifMulticast = 0x8, + ifLoopback = 0x10, + ifPTP = 0x20 + }; + + typedef PIFlags InterfaceFlags; + + struct Interface { + int index; + PIString name; + PIString mac; + PIString address; + PIString netmask; + PIString broadcast; + PIString ptp; + InterfaceFlags flags; + bool isActive() const {return flags[PIEthernet::ifActive];} + bool isRunning() const {return flags[PIEthernet::ifRunning];} + bool isBroadcast() const {return flags[PIEthernet::ifBroadcast];} + bool isMulticast() const {return flags[PIEthernet::ifMulticast];} + bool isLoopback() const {return flags[PIEthernet::ifLoopback];} + bool isPTP() const {return flags[PIEthernet::ifPTP];} + }; + + class InterfaceList: public PIVector { + public: + InterfaceList(): PIVector() {} + const Interface * getByIndex(int index) const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].index == index) return &((*this)[i]); return 0;} + const Interface * getByName(const PIString & name) const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].name == name) return &((*this)[i]); return 0;} + const Interface * getByAddress(const PIString & address) const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].address == address) return &((*this)[i]); return 0;} + const Interface * getLoopback() const {for (int i = 0; i < size_s(); ++i) if ((*this)[i].isLoopback()) return &((*this)[i]); return 0;} + }; + + static InterfaceList interfaces(); + static PIString interfaceAddress(const PIString & interface_); + static PIStringList allAddresses(); + + static void parseAddress(const PIString & ipp, PIString * ip, int * port); + static PIString macFromBytes(const PIByteArray & mac) {PIString r; for (int i = 0; i < mac.size_s(); ++i) {r += PIString::fromNumber(mac[i], 16).expandLeftTo(2, '0'); if (i < mac.size_s() - 1) r += ":";} return r;} + static PIByteArray macToBytes(const PIString & mac) {PIByteArray r; PIStringList sl = mac.split(":"); piForeachC (PIString & i, sl) r << uchar(i.toInt(16)); return r;} + static PIString applyMask(const PIString & ip, const PIString & mask) {struct in_addr ia; ia.s_addr = inet_addr(ip.data()) & inet_addr(mask.data()); return PIString(inet_ntoa(ia));} + static PIString getBroadcast(const PIString & ip, const PIString & mask) {struct in_addr ia; ia.s_addr = inet_addr(ip.data()) | ~inet_addr(mask.data()); return PIString(inet_ntoa(ia));} protected: PIEthernet(int sock, PIString ip_port); @@ -101,11 +146,12 @@ protected: bool openDevice(); bool closeDevice(); #ifdef WINDOWS - void closeSocket(int & sd) {if (sd != -1) closesocket(sd); sd = -1;} + void closeSocket(int & sd) {if (sd != -1) {shutdown(sd, SD_BOTH); closesocket(sd);} sd = -1;} #else - void closeSocket(int & sd) {if (sd != -1) ::close(sd); sd = -1;} + void closeSocket(int & sd) {if (sd != -1) {shutdown(sock, SHUT_RDWR); ::close(sd);} sd = -1;} + static PIString getSockAddr(sockaddr * s) {return s == 0 ? PIString() : PIString(inet_ntoa(((sockaddr_in*)s)->sin_addr));} #endif - void parseAddress(const PIString & ipp, PIString * ip, int * port); + int sock, sock_s, port_, port_s, port_c, wrote; bool connected_; @@ -125,10 +171,10 @@ private: static std::string ethErrorString() { #ifdef WINDOWS - char * msg; - int err = WSAGetLastError(); - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); - return "code " + itos(err) + " - " + string(msg); + char * msg; + int err = WSAGetLastError(); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); + return "code " + itos(err) + " - " + string(msg); #else return errorString(); #endif diff --git a/pievaluator.cpp b/pievaluator.cpp index 049f9ce9..2818231d 100644 --- a/pievaluator.cpp +++ b/pievaluator.cpp @@ -50,6 +50,12 @@ PIEvaluatorContent::PIEvaluatorContent() { addFunction("sign", 1); addFunction("rad", 1); addFunction("deg", 1); + addFunction("j0", 1); + addFunction("j1", 1); + addFunction("jn", 2); + addFunction("y0", 1); + addFunction("y1", 1); + addFunction("yn", 2); clearCustomVariables(); //addVariable("n", 0.); //addVariable("x1", 123); @@ -126,6 +132,12 @@ PIEvaluatorTypes::BaseFunctions PIEvaluatorContent::getBaseFunction(const PIStri if (name == "sign") return PIEvaluatorTypes::bfSign; if (name == "rad") return PIEvaluatorTypes::bfRad; if (name == "deg") return PIEvaluatorTypes::bfDeg; + if (name == "j0") return PIEvaluatorTypes::bfJ0; + if (name == "j1") return PIEvaluatorTypes::bfJ1; + if (name == "jn") return PIEvaluatorTypes::bfJN; + if (name == "y0") return PIEvaluatorTypes::bfY0; + if (name == "y1") return PIEvaluatorTypes::bfY1; + if (name == "yn") return PIEvaluatorTypes::bfYN; return PIEvaluatorTypes::bfUnknown; } @@ -957,6 +969,24 @@ inline void PIEvaluator::execFunction(const PIEvaluatorTypes::Instruction & ci) case PIEvaluatorTypes::bfDeg: tmpvars[oi].value = value(ci.operators[0]) * complexd(rad2deg, 0.); break; + case PIEvaluatorTypes::bfJ0: + tmpvars[oi].value = j0(value(ci.operators[0]).real()); + break; + case PIEvaluatorTypes::bfJ1: + tmpvars[oi].value = j1(value(ci.operators[0]).real()); + break; + case PIEvaluatorTypes::bfJN: + tmpvars[oi].value = jn(round(value(ci.operators[1]).real()), value(ci.operators[0]).real()); + break; + case PIEvaluatorTypes::bfY0: + tmpvars[oi].value = y0(value(ci.operators[0]).real()); + break; + case PIEvaluatorTypes::bfY1: + tmpvars[oi].value = y1(value(ci.operators[0]).real()); + break; + case PIEvaluatorTypes::bfYN: + tmpvars[oi].value = yn(round(value(ci.operators[1]).real()), value(ci.operators[0]).real()); + break; case PIEvaluatorTypes::bfRandom: tmp = static_cast(rand()) / RAND_MAX; stmp = value(ci.operators[1]) - value(ci.operators[0]); diff --git a/pievaluator.h b/pievaluator.h index 38c4500b..40b5e07d 100644 --- a/pievaluator.h +++ b/pievaluator.h @@ -36,7 +36,8 @@ namespace PIEvaluatorTypes { bfSqrt, bfSqr, bfPow, bfAbs, bfLn, bfLg, bfLog, bfSign, bfIm, bfRe, bfArg, bfLen, bfConj, - bfRad, bfDeg}; + bfRad, bfDeg, bfJ0, bfJ1, bfJN, + bfY0, bfY1, bfYN}; struct Instruction { Instruction() {;} diff --git a/pifile.cpp b/pifile.cpp index 4587418f..3f7c9bd2 100644 --- a/pifile.cpp +++ b/pifile.cpp @@ -104,7 +104,7 @@ void PIFile::resize(llong new_size, char fill_) { delete[] buff; return; } - piCout << "[PIFile] Downsize is not support yet :-("; + piCoutObj << "[PIFile] Downsize is not support yet :-("; } diff --git a/pifile.h b/pifile.h index 8133c03b..77711f62 100644 --- a/pifile.h +++ b/pifile.h @@ -36,7 +36,7 @@ public: //PIFile & operator =(const PIFile & f) {path_ = f.path_; type_ = f.type_; return *this;} - void flush() {if (!opened_) fflush(fd);} + void flush() {if (opened_) fflush(fd);} EVENT_HANDLER(void, clear) {close(); fd = fopen(path_.data(), "w"); if (fd != 0) fclose(fd); fd = 0; opened_ = false; open();} void seek(llong position) {if (!opened_) return; fseek(fd, position, SEEK_SET); clearerr(fd);} void seekToBegin() {if (!opened_) return; fseek(fd, 0, SEEK_SET); clearerr(fd);} @@ -79,34 +79,34 @@ public: PIFile & operator =(const PIFile & f) {path_ = f.path_; mode_ = f.mode_; return *this;} - PIFile & operator <<(const char v) {if (!isWriteable()) return *this; write(&v, 1); return *this;} + PIFile & operator <<(const char v) {if (canWrite() && fd != 0) write(&v, 1); return *this;} //PIFile & operator <<(const string & v) {write(v.c_str(), v.size()); return *this;} - PIFile & operator <<(const PIString & v) {if (!isWriteable()) return *this; write(v.data(), v.lengthAscii()); return *this;} - PIFile & operator <<(const PIByteArray & v) {if (!isWriteable()) return *this; write(v.data(), v.size()); return *this;} - PIFile & operator <<(short v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%hd", v); return *this;} - PIFile & operator <<(int v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%d", v); return *this;} - PIFile & operator <<(long v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%ld", v); return *this;} - PIFile & operator <<(llong v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%lld", v); return *this;} - PIFile & operator <<(uchar v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%u", int(v)); return *this;} - PIFile & operator <<(ushort v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%hu", v); return *this;} - PIFile & operator <<(uint v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%u", v); return *this;} - PIFile & operator <<(ulong v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%lu", v); return *this;} - PIFile & operator <<(ullong v) {if (!isWriteable()) return *this; ret = fprintf(fd, "%llu", v); return *this;} - PIFile & operator <<(float v) {if (!isWriteable()) return *this; ret = fprintf(fd, ("%" + prec_str + "f").c_str(), v); return *this;} - PIFile & operator <<(double v) {if (!isWriteable()) return *this; ret = fprintf(fd, ("%" + prec_str + "lf").c_str(), v); return *this;} + PIFile & operator <<(const PIString & v) {if (canWrite() && fd != 0) write(v.data(), v.lengthAscii()); return *this;} + PIFile & operator <<(const PIByteArray & v) {if (canWrite() && fd != 0) write(v.data(), v.size()); return *this;} + PIFile & operator <<(short v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%hd", v); return *this;} + PIFile & operator <<(int v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%d", v); return *this;} + PIFile & operator <<(long v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%ld", v); return *this;} + PIFile & operator <<(llong v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%lld", v); return *this;} + PIFile & operator <<(uchar v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%u", int(v)); return *this;} + PIFile & operator <<(ushort v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%hu", v); return *this;} + PIFile & operator <<(uint v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%u", v); return *this;} + PIFile & operator <<(ulong v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%lu", v); return *this;} + PIFile & operator <<(ullong v) {if (canWrite() && fd != 0) ret = fprintf(fd, "%llu", v); return *this;} + PIFile & operator <<(float v) {if (canWrite() && fd != 0) ret = fprintf(fd, ("%" + prec_str + "f").c_str(), v); return *this;} + PIFile & operator <<(double v) {if (canWrite() && fd != 0) ret = fprintf(fd, ("%" + prec_str + "lf").c_str(), v); return *this;} - PIFile & operator >>(char & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%hhn", &v); return *this;} - PIFile & operator >>(short & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%hn", &v); return *this;} - PIFile & operator >>(int & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%n", &v); return *this;} - PIFile & operator >>(long & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%ln", &v); return *this;} - PIFile & operator >>(llong & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%lln", &v); return *this;} - PIFile & operator >>(uchar & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%hhn", &v); return *this;} - PIFile & operator >>(ushort & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%hn", &v); return *this;} - PIFile & operator >>(uint & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%n", &v); return *this;} - PIFile & operator >>(ulong & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%ln", &v); return *this;} - PIFile & operator >>(ullong & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%lln", &v); return *this;} - PIFile & operator >>(float & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%f", &v); return *this;} - PIFile & operator >>(double & v) {if (!isWriteable()) return *this; ret = fscanf(fd, "%lf", &v); return *this;} + PIFile & operator >>(char & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hhn", &v); return *this;} + PIFile & operator >>(short & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hn", &v); return *this;} + PIFile & operator >>(int & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%n", &v); return *this;} + PIFile & operator >>(long & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%ln", &v); return *this;} + PIFile & operator >>(llong & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%lln", &v); return *this;} + PIFile & operator >>(uchar & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hhn", &v); return *this;} + PIFile & operator >>(ushort & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%hn", &v); return *this;} + PIFile & operator >>(uint & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%n", &v); return *this;} + PIFile & operator >>(ulong & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%ln", &v); return *this;} + PIFile & operator >>(ullong & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%lln", &v); return *this;} + PIFile & operator >>(float & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%f", &v); return *this;} + PIFile & operator >>(double & v) {if (canRead() && fd != 0) ret = fscanf(fd, "%lf", &v); return *this;} static PIFile openTemporary(PIIODevice::DeviceMode mode = PIIODevice::ReadWrite) {return PIFile(PIString(tmpnam(0)), mode);} static bool isExists(const PIString & path); diff --git a/piincludes.cpp b/piincludes.cpp index 7ddab3c7..1754eb60 100644 --- a/piincludes.cpp +++ b/piincludes.cpp @@ -18,7 +18,7 @@ */ #include "piincludes.h" -#include "pimutex.h" +#include "piconsole.h" bool isPIInit = false; bool piDebug = true; @@ -53,18 +53,167 @@ PIMutex __PICout_mutex__; */ -PICout::PICout(PIFlags controls): fo_(true), cc_(false), co_(controls) { +#ifdef WINDOWS +void * PICout::hOut = 0; +WORD PICout::dattr = 0; +DWORD PICout::smode = 0; +#endif + + +PICout::PICout(PIFlags controls): fo_(true), cc_(false), fc_(false), cnb_(10), co_(controls) { +#ifdef WINDOWS + if (hOut == 0) { + hOut = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO sbi; + GetConsoleScreenBufferInfo(hOut, &sbi); + dattr = sbi.wAttributes; + } + attr_ = dattr; +#endif __PICout_mutex__.lock(); } PICout::~PICout() { + if (fc_) applyFormat(PICoutManipulators::Default); if (cc_) return; newLine(); __PICout_mutex__.unlock(); } +PICout PICout::operator<<(const PICoutAction v) { +#ifdef WINDOWS + CONSOLE_SCREEN_BUFFER_INFO sbi; + COORD coord; + CONSOLE_CURSOR_INFO curinfo; +#endif + switch (v) { + case PICoutManipulators::Flush: std::cout << std::flush; break; + case PICoutManipulators::Backspace: +#ifdef WINDOWS + GetConsoleScreenBufferInfo(hOut, &sbi); + coord = sbi.dwCursorPosition; + coord.X = piMax(0, int(coord.X) - 1); + SetConsoleCursorPosition(hOut, coord); + printf(" "); + SetConsoleCursorPosition(hOut, coord); +#else + printf("\e[1D \e[1D"); +#endif + break; + case PICoutManipulators::ShowCursor: +#ifdef WINDOWS + GetConsoleCursorInfo(hOut, &curinfo); + curinfo.bVisible = true; + SetConsoleCursorInfo(hOut, &curinfo); +#else + printf("\e[?25h"); +#endif + break; + case PICoutManipulators::HideCursor: +#ifdef WINDOWS + CONSOLE_CURSOR_INFO curinfo; + GetConsoleCursorInfo(hOut, &curinfo); + curinfo.bVisible = false; + SetConsoleCursorInfo(hOut, &curinfo); +#else + printf("\e[?25l"); +#endif + break; + default: break; + }; + return *this; +} + + +#define PINUMERICCOUT if (cnb_ == 10) std::cout << v; else std::cout << PIString::fromNumber(v, cnb_); + +PICout PICout::operator <<(const uchar v) {space(); if (cnb_ == 10) std::cout << ushort(v); else std::cout << PIString::fromNumber(v, cnb_); return *this;} + +PICout PICout::operator <<(const short int v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const ushort v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const int v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const uint v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const long v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const ulong v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const llong v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const ullong v) {space(); PINUMERICCOUT return *this;} + +PICout PICout::operator <<(const float v) {space(); std::cout << v; return *this;} + +PICout PICout::operator <<(const double v) {space(); std::cout << v; return *this;} + +#undef PINUMERICCOUT + + +void PICout::applyFormat(PICoutFormat f) { + fc_ = true; +#ifdef WINDOWS + static int mask_fore = ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + static int mask_back = ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + switch (f) { + case Bin: case Oct: case Dec: case Hex: break; + case PICoutManipulators::Bold: attr_ |= FOREGROUND_INTENSITY; break; + case PICoutManipulators::Underline: attr_ |= COMMON_LVB_UNDERSCORE; break; + case PICoutManipulators::Black: attr_ = (attr_ & mask_fore); break; + case PICoutManipulators::Red: attr_ = (attr_ & mask_fore) | FOREGROUND_RED; break; + case PICoutManipulators::Green: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN; break; + case PICoutManipulators::Blue: attr_ = (attr_ & mask_fore) | FOREGROUND_BLUE; break; + case PICoutManipulators::Yellow: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN; break; + case PICoutManipulators::Magenta: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_BLUE; break; + case PICoutManipulators::Cyan: attr_ = (attr_ & mask_fore) | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case PICoutManipulators::White: attr_ = (attr_ & mask_fore) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case PICoutManipulators::BackBlack: attr_ = (attr_ & mask_back); break; + case PICoutManipulators::BackRed: attr_ = (attr_ & mask_back) | BACKGROUND_RED; break; + case PICoutManipulators::BackGreen: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN; break; + case PICoutManipulators::BackBlue: attr_ = (attr_ & mask_back) | BACKGROUND_BLUE; break; + case PICoutManipulators::BackYellow: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN; break; + case PICoutManipulators::BackMagenta: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_BLUE; break; + case PICoutManipulators::BackCyan: attr_ = (attr_ & mask_back) | BACKGROUND_GREEN | BACKGROUND_BLUE; break; + case PICoutManipulators::BackWhite: attr_ = (attr_ & mask_back) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break; + case PICoutManipulators::Default: attr_ = dattr; break; + default: break; + } + SetConsoleTextAttribute(hOut, attr_); +#else + switch (f) { + case Bin: case Oct: case Dec: case Hex: break; + case PICoutManipulators::Bold: printf("\e[1m"); break; + case PICoutManipulators::Faint: printf("\e[2m"); break; + case PICoutManipulators::Italic: printf("\e[3m"); break; + case PICoutManipulators::Underline: printf("\e[4m"); break; + case PICoutManipulators::Blink: printf("\e[5m"); break; + case PICoutManipulators::Black: printf("\e[30m"); break; + case PICoutManipulators::Red: printf("\e[31m"); break; + case PICoutManipulators::Green: printf("\e[32m"); break; + case PICoutManipulators::Blue: printf("\e[34m"); break; + case PICoutManipulators::Yellow: printf("\e[33m"); break; + case PICoutManipulators::Magenta: printf("\e[35m"); break; + case PICoutManipulators::Cyan: printf("\e[36m"); break; + case PICoutManipulators::White: printf("\e[37m"); break; + case PICoutManipulators::BackBlack: printf("\e[40m"); break; + case PICoutManipulators::BackRed: printf("\e[41m"); break; + case PICoutManipulators::BackGreen: printf("\e[42m"); break; + case PICoutManipulators::BackBlue: printf("\e[44m"); break; + case PICoutManipulators::BackYellow: printf("\e[43m"); break; + case PICoutManipulators::BackMagenta: printf("\e[45m"); break; + case PICoutManipulators::BackCyan: printf("\e[46m"); break; + case PICoutManipulators::BackWhite: printf("\e[47m"); break; + case PICoutManipulators::Default: printf("\e[0m"); break; + default: break; + } +#endif +} + + /*! \mainpage Title * This is main page */ diff --git a/piincludes.h b/piincludes.h index 5cef4c1f..6e51a0f0 100644 --- a/piincludes.h +++ b/piincludes.h @@ -27,7 +27,7 @@ #define PIINCLUDES_H //! Version of PIP in hex - 0x##(Major)##(Minor)##(Revision) -#define PIP_VERSION 0x000304 +#define PIP_VERSION 0x000306 //! Major value of PIP version #define PIP_VERSION_MAJOR (PIP_VERSION & 0xFF0000) >> 16 @@ -78,16 +78,16 @@ #endif -#if WIN32 || WIN64 || _WIN32 || _WIN64 || __WIN32__ || __WIN64__ +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__WIN64__) # define WINDOWS #endif -#if __QNX__ || __QNXNTO__ +#if defined(__QNX__) || defined(__QNXNTO__) # define QNX #endif -#if __FreeBSD__ +#ifdef __FreeBSD__ # define FREE_BSD #endif -#if __APPLE__ || __MACH__ +#if defined(__APPLE__) || defined(__MACH__) # define MAC_OS #endif #ifndef WINDOWS @@ -99,7 +99,7 @@ # endif # endif #endif -#if __GNUC__ +#ifdef __GNUC__ # define CC_GCC # define CC_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__) # if CC_GCC_VERSION > 0x025F // > 2.95 @@ -107,8 +107,25 @@ # define HAS_LOCALE # endif # endif -#elif _MSC_VER +# pragma GCC diagnostic ignored "-Wformat" +# pragma GCC diagnostic ignored "-Wformat-extra-args" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +#elif defined(_MSC_VER) # define CC_VC +# pragma warning(disable: 4061) +# pragma warning(disable: 4100) +# pragma warning(disable: 4239) +# pragma warning(disable: 4242) +# pragma warning(disable: 4244) +# pragma warning(disable: 4251) +# pragma warning(disable: 4365) +# pragma warning(disable: 4512) +# pragma warning(disable: 4668) +# pragma warning(disable: 4710) +# pragma warning(disable: 4800) +# pragma warning(disable: 4820) +# pragma warning(disable: 4986) +# pragma warning(disable: 4996) #else # define CC_OTHER #endif @@ -168,7 +185,7 @@ #include #include #ifdef WINDOWS -typedef int socklen_t; + typedef int socklen_t; # include # include # include @@ -189,6 +206,7 @@ typedef int socklen_t; # include # include # include +# include #endif #ifdef MAC_OS # include @@ -196,8 +214,8 @@ typedef int socklen_t; # include # include # define environ (*_NSGetEnviron()) -typedef long time_t; -extern clock_serv_t __pi_mac_clock; + typedef long time_t; + extern clock_serv_t __pi_mac_clock; #endif #ifndef QNX # ifndef WINDOWS @@ -212,7 +230,7 @@ extern clock_serv_t __pi_mac_clock; # define PIP_TIMER_RT #endif #ifdef FREE_BSD -extern char ** environ; + extern char ** environ; #endif #include "pimonitor.h" @@ -405,7 +423,7 @@ template inline T piClamp(const T & v, const T & min, const T & max) extern bool isPIInit; //! global variable enabling output to piCout -extern bool piDebug; +extern PIP_EXPORT bool piDebug; extern string ifconfigPath; @@ -656,6 +674,9 @@ private: //! Macro used for conditional (piDebug) output to PICout #define piCout if (piDebug) PICout() +//! Macro used for conditional (piDebug and PIObject::debug()) output to PICout for subclasses of PIObject +#define piCoutObj if (piDebug && debug_) PICout() + class PIMutex; extern PIMutex __PICout_mutex__; @@ -664,25 +685,58 @@ namespace PICoutManipulators { //! \brief Enum contains special characters enum PIP_EXPORT PICoutSpecialChar { - Null /*! Null-character, '\\0' */, - NewLine /*! New line character, '\\n' */, - Tab /*! Tab character, '\\t' */, - Esc /*! Escape character, '\\e' */, - Quote /*! Quote character, '"' */ + Null /*! Null-character, '\\0' */, + NewLine /*! New line character, '\\n' */, + Tab /*! Tab character, '\\t' */, + Esc /*! Escape character, '\\e' */, + Quote /*! Quote character, '"' */ }; //! \brief Enum contains immediate action enum PIP_EXPORT PICoutAction { - Flush /*! Flush the output */ + Flush /*! Flush the output */, + Backspace /*! Remove last symbol */, + ShowCursor /*! Show cursor */, + HideCursor /*! Hide cursor */ }; //! \brief Enum contains control of PICout enum PIP_EXPORT PICoutControl { - AddNone /*! No controls */ = 0x0, - AddSpaces /*! Spaces will be appear after each output */ = 0x1, - AddNewLine /*! New line will be appear after all output */ = 0x2, - AddQuotes /*! Each string will be quoted */ = 0x4, - AddAll /*! All controls */ = 0xFFFFFFFF + AddNone /*! No controls */ = 0x0, + AddSpaces /*! Spaces will be appear after each output */ = 0x1, + AddNewLine /*! New line will be appear after all output */ = 0x2, + AddQuotes /*! Each string will be quoted */ = 0x4, + AddAll /*! All controls */ = 0xFFFFFFFF + }; + + //! \brief Enum contains output format + enum PIP_EXPORT PICoutFormat { + Bin /*! Binary representation of integers */ = 0x01, + Oct /*! Octal representation of integers */ = 0x02, + Dec /*! Decimal representation of integers */ = 0x04, + Hex /*! Hexadecimal representation of integers */ = 0x08, + Bold /*! Bold */ = 0x10, + Faint /*! */ = 0x20, + Italic /*! */ = 0x40, + Underline /*! Underline */ = 0x80, + Blink /*! Blink */ = 0x100, + Black /*! Black font */ = 0x400, + Red /*! Red font */ = 0x800, + Green /*! Green font */ = 0x1000, + Blue /*! Blue font */ = 0x2000, + Yellow /*! Yellow font */ = 0x4000, + Magenta /*! Magenta font */ = 0x8000, + Cyan /*! Cyan font */ = 0x10000, + White /*! White font */ = 0x20000, + BackBlack /*! Black background */ = 0x40000, + BackRed /*! Red background */ = 0x80000, + BackGreen /*! Green background */ = 0x100000, + BackBlue /*! Blue background */ = 0x200000, + BackYellow /*! Yellow background */ = 0x400000, + BackMagenta /*! Magenta background */ = 0x800000, + BackCyan /*! Cyan background */ = 0x1000000, + BackWhite /*! White background */ = 0x2000000, + Default /*! Default format */ = 0x4000000, }; }; @@ -695,7 +749,7 @@ public: //! Default constructor with default features PICout(PIFlags controls = AddSpaces | AddNewLine); - PICout(const PICout & other): fo_(other.fo_), cc_(true), co_(other.co_) {;} + PICout(const PICout & other): fo_(other.fo_), cc_(true), fc_(false), cnb_(other.cnb_), attr_(other.attr_), co_(other.co_) {;} ~PICout(); //! Output operator for strings with "const char * " type @@ -711,89 +765,141 @@ public: PICout operator <<(const char v) {space(); std::cout << v; return *this;} //! Output operator for "unsigned char" values - PICout operator <<(const uchar v) {space(); std::cout << ushort(v); return *this;} + PICout operator <<(const uchar v); //! Output operator for "short" values - PICout operator <<(const short v) {space(); std::cout << v; return *this;} + PICout operator <<(const short v); //! Output operator for "unsigned short" values - PICout operator <<(const ushort v) {space(); std::cout << v; return *this;} + PICout operator <<(const ushort v); //! Output operator for "int" values - PICout operator <<(const int v) {space(); std::cout << v; return *this;} + PICout operator <<(const int v); //! Output operator for "unsigned int" values - PICout operator <<(const uint v) {space(); std::cout << v; return *this;} + PICout operator <<(const uint v); //! Output operator for "long" values - PICout operator <<(const long v) {space(); std::cout << v; return *this;} + PICout operator <<(const long v); //! Output operator for "unsigned long" values - PICout operator <<(const ulong v) {space(); std::cout << v; return *this;} + PICout operator <<(const ulong v); //! Output operator for "long long" values - PICout operator <<(const llong v) {space(); std::cout << v; return *this;} + PICout operator <<(const llong v); //! Output operator for "unsigned long long" values - PICout operator <<(const ullong v) {space(); std::cout << v; return *this;} + PICout operator <<(const ullong v); //! Output operator for "float" values - PICout operator <<(const float v) {space(); std::cout << v; return *this;} + PICout operator <<(const float v); //! Output operator for "double" values - PICout operator <<(const double v) {space(); std::cout << v; return *this;} + PICout operator <<(const double v); //! Output operator for \a PICoutSpecialChar values PICout operator <<(const PICoutSpecialChar v) { switch (v) { - case Null: std::cout << char(0); - case NewLine: std::cout << '\n'; - case Tab: std::cout << '\t'; - case Esc: std::cout << '\e'; - case Quote: std::cout << '"'; + case Null: std::cout << char(0); break; + case NewLine: std::cout << '\n'; fo_ = true; break; + case Tab: std::cout << '\t'; break; + case Esc: +#ifdef CC_VC + std::cout << char(27); +#else + std::cout << '\e'; +#endif + break; + case Quote: std::cout << '"'; break; + }; + return *this; + } + + //! Output operator for \a PIFlags values + PICout operator <<(const PIFlags v) { + if (v[Bin]) cnb_ = 2; + if (v[Oct]) cnb_ = 8; + if (v[Dec]) cnb_ = 10; + if (v[Hex]) cnb_ = 16; + if (v[Bold]) applyFormat(Bold); + if (v[Faint]) applyFormat(Faint); + if (v[Italic]) applyFormat(Italic); + if (v[Underline]) applyFormat(Underline); + if (v[Blink]) applyFormat(Blink); + if (v[Black]) applyFormat(Black); + if (v[Red]) applyFormat(Red); + if (v[Green]) applyFormat(Green); + if (v[Blue]) applyFormat(Blue); + if (v[Yellow]) applyFormat(Yellow); + if (v[Magenta]) applyFormat(Magenta); + if (v[Cyan]) applyFormat(Cyan); + if (v[White]) applyFormat(White); + if (v[BackBlack]) applyFormat(BackBlack); + if (v[BackRed]) applyFormat(BackRed); + if (v[BackGreen]) applyFormat(BackGreen); + if (v[BackBlue]) applyFormat(BackBlue); + if (v[BackYellow]) applyFormat(BackYellow); + if (v[BackMagenta]) applyFormat(BackMagenta); + if (v[BackCyan]) applyFormat(BackCyan); + if (v[BackWhite]) applyFormat(BackWhite); + if (v[Default]) applyFormat(Default); + return *this; + } + + //! Output operator for \a PICoutFormat values + PICout operator <<(const PICoutFormat v) { + switch (v) { + case Bin: cnb_ = 2; break; + case Oct: cnb_ = 8; break; + case Dec: cnb_ = 10; break; + case Hex: cnb_ = 16; break; + default: applyFormat(v); }; return *this; } //! Do some action - PICout operator <<(const PICoutAction v) { - switch (v) { - case Flush: std::cout << std::flush; - }; - return *this; - } + PICout operator <<(const PICoutAction v); //! Set control flag "c" is "on" state - void setControl(PICoutControl c, bool on = true) {co_.setFlag(c, on);} + PICout & setControl(PICoutControl c, bool on = true) {co_.setFlag(c, on); return *this;} - //! Set control flags "c" and if "save" exec \a saveControl() - void setControl(PICoutControls c, bool save = false) {if (save) saveControl(); co_ = c;} + //! Set control flags "c" and if "save" exec \a saveControl() + PICout & setControl(PICoutControls c, bool save = false) {if (save) saveControl(); co_ = c; return *this;} //! Save control flags to internal stack \sa \a restoreControl() - void saveControl() {cos_.push(co_);} + PICout & saveControl() {cos_.push(co_); return *this;} //! Restore control flags from internal stack \sa \a saveControl() - void restoreControl() {if (!cos_.empty()) {co_ = cos_.top(); cos_.pop();}} + PICout & restoreControl() {if (!cos_.empty()) {co_ = cos_.top(); cos_.pop();} return *this;} /*! \brief Conditional put space character to output * \details If it is not a first output and control \a AddSpaces is set * space character is put \sa \a quote(), \a newLine() */ - inline void space() {if (!fo_ && co_[AddSpaces]) std::cout << ' '; fo_ = false;} + inline PICout & space() {if (!fo_ && co_[AddSpaces]) std::cout << ' '; fo_ = false; return *this;} /*! \brief Conditional put quote character to output * \details If control \a AddQuotes is set * quote character is put \sa \a space(), \a newLine() */ - inline void quote() {if (co_[AddQuotes]) std::cout << '"'; fo_ = false;} + inline PICout & quote() {if (co_[AddQuotes]) std::cout << '"'; fo_ = false; return *this;} /*! \brief Conditional put new line character to output * \details If control \a AddNewLine is set * new line character is put \sa \a space(), \a quote() */ - inline void newLine() {if (co_[AddNewLine]) std::cout << std::endl; fo_ = false;} + inline PICout & newLine() {if (co_[AddNewLine]) std::cout << std::endl; fo_ = false; return *this;} private: - bool fo_, cc_; + void applyFormat(PICoutFormat f); + + bool fo_, cc_, fc_; + int cnb_, attr_; PICoutControls co_; std::stack cos_; +#ifdef WINDOWS + static void * hOut; + static WORD dattr; + static DWORD smode; +#endif }; #endif // PIINCLUDES_H diff --git a/piiodevice.cpp b/piiodevice.cpp index 3e0a4843..8db6bc67 100644 --- a/piiodevice.cpp +++ b/piiodevice.cpp @@ -24,7 +24,7 @@ * \brief Base class for input/output classes * * \section PIIODevice_sec0 Synopsis - * This class provide open/close logic, threaded read and virtual input/output + * This class provide open/close logic, threaded read/write and virtual input/output * functions \a read() and \a write(). You should implement pure virtual * function \a openDevice() in your subclass. * @@ -41,13 +41,20 @@ * bool func_name(void * Threaded_read_data, uchar * readed_data, int readed_size)\n * Threaded read starts with function \a startThreadedRead(). * - * \section PIIODevice_sec3 Internal buffer + * \section PIIODevice_sec3 Threaded write + * PIIODevice aggregate another PIThread to perform a threaded write by function + * \a writeThreaded(). This function add task to internal queue and return + * queue entry ID. You should start write thread by function \a startThreadedWrite. + * On successful write event \a threadedWriteEvent is raised with two arguments - + * task ID and written bytes count. + * + * \section PIIODevice_sec4 Internal buffer * PIIODevice have internal buffer for threaded read, and \a threadedRead() function * receive pointer to this buffer in first argument. You can adjust size of this buffer * by function \a setThreadedReadBufferSize() \n * Default size of this buffer is 4096 bytes * - * \section PIIODevice_sec4 Reopen + * \section PIIODevice_sec5 Reopen * When threaded read is begin its call \a open() if device is closed. While threaded * read running PIIODevice check if device opened every read and if not call \a open() * every reopen timeout if reopen enabled. Reopen timeout is set by \a setReopenTimeout(), @@ -58,11 +65,10 @@ */ -//! Constructs a empty PIIODevice PIIODevice::PIIODevice(): PIThread() { mode_ = ReadOnly; opened_ = init_ = thread_started_ = false; - reopen_enabled_ = true; + reopen_enabled_ = raise_threaded_read_ = true; reopen_timeout_ = 1000; ret_func_ = 0; ret_data_ = 0; @@ -82,7 +88,7 @@ PIIODevice::PIIODevice(const PIString & path, PIIODevice::DeviceMode type, bool path_ = path; mode_ = type; opened_ = init_ = thread_started_ = false; - reopen_enabled_ = true; + reopen_enabled_ = raise_threaded_read_ = true; reopen_timeout_ = 1000; ret_func_ = 0; ret_data_ = 0; @@ -150,7 +156,7 @@ void PIIODevice::begin() { void PIIODevice::run() { if (!isReadable()) { //cout << "not readable\n"; - stop(); + PIThread::stop(); return; } if (!thread_started_) { @@ -165,7 +171,7 @@ void PIIODevice::run() { return; } threadedRead(buffer_tr.data(), readed_); - threadedReadEvent(buffer_tr.data(), readed_); + if (raise_threaded_read_) threadedReadEvent(buffer_tr.data(), readed_); } diff --git a/piiodevice.h b/piiodevice.h index 47692c7f..1119ed3f 100644 --- a/piiodevice.h +++ b/piiodevice.h @@ -28,23 +28,13 @@ // function executed from threaded read, pass ThreadedReadData, readedData, sizeOfData typedef bool (*ReadRetFunc)(void * , uchar * , int ); -// events: -// void opened() -// void closed() -// -// handlers: -// bool open() -// bool open(const PIString & path) -// bool open(const DeviceMode & type) -// bool open(const PIString & path, const DeviceMode & type) -// bool close() -// bool initialize() -// void flush() class PIP_EXPORT PIIODevice: public PIThread { PIOBJECT(PIIODevice) public: + + //! Constructs a empty PIIODevice PIIODevice(); //! \brief Open modes for PIIODevice @@ -55,7 +45,7 @@ public: }; PIIODevice(const PIString & path, DeviceMode type = ReadWrite, bool initNow = true); - virtual ~PIIODevice() {stop(); write_thread.stop(); if (opened_) {closeDevice(); if (!opened_) closed();}} + virtual ~PIIODevice() {stop(); if (opened_) {closeDevice(); if (!opened_) closed();}} //! Current open mode of device DeviceMode mode() const {return mode_;} @@ -63,67 +53,119 @@ public: //! Current path of device PIString path() const {return path_;} - //! return \b true if mode is ReadOnly or ReadWrite + //! Set path of device + void setPath(const PIString & path) {path_ = path;} + + //! Return \b true if mode is ReadOnly or ReadWrite bool isReadable() const {return (mode_ & ReadOnly);} - //! return \b true if mode is WriteOnly or ReadWrite + //! Return \b true if mode is WriteOnly or ReadWrite bool isWriteable() const {return (mode_ & WriteOnly);} bool isInitialized() const {return init_;} - //! return \b true if device is successfully opened + //! Return \b true if device is successfully opened bool isOpened() const {return opened_;} - //! return \b true if device is closed + //! Return \b true if device is closed bool isClosed() const {return !opened_;} - //! return \b true if device can read \b now + //! Return \b true if device can read \b now bool canRead() const {return opened_ && (mode_ & ReadOnly);} - //! return \b true if device can write \b now + //! Return \b true if device can write \b now bool canWrite() const {return opened_ && (mode_ & WriteOnly);} - // Enable timer to periodically open device until it will be opened + //! Set execution of \a open enabled while threaded read on closed device void setReopenEnabled(bool yes = true) {reopen_enabled_ = yes;} + + //! Set timeout in milliseconds between \a open tryings if reopen is enabled void setReopenTimeout(int msecs = 1000) {reopen_timeout_ = msecs;} + + //! Return reopen enable bool isReopenEnabled() const {return reopen_enabled_;} + + //! Return reopen timeout int reopenTimeout() {return reopen_timeout_;} - // set return function executed when successful read in thread + /** \brief Set "threaded read slot" + * \details Set external static function of threaded read that will be executed + * at every successful threaded read. Function should have format + * "bool func(void * data, uchar * readed, int size)" */ void setThreadedReadSlot(ReadRetFunc func) {ret_func_ = func;} + + //! Set custom data that will be passed to "threaded read slot" void setThreadedReadData(void * d) {ret_data_ = d;} + + /** \brief Set size of threaded read buffer + * \details Default size is 4096 bytes. If your device can read at single read + * more than 4096 bytes you should use this function to adjust buffer size */ void setThreadedReadBufferSize(int new_size) {buffer_tr.resize(new_size);} + //! Return size of threaded read buffer int threadedReadBufferSize() const {return buffer_tr.size_s();} + + //! Return content of threaded read buffer const uchar * threadedReadBuffer() const {return buffer_tr.data();} - bool isThreadedRead() const {return isRunning();} - void startThreadedRead() {if (!isRunning()) start();} - void startThreadedRead(ReadRetFunc func) {ret_func_ = func; if (!isRunning()) start();} - + //! Return \b true if threaded read is started + bool isThreadedRead() const {return isRunning();} + + //! Start threaded read + void startThreadedRead() {if (!isRunning()) PIThread::start();} + + //! Start threaded read and assign "threaded read slot" to "func" + void startThreadedRead(ReadRetFunc func) {ret_func_ = func; if (!isRunning()) PIThread::start();} + + //! Stop threaded read + void stopThreadedRead() {PIThread::stop();} + + + //! Return \b true if threaded write is started bool isThreadedWrite() const {return write_thread.isRunning();} + + //! Start threaded write void startThreadedWrite() {if (!write_thread.isRunning()) write_thread.startOnce();} + + //! Stop threaded write void stopThreadedWrite() {write_thread.stop();} + + //! Clear threaded write task queue void clearThreadedWriteQueue() {write_thread.lock(); write_queue.clear(); write_thread.unlock();} + + //! Start both threaded read and threaded write + void start() {startThreadedRead(); startThreadedWrite();} + + //! Stop both threaded read and threaded write and if "wait" block until both threads are stop + void stop(bool wait = false) {stopThreadedRead(); stopThreadedWrite(); if (wait) while (write_thread.isRunning() || isRunning()) msleep(1);} - // Read from device to "read_to" maximum "max_size" bytes, return readed bytes count - virtual int read(void * read_to, int max_size) {piCout << "[PIIODevice] \"read\" not implemented!"; return -2;} - // Write to device "data" maximum "max_size" bytes, return written bytes count - virtual int write(const void * data, int max_size) {piCout << "[PIIODevice] \"write\" not implemented!"; return -2;} + //! Reimplement this function to read from your device + virtual int read(void * read_to, int max_size) {piCoutObj << "[PIIODevice] \"read\" not implemented!"; return -2;} - // Read from device maximum "max_size" bytes and return them as PIByteArray + //! Reimplement this function to write to your device + virtual int write(const void * data, int max_size) {piCoutObj << "[PIIODevice] \"write\" not implemented!"; return -2;} + + + //! Read from device maximum "max_size" bytes and return them as PIByteArray PIByteArray read(int max_size) {buffer_in.resize(max_size); int ret = read(buffer_in.data(), max_size); if (ret < 0) return PIByteArray(); return buffer_in.resized(ret);} + + //! Write to device "data" int write(const PIByteArray & data) {return write(data.data(), data.size_s());} + + //! Add task to threaded write queue and return task ID ullong writeThreaded(const void * data, int max_size) {return writeThreaded(PIByteArray(data, uint(max_size)));} + + //! Add task to threaded write queue and return task ID ullong writeThreaded(const PIByteArray & data); + EVENT_HANDLER(bool, open) {if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;} EVENT_HANDLER1(bool, open, const PIString &, _path) {path_ = _path; if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;} EVENT_HANDLER1(bool, open, const DeviceMode &, _type) {mode_ = _type; if (!init_) init(); opened_ = openDevice(); if (opened_) opened(); return opened_;} @@ -153,6 +195,12 @@ public: //! \fn bool open(const PIString & path, const DeviceMode & mode) //! \brief Open device with path "path" and mode "mode" + //! \fn bool close() + //! \brief Close device + + //! \fn bool initialize() + //! \brief Initialize device + //! \} //! \vhandlers //! \{ @@ -174,19 +222,22 @@ public: //! \brief Raise if read thread succesfull read some data //! \fn void threadedWriteEvent(ullong id, int written_size) - //! \brief Raise if write thread succesfull write some data of queue item with id "id" + //! \brief Raise if write thread succesfull write some data of task with ID "id" //! \} protected: - // Function executed before first openDevice() or from constructor + + //! Function executed before first \a openDevice() or from constructor virtual bool init() {return true;} - // Functions to open and close device, return value will set to "opened_" variable + //! Reimplement to open device, return value will be set to "opened_" variable virtual bool openDevice() = 0; // use path_, type_, opened_, init_ variables + + //! Reimplement to close device, inverse return value will be set to "opened_" variable virtual bool closeDevice() {return true;} // use path_, type_, opened_, init_ variables - // Function executed when thread read some data, default implementation execute external slot "ret_func_" + //! Function executed when thread read some data, default implementation execute external slot "ret_func_" virtual bool threadedRead(uchar * readed, int size) {if (ret_func_ != 0) return ret_func_(ret_data_, readed, size); return true;} void terminate(); @@ -194,7 +245,7 @@ protected: PIString path_; DeviceMode mode_; ReadRetFunc ret_func_; - bool init_, opened_, thread_started_, reopen_enabled_; + bool init_, opened_, thread_started_, reopen_enabled_, raise_threaded_read_; int reopen_timeout_; void * ret_data_; diff --git a/pikbdlistener.cpp b/pikbdlistener.cpp index cd364a95..e5285c08 100644 --- a/pikbdlistener.cpp +++ b/pikbdlistener.cpp @@ -64,13 +64,15 @@ void PIKbdListener::run() { KEY_EVENT_RECORD ker = ir.Event.KeyEvent; if (ker.bKeyDown) { bool ctrl = ((ker.dwControlKeyState & LEFT_CTRL_PRESSED) || (ker.dwControlKeyState & RIGHT_CTRL_PRESSED)); + bool shift = (ker.dwControlKeyState & SHIFT_PRESSED); + if (ker.dwControlKeyState & CAPSLOCK_ON) shift = !shift; //cout << "key " << int(ker.wVirtualKeyCode) << endl; switch (ker.wVirtualKeyCode) { case 37: ret = 1; lc = (ctrl ? CtrlLeftArrow : LeftArrow); break; case 38: ret = 1; lc = (ctrl ? CtrlUpArrow : UpArrow); break; case 39: ret = 1; lc = (ctrl ? CtrlRightArrow : RightArrow); break; case 40: ret = 1; lc = (ctrl ? CtrlDownArrow : DownArrow); break; - default: ret = 1; lc = ker.uChar.AsciiChar; break; + default: ret = 1; lc = (shift ? char(toupper(ker.uChar.AsciiChar)) : ker.uChar.AsciiChar); break; } if (lc == 0) return; } else return; diff --git a/pimath.cpp b/pimath.cpp index bf7a761f..1d44b3f5 100644 --- a/pimath.cpp +++ b/pimath.cpp @@ -20,6 +20,443 @@ #include "pimath.h" +#ifndef PIP_MATH_J0 +double j0(const double & v) { + double x = v; + double xsq; + double nn; + double pzero; + double qzero; + double p1; + double q1; + double result; + if (x < 0) x = -x; + if (x > 8.) { + double xsq_; + double p2; + double q2; + double p3; + double q3; + xsq_ = 64. / (x * x); + p2 = 0.0; + p2 = 2485.271928957404011288128951 + xsq_ * p2; + p2 = 153982.6532623911470917825993 + xsq_ * p2; + p2 = 2016135.283049983642487182349 + xsq_ * p2; + p2 = 8413041.456550439208464315611 + xsq_ * p2; + p2 = 12332384.76817638145232406055 + xsq_ * p2; + p2 = 5393485.083869438325262122897 + xsq_ * p2; + q2 = 1.0; + q2 = 2615.700736920839685159081813 + xsq_ * q2; + q2 = 156001.7276940030940592769933 + xsq_ * q2; + q2 = 2025066.801570134013891035236 + xsq_ * q2; + q2 = 8426449.050629797331554404810 + xsq_ * q2; + q2 = 12338310.22786324960844856182 + xsq_ * q2; + q2 = 5393485.083869438325560444960 + xsq_ * q2; + p3 = -0.0; + p3 = -4.887199395841261531199129300 +xsq_ * p3; + p3 = -226.2630641933704113967255053 +xsq_ * p3; + p3 = -2365.956170779108192723612816 +xsq_ * p3; + p3 = -8239.066313485606568803548860 +xsq_ * p3; + p3 = -10381.41698748464093880530341 +xsq_ * p3; + p3 = -3984.617357595222463506790588 +xsq_ * p3; + q3 = 1.0; + q3 = 408.7714673983499223402830260 + xsq_ * q3; + q3 = 15704.89191515395519392882766 + xsq_ * q3; + q3 = 156021.3206679291652539287109 + xsq_ * q3; + q3 = 533291.3634216897168722255057 + xsq_ * q3; + q3 = 666745.4239319826986004038103 + xsq_ * q3; + q3 = 255015.5108860942382983170882 + xsq_ * q3; + pzero = p2 / q2; + qzero = 8. * p3 / q3 / x; + nn = x- M_PI / 4.; + result = sqrt(2. / M_PI / x) * (pzero * cos(nn) - qzero * sin(nn)); + return result; + } + xsq = x * x; + p1 = 26857.86856980014981415848441; + p1 = -40504123.71833132706360663322 + xsq * p1; + p1 = 25071582855.36881945555156435 + xsq * p1; + p1 = -8085222034853.793871199468171 + xsq * p1; + p1 = 1434354939140344.111664316553 + xsq * p1; + p1 = -136762035308817138.6865416609 + xsq * p1; + p1 = 6382059341072356562.289432465 + xsq * p1; + p1 = -117915762910761053603.8440800 + xsq * p1; + p1 = 493378725179413356181.6813446 + xsq * p1; + q1 = 1.; + q1 = 1363.063652328970604442810507 + xsq * q1; + q1 = 1114636.098462985378182402543 + xsq * q1; + q1 = 669998767.2982239671814028660 + xsq * q1; + q1 = 312304311494.1213172572469442 + xsq * q1; + q1 = 112775673967979.8507056031594 + xsq * q1; + q1 = 30246356167094626.98627330784 + xsq * q1; + q1 = 5428918384092285160.200195092 + xsq * q1; + q1 = 493378725179413356211.3278438 + xsq * q1; + return p1 / q1; +} +#endif + + +#ifndef PIP_MATH_J1 +double j1(const double & v) { + double x = v; + double s; + double xsq; + double nn; + double pzero; + double qzero; + double p1; + double q1; + double result; + s = sign(x); + if (x < 0) + x = -x; + if (x > 8.) { + double xsq_; + double p2; + double q2; + double p3; + double q3; + xsq_ = 64.0 / (x * x); + p2 = -1611.616644324610116477412898; + p2 = -109824.0554345934672737413139 + xsq_ * p2; + p2 = -1523529.351181137383255105722 + xsq_ * p2; + p2 = -6603373.248364939109255245434 + xsq_ * p2; + p2 = -9942246.505077641195658377899 + xsq_ * p2; + p2 = -4435757.816794127857114720794 + xsq_ * p2; + q2 = 1.0; + q2 = -1455.009440190496182453565068 + xsq_ * q2; + q2 = -107263.8599110382011903063867 + xsq_ * q2; + q2 = -1511809.506634160881644546358 + xsq_ * q2; + q2 = -6585339.479723087072826915069 + xsq_ * q2; + q2 = -9934124.389934585658967556309 + xsq_ * q2; + q2 = -4435757.816794127856828016962 + xsq_ * q2; + p3 = 35.26513384663603218592175580; + p3 = 1706.375429020768002061283546 + xsq_ * p3; + p3 = 18494.26287322386679652009819 + xsq_ * p3; + p3 = 66178.83658127083517939992166 + xsq_ * p3; + p3 = 85145.16067533570196555001171 + xsq_ * p3; + p3 = 33220.91340985722351859704442 + xsq_ * p3; + q3 = 1.0; + q3 = 863.8367769604990967475517183 + xsq_ * q3; + q3 = 37890.22974577220264142952256 + xsq_ * q3; + q3 = 400294.4358226697511708610813 + xsq_ * q3; + q3 = 1419460.669603720892855755253 + xsq_ * q3; + q3 = 1819458.042243997298924553839 + xsq_ * q3; + q3 = 708712.8194102874357377502472 + xsq_ * q3; + pzero = p2 / q2; + qzero = 8 * p3 / q3 / x; + nn = x - 3 * M_PI / 4; + result = sqrt(2 / M_PI / x) * (pzero * cos(nn) - qzero * sin(nn)); + if (s < 0) + result = -result; + return result; + } + xsq = sqr(x); + p1 = 2701.122710892323414856790990; + p1 = -4695753.530642995859767162166 + xsq * p1; + p1 = 3413234182.301700539091292655 + xsq * p1; + p1 = -1322983480332.126453125473247 + xsq * p1; + p1 = 290879526383477.5409737601689 + xsq * p1; + p1 = -35888175699101060.50743641413 + xsq * p1; + p1 = 2316433580634002297.931815435 + xsq * p1; + p1 = -66721065689249162980.20941484 + xsq * p1; + p1 = 581199354001606143928.050809 + xsq * p1; + q1 = 1.0; + q1 = 1606.931573481487801970916749 + xsq * q1; + q1 = 1501793.594998585505921097578 + xsq * q1; + q1 = 1013863514.358673989967045588 + xsq * q1; + q1 = 524371026216.7649715406728642 + xsq * q1; + q1 = 208166122130760.7351240184229 + xsq * q1; + q1 = 60920613989175217.46105196863 + xsq * q1; + q1 = 11857707121903209998.37113348 + xsq * q1; + q1 = 1162398708003212287858.529400 + xsq * q1; + result = s * x * p1 / q1; + return result; +} +#endif + + +#ifndef PIP_MATH_JN +double jn(const int & n, const double & v) { + double x = v; + double pkm2; + double pkm1; + double pk; + double xk; + double r; + double ans; + int k; + int sg; + double result; + if (n < 0) { + n = -n; + if (n % 2 == 0) + sg = 1; + else + sg = -1; + } else + sg = 1; + if (x < 0) { + if (n % 2 != 0) + sg = -sg; + x = -x; + } + if (n == 0) { + result = sg * j0(x); + return result; + } + if (n == 1) { + result = sg * j1(x); + return result; + } + if (n == 2) { + if (x == 0) + result = 0; + else + result = sg * (2.0 * j1(x) / x - j0(x)); + return result; + } + if (x < 1E-16) { + result = 0; + return result; + } + k = 53; + pk = 2 * (n + k); + ans = pk; + xk = x * x; + do { + pk = pk - 2.0; + ans = pk - xk / ans; + k = k - 1; + } while (k != 0); + ans = x / ans; + pk = 1.0; + pkm1 = 1.0 / ans; + k = n - 1; + r = 2 * k; + do { + pkm2 = (pkm1 * r - pk * x) / x; + pk = pkm1; + pkm1 = pkm2; + r = r - 2.0; + k = k - 1; + } while (k != 0); + if (fabs(pk) > fabs(pkm1)) + ans = j1(x) / pk; + else + ans = j0(x) / pkm1; + result = sg * ans; + return result; +} +#endif + + +#ifndef PIP_MATH_Y0 +double y0(const double & v) { + double x = v; + double nn; + double xsq; + double pzero; + double qzero; + double p4; + double q4; + double result; + if (x > 8.) { + double xsq_; + double p2; + double q2; + double p3; + double q3; + xsq_ = 64.0 / (x * x); + p2 = 0.0; + p2 = 2485.271928957404011288128951 + xsq_ * p2; + p2 = 153982.6532623911470917825993 + xsq_ * p2; + p2 = 2016135.283049983642487182349 + xsq_ * p2; + p2 = 8413041.456550439208464315611 + xsq_ * p2; + p2 = 12332384.76817638145232406055 + xsq_ * p2; + p2 = 5393485.083869438325262122897 + xsq_ * p2; + q2 = 1.0; + q2 = 2615.700736920839685159081813 + xsq_ * q2; + q2 = 156001.7276940030940592769933 + xsq_ * q2; + q2 = 2025066.801570134013891035236 + xsq_ * q2; + q2 = 8426449.050629797331554404810 + xsq_ * q2; + q2 = 12338310.22786324960844856182 + xsq_ * q2; + q2 = 5393485.083869438325560444960 + xsq_ * q2; + p3 = -0.0; + p3 = -4.887199395841261531199129300 + xsq_ * p3; + p3 = -226.2630641933704113967255053 + xsq_ * p3; + p3 = -2365.956170779108192723612816 + xsq_ * p3; + p3 = -8239.066313485606568803548860 + xsq_ * p3; + p3 = -10381.41698748464093880530341 + xsq_ * p3; + p3 = -3984.617357595222463506790588 + xsq_ * p3; + q3 = 1.0; + q3 = 408.7714673983499223402830260 + xsq_ * q3; + q3 = 15704.89191515395519392882766 + xsq_ * q3; + q3 = 156021.3206679291652539287109 + xsq_ * q3; + q3 = 533291.3634216897168722255057 + xsq_ * q3; + q3 = 666745.4239319826986004038103 + xsq_ * q3; + q3 = 255015.5108860942382983170882 + xsq_ * q3; + pzero = p2 / q2; + qzero = 8 * p3 / q3 / x; + nn = x - M_PI / 4; + result = sqrt(2 / M_PI / x) * (pzero * sin(nn) + qzero * cos(nn)); + return result; + } + xsq = sqr(x); + p4 = -41370.35497933148554125235152; + p4 = 59152134.65686889654273830069 + xsq * p4; + p4 = -34363712229.79040378171030138 + xsq * p4; + p4 = 10255208596863.94284509167421 + xsq * p4; + p4 = -1648605817185729.473122082537 + xsq * p4; + p4 = 137562431639934407.8571335453 + xsq * p4; + p4 = -5247065581112764941.297350814 + xsq * p4; + p4 = 65874732757195549259.99402049 + xsq * p4; + p4 = -27502866786291095837.01933175 + xsq * p4; + q4 = 1.0; + q4 = 1282.452772478993804176329391 + xsq * q4; + q4 = 1001702.641288906265666651753 + xsq * q4; + q4 = 579512264.0700729537480087915 + xsq * q4; + q4 = 261306575504.1081249568482092 + xsq * q4; + q4 = 91620380340751.85262489147968 + xsq * q4; + q4 = 23928830434997818.57439356652 + xsq * q4; + q4 = 4192417043410839973.904769661 + xsq * q4; + q4 = 372645883898616588198.9980 + xsq * q4; + result = p4 / q4 + 2 / M_PI * j0(x) * log(x); + return result; +} +#endif + + +#ifndef PIP_MATH_Y1 +double y1(const double & v) { + double x = v; + double nn; + double xsq; + double pzero; + double qzero; + double p4; + double q4; + double result; + if (x > 8.) { + double xsq_; + double p2; + double q2; + double p3; + double q3; + xsq_ = 64.0 / (x * x); + p2 = -1611.616644324610116477412898; + p2 = -109824.0554345934672737413139 + xsq_ * p2; + p2 = -1523529.351181137383255105722 + xsq_ * p2; + p2 = -6603373.248364939109255245434 + xsq_ * p2; + p2 = -9942246.505077641195658377899 + xsq_ * p2; + p2 = -4435757.816794127857114720794 + xsq_ * p2; + q2 = 1.0; + q2 = -1455.009440190496182453565068 + xsq_ * q2; + q2 = -107263.8599110382011903063867 + xsq_ * q2; + q2 = -1511809.506634160881644546358 + xsq_ * q2; + q2 = -6585339.479723087072826915069 + xsq_ * q2; + q2 = -9934124.389934585658967556309 + xsq_ * q2; + q2 = -4435757.816794127856828016962 + xsq_ * q2; + p3 = 35.26513384663603218592175580; + p3 = 1706.375429020768002061283546 + xsq_ * p3; + p3 = 18494.26287322386679652009819 + xsq_ * p3; + p3 = 66178.83658127083517939992166 + xsq_ * p3; + p3 = 85145.16067533570196555001171 + xsq_ * p3; + p3 = 33220.91340985722351859704442 + xsq_ * p3; + q3 = 1.0; + q3 = 863.8367769604990967475517183 + xsq_ * q3; + q3 = 37890.22974577220264142952256 + xsq_ * q3; + q3 = 400294.4358226697511708610813 + xsq_ * q3; + q3 = 1419460.669603720892855755253 + xsq_ * q3; + q3 = 1819458.042243997298924553839 + xsq_ * q3; + q3 = 708712.8194102874357377502472 + xsq_ * q3; + pzero = p2 / q2; + qzero = 8 * p3 / q3 / x; + nn = x - 3 * M_PI / 4; + result = sqrt(2 / M_PI / x) * (pzero * sin(nn) + qzero * cos(nn)); + return result; + } + xsq = sqr(x); + p4 = -2108847.540133123652824139923; + p4 = 3639488548.124002058278999428 + xsq * p4; + p4 = -2580681702194.450950541426399 + xsq * p4; + p4 = 956993023992168.3481121552788 + xsq * p4; + p4 = -196588746272214065.8820322248 + xsq * p4; + p4 = 21931073399177975921.11427556 + xsq * p4; + p4 = -1212297555414509577913.561535 + xsq * p4; + p4 = 26554738314348543268942.48968 + xsq * p4; + p4 = -99637534243069222259967.44354 + xsq * p4; + q4 = 1.0; + q4 = 1612.361029677000859332072312 + xsq * q4; + q4 = 1563282.754899580604737366452 + xsq * q4; + q4 = 1128686837.169442121732366891 + xsq * q4; + q4 = 646534088126.5275571961681500 + xsq * q4; + q4 = 297663212564727.6729292742282 + xsq * q4; + q4 = 108225825940881955.2553850180 + xsq * q4; + q4 = 29549879358971486742.90758119 + xsq * q4; + q4 = 5435310377188854170800.653097 + xsq * q4; + q4 = 508206736694124324531442.4152 + xsq * q4; + result = x * p4 / q4 + 2 / M_PI * (j1(x) * log(x) - 1 / x); + return result; +} +#endif + + +#ifndef PIP_MATH_YN +double yn(const int & n, const double & v) { + int i; + double x = v; + double a; + double b; + double tmp; + double s; + double result; + s = 1; + if (n < 0) { + n = -n; + if (n % 2 != 0) + s = -1; + } + if (n == 0) { + result = y0(x); + return result; + } + if (n == 1) { + result = s * y1(x); + return result; + } + a = y0(x); + b = y1(x); + for (i = 1; i <= n - 1; i++) { + tmp = b; + b = 2 * i / x * b - a; + a = tmp; + } + result = s * b; + return result; +} +#endif + + +double randomn(double dv, double sv) { + static bool agen = false; + double s = 2., v0 = 0., v1 = 0.; + if (agen) { + agen = false; + v1 = v1 * sqrt(-2 * log(s) / s); + return v1 * sv + dv; + } + while (s > 1. || s == 0.) { + v0 = randomd(); + v1 = randomd(); + s = v0*v0 + v1*v1; + } + v0 = v0 * sqrt(-2 * log(s) / s); + return v0 * sv + dv; +} + + + const char Solver::methods_desc[] = "b{Methods:}\ \n -1 - Global settings\ \n 01 - Eyler 1\ @@ -70,7 +507,7 @@ void Solver::fromTF(const TransferFunction & TF) { if (TF.vector_An.size() >= TF.vector_Bm.size()) size = TF.vector_An.size()-1; else { - cout << "Solver error: {A} should be greater than {B}" << endl; + piCout << "Solver error: {A} should be greater than {B}"; return; } if (size == 0) return; @@ -327,7 +764,7 @@ void PIFFT::fftc1d(const PIVector &a, uint n) { void PIFFT::fftc1r(const PIVector & a, uint n) { uint i; - if( n%2==0 ) { + if( n%2==0) { PIVector buf; uint n2 = n/2; //buf.resize(n); @@ -455,14 +892,14 @@ void PIFFT::ftbase_ftbasegenerateplanrec( int ftbase_fftcodeletplan = 2; int ftbase_fftrealcooleytukeyplan = 5; int ftbase_fftemptyplan = 6; - if( *plansize+ftbase_ftbaseplanentrysize>(*planarraysize) ) { + if( *plansize+ftbase_ftbaseplanentrysize>(*planarraysize)) { curplan.plan.resize(8*(*planarraysize)); *planarraysize = 8*(*planarraysize); } entryoffset = *plansize; esize = ftbase_ftbaseplanentrysize; *plansize = *plansize+esize; - if( n==1 ) { + if( n==1) { curplan.plan[entryoffset+0] = esize; curplan.plan[entryoffset+1] = -1; curplan.plan[entryoffset+2] = -1; @@ -474,12 +911,12 @@ void PIFFT::ftbase_ftbasegenerateplanrec( return; } ftbasefactorize(n, &n1, &n2); - if( n1!=1 ) { + if( n1!=1) { *tmpmemsize = piMax(*tmpmemsize, 2*n1*n2); curplan.plan[entryoffset+0] = esize; curplan.plan[entryoffset+1] = n1; curplan.plan[entryoffset+2] = n2; - if( tasktype==ftbase_ftbasecffttask ) + if( tasktype==ftbase_ftbasecffttask) curplan.plan[entryoffset+3] = ftbase_fftcooleytukeyplan; else curplan.plan[entryoffset+3] = ftbase_fftrealcooleytukeyplan; @@ -501,9 +938,9 @@ void PIFFT::ftbase_ftbasegenerateplanrec( curplan.plan[entryoffset+5] = -1; curplan.plan[entryoffset+6] = -1; curplan.plan[entryoffset+7] = *precomputedsize; - if( n==3 ) + if( n==3) *precomputedsize = *precomputedsize+2; - if( n==5 ) + if( n==5) *precomputedsize = *precomputedsize+5; return; } else { @@ -547,22 +984,22 @@ void PIFFT::ftbase_ftbaseprecomputeplanrec(ftplan* plan, int ftbase_fhtcooleytukeyplan = 3; int ftbase_fhtcodeletplan = 4; int ftbase_fftrealcooleytukeyplan = 5; - if( (curplan.plan[entryoffset+3]==ftbase_fftcooleytukeyplan||curplan.plan[entryoffset+3]==ftbase_fftrealcooleytukeyplan)||curplan.plan[entryoffset+3]==ftbase_fhtcooleytukeyplan ) { + if( (curplan.plan[entryoffset+3]==ftbase_fftcooleytukeyplan||curplan.plan[entryoffset+3]==ftbase_fftrealcooleytukeyplan)||curplan.plan[entryoffset+3]==ftbase_fhtcooleytukeyplan) { ftbase_ftbaseprecomputeplanrec(plan, curplan.plan[entryoffset+5], stackptr); ftbase_ftbaseprecomputeplanrec(plan, curplan.plan[entryoffset+6], stackptr); return; } - if( curplan.plan[entryoffset+3]==ftbase_fftcodeletplan||curplan.plan[entryoffset+3]==ftbase_fhtcodeletplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fftcodeletplan||curplan.plan[entryoffset+3]==ftbase_fhtcodeletplan) { n1 = curplan.plan[entryoffset+1]; n2 = curplan.plan[entryoffset+2]; n = n1*n2; - if( n==3 ) { + if( n==3) { offs = curplan.plan[entryoffset+7]; curplan.precomputed[offs+0] = cos(2*M_PI/3)-1; curplan.precomputed[offs+1] = sin(2*M_PI/3); return; } - if( n==5 ) { + if( n==5) { offs = curplan.plan[entryoffset+7]; v = 2*M_PI/5; curplan.precomputed[offs+0] = (cos(v)+cos(2*v))/2-1; @@ -573,7 +1010,7 @@ void PIFFT::ftbase_ftbaseprecomputeplanrec(ftplan* plan, return; } } - if( curplan.plan[entryoffset+3]==ftbase_fftbluesteinplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fftbluesteinplan) { ftbase_ftbaseprecomputeplanrec(plan, curplan.plan[entryoffset+5], stackptr); n = curplan.plan[entryoffset+1]; m = curplan.plan[entryoffset+4]; @@ -587,7 +1024,7 @@ void PIFFT::ftbase_ftbaseprecomputeplanrec(ftplan* plan, curplan.precomputed[offs+2*i+1] = by; curplan.precomputed[offs+2*m+2*i+0] = bx; curplan.precomputed[offs+2*m+2*i+1] = by; - if( i>0 ) { + if( i>0) { curplan.precomputed[offs+2*(m-i)+0] = bx; curplan.precomputed[offs+2*(m-i)+1] = by; } @@ -601,29 +1038,29 @@ void PIFFT::ftbase_ftbaseprecomputeplanrec(ftplan* plan, void PIFFT::ftbasefactorize(int n, int* n1, int* n2) { *n1 = *n2 = 0; int ftbase_ftbasecodeletrecommended = 5; - if( (*n1)*(*n2)!=n ) { + if( (*n1)*(*n2)!=n) { for(int j=ftbase_ftbasecodeletrecommended; j>=2; j--) { - if( n%j==0 ) { + if( n%j==0) { *n1 = j; *n2 = n/j; break; } } } - if( (*n1)*(*n2)!=n ) { + if( (*n1)*(*n2)!=n) { for(int j=ftbase_ftbasecodeletrecommended+1; j<=n-1; j++) { - if( n%j==0 ) { + if( n%j==0) { *n1 = j; *n2 = n/j; break; } } } - if( (*n1)*(*n2)!=n ) { + if( (*n1)*(*n2)!=n) { *n1 = 1; *n2 = n; } - if( (*n2)==1 && (*n1)!=1 ) { + if( (*n2)==1 && (*n1)!=1) { *n2 = *n1; *n1 = 1; } @@ -637,15 +1074,15 @@ Is number smooth? Copyright 01.05.2009 by Bochkanov Sergey *************************************************************************/ void PIFFT::ftbase_ftbasefindsmoothrec(int n, int seed, int leastfactor, int* best) { - if( seed>=n ) { + if( seed>=n) { *best = piMin(*best, seed); return; } - if( leastfactor<=2 ) + if( leastfactor<=2) ftbase_ftbasefindsmoothrec(n, seed*2, 2, best); - if( leastfactor<=3 ) + if( leastfactor<=3) ftbase_ftbasefindsmoothrec(n, seed*3, 3, best); - if( leastfactor<=5 ) + if( leastfactor<=5) ftbase_ftbasefindsmoothrec(n, seed*5, 5, best); } @@ -670,9 +1107,9 @@ void PIFFT::ftbase_internalreallintranspose(PIVector* a, int m, int n, i void PIFFT::ftbase_fftirltrec(PIVector* a, int astart, int astride, PIVector* b, int bstart, int bstride, int m, int n) { int idx1, idx2; int m1, n1; - if( m==0||n==0 ) + if( m==0||n==0) return; - if( piMax(m, n)<=8 ) { + if( piMax(m, n)<=8) { for(int i=0; i<=m-1; i++) { idx1 = bstart+i; idx2 = astart+i*astride; @@ -684,15 +1121,15 @@ void PIFFT::ftbase_fftirltrec(PIVector* a, int astart, int astride, PIVe } return; } - if( n>m ) { + if( n>m) { n1 = n/2; - if( n-n1>=8&&n1%8!=0 ) + if( n-n1>=8&&n1%8!=0) n1 = n1+(8-n1%8); ftbase_fftirltrec(a, astart, astride, b, bstart, bstride, m, n1); ftbase_fftirltrec(a, astart+n1, astride, b, bstart+n1*bstride, bstride, m, n-n1); } else { m1 = m/2; - if( m-m1>=8&&m1%8!=0 ) + if( m-m1>=8&&m1%8!=0) m1 = m1+(8-m1%8); ftbase_fftirltrec(a, astart, astride, b, bstart, bstride, m1, n); ftbase_fftirltrec(a, astart+m1*astride, astride, b, bstart+m1, bstride, m-m1, n); @@ -709,9 +1146,9 @@ void PIFFT::ftbase_internalcomplexlintranspose(PIVector* a, int m, int n void PIFFT::ftbase_ffticltrec(PIVector* a, int astart, int astride, PIVector* b, int bstart, int bstride, int m, int n) { int idx1, idx2, m2, m1, n1; - if( m==0||n==0 ) + if( m==0||n==0) return; - if( piMax(m, n)<=8 ) { + if( piMax(m, n)<=8) { m2 = 2*bstride; for(int i=0; i<=m-1; i++) { idx1 = bstart+2*i; @@ -725,15 +1162,15 @@ void PIFFT::ftbase_ffticltrec(PIVector* a, int astart, int astride, PIVe } return; } - if( n>m ) { + if( n>m) { n1 = n/2; - if( n-n1>=8&&n1%8!=0 ) + if( n-n1>=8&&n1%8!=0) n1 = n1+(8-n1%8); ftbase_ffticltrec(a, astart, astride, b, bstart, bstride, m, n1); ftbase_ffticltrec(a, astart+2*n1, astride, b, bstart+2*n1*bstride, bstride, m, n-n1); } else { m1 = m/2; - if( m-m1>=8&&m1%8!=0 ) + if( m-m1>=8&&m1%8!=0) m1 = m1+(8-m1%8); ftbase_ffticltrec(a, astart, astride, b, bstart, bstride, m1, n); ftbase_ffticltrec(a, astart+2*m1*astride, astride, b, bstart+2*m1, bstride, m-m1, n); @@ -775,9 +1212,9 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, int ftbase_fftemptyplan = 6; PIVector & tmpb(curplan.tmpbuf); - if( curplan.plan[entryoffset+3]==ftbase_fftemptyplan ) + if( curplan.plan[entryoffset+3]==ftbase_fftemptyplan) return; - if( curplan.plan[entryoffset+3]==ftbase_fftcooleytukeyplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fftcooleytukeyplan) { n1 = curplan.plan[entryoffset+1]; n2 = curplan.plan[entryoffset+2]; ftbase_internalcomplexlintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf)); @@ -790,7 +1227,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, ftbase_internalcomplexlintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf)); return; } - if( curplan.plan[entryoffset+3]==ftbase_fftrealcooleytukeyplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fftrealcooleytukeyplan) { n1 = curplan.plan[entryoffset+1]; n2 = curplan.plan[entryoffset+2]; ftbase_internalcomplexlintranspose(a, n2, n1, aoffset, &(curplan.tmpbuf)); @@ -817,7 +1254,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, } for (int i=0; i<2*n2*2; i++) (*a)[offs+i] = tmpb[i]; } - if( n1%2!=0 ) + if( n1%2!=0) ftbaseexecuteplanrec(a, aoffset+(n1-1)*n2*2, plan, curplan.plan[entryoffset+6], stackptr); ftbase_ffttwcalc(a, aoffset, n2, n1); ftbase_internalcomplexlintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf)); @@ -826,7 +1263,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, ftbase_internalcomplexlintranspose(a, n2, n1, aoffset, &(curplan.tmpbuf)); return; } - if( curplan.plan[entryoffset+3]==ftbase_fhtcooleytukeyplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fhtcooleytukeyplan) { n1 = curplan.plan[entryoffset+1]; n2 = curplan.plan[entryoffset+2]; n = n1*n2; @@ -846,7 +1283,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, ftbase_ffttwcalc(&(curplan.tmpbuf), 0, n1, n2); for(int j=0; j<=n1-1; j++) (*a)[aoffset+j] = tmpb[2*j+0]+tmpb[2*j+1]; - if( n2%2==0 ) { + if( n2%2==0) { offs = 2*(n2/2)*n1; offsa = aoffset+n2/2*n1; for(int j=0; j<=n1-1; j++) @@ -868,11 +1305,11 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, ftbase_internalreallintranspose(a, n1, n2, aoffset, &(curplan.tmpbuf)); return; } - if( curplan.plan[entryoffset+3]==ftbase_fftcodeletplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fftcodeletplan) { n1 = curplan.plan[entryoffset+1]; n2 = curplan.plan[entryoffset+2]; n = n1*n2; - if( n==2 ) { + if( n==2) { a0x = (*a)[aoffset+0]; a0y = (*a)[aoffset+1]; a1x = (*a)[aoffset+2]; @@ -887,7 +1324,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, (*a)[aoffset+3] = v3; return; } - if( n==3 ) { + if( n==3) { offs = curplan.plan[entryoffset+7]; c1 = curplan.precomputed[offs+0]; c2 = curplan.precomputed[offs+1]; @@ -919,7 +1356,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, (*a)[aoffset+5] = a2y; return; } - if( n==4 ) { + if( n==4) { a0x = (*a)[aoffset+0]; a0y = (*a)[aoffset+1]; a1x = (*a)[aoffset+2]; @@ -946,7 +1383,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, (*a)[aoffset+7] = m2y-m3y; return; } - if( n==5 ) { + if( n==5) { offs = curplan.plan[entryoffset+7]; c1 = curplan.precomputed[offs+0]; c2 = curplan.precomputed[offs+1]; @@ -996,18 +1433,18 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, return; } } - if( curplan.plan[entryoffset+3]==ftbase_fhtcodeletplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fhtcodeletplan) { n1 = curplan.plan[entryoffset+1]; n2 = curplan.plan[entryoffset+2]; n = n1*n2; - if( n==2 ) { + if( n==2) { a0x = (*a)[aoffset+0]; a1x = (*a)[aoffset+1]; (*a)[aoffset+0] = a0x+a1x; (*a)[aoffset+1] = a0x-a1x; return; } - if( n==3 ) { + if( n==3) { offs = curplan.plan[entryoffset+7]; c1 = curplan.precomputed[offs+0]; c2 = curplan.precomputed[offs+1]; @@ -1024,7 +1461,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, (*a)[aoffset+2] = s1x+m2y; return; } - if( n==4 ) { + if( n==4) { a0x = (*a)[aoffset+0]; a1x = (*a)[aoffset+1]; a2x = (*a)[aoffset+2]; @@ -1039,7 +1476,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, (*a)[aoffset+3] = m2x+m3y; return; } - if( n==5 ) { + if( n==5) { offs = curplan.plan[entryoffset+7]; c1 = curplan.precomputed[offs+0]; c2 = curplan.precomputed[offs+1]; @@ -1067,7 +1504,7 @@ void PIFFT::ftbaseexecuteplanrec(PIVector* a, int aoffset, ftplan* plan, return; } } - if( curplan.plan[entryoffset+3]==ftbase_fftbluesteinplan ) { + if( curplan.plan[entryoffset+3]==ftbase_fftbluesteinplan) { n = curplan.plan[entryoffset+1]; m = curplan.plan[entryoffset+4]; offs = curplan.plan[entryoffset+7]; @@ -1148,8 +1585,8 @@ void PIFFT::ftbase_ffttwcalc(PIVector * a, int aoffset, int n1, int n2) tmpy = x*twy+y*twxm1; (*a)[offs+0] = x+tmpx; (*a)[offs+1] = y+tmpy; - if( j * a, int aoffset, int n1, int n2) } } - if( i * a, int aoffset, int n1, int n2) } } } - - - -PIStatistic::PIStatistic() { - mean = 0.; - variance = 0.; - skewness = 0.; - kurtosis = 0.; -} - - -bool PIStatistic::calculate(const PIVector & val) { - double v = 0., v1 = 0., v2 = 0., stddev = 0.; - int i, n = val.size(); - if (n < 2) - return false; - /* - * Mean - */ - for (i = 0; i < n; i++) - mean += val[i]; - mean /= n; - /* - * Variance (using corrected two-pass algorithm) - */ - for (i = 0; i < n; i++) - v1 += sqr(val[i] - mean); - for (i = 0; i < n; i++) - v2 += val[i] - mean; - v2 = sqr(v2) / n; - variance = (v1 - v2) / (n - 1); - if(variance < 0) - variance = 0.; - stddev = sqrt(variance); - /* - * Skewness and kurtosis - */ - if (stddev != 0) { - for (i = 0; i < n; i++) { - v = (val[i] - mean) / stddev; - v2 = sqr(v); - skewness = skewness + v2 * v; - kurtosis = kurtosis + sqr(v2); - } - skewness /= n; - kurtosis = kurtosis / n - 3.; - } - return true; -} diff --git a/pimath.h b/pimath.h index 2a4a7cbf..310546bf 100644 --- a/pimath.h +++ b/pimath.h @@ -35,6 +35,18 @@ #ifndef M_LN10 # define M_LN10 2.30258509299404568402 #endif +#ifndef M_SQRT2 +# define M_SQRT2 1.41421356237309514547 +#endif +#ifndef M_SQRT3 +# define M_SQRT3 1.73205080756887719318 +#endif +#ifndef M_1_SQRT2 +# define M_1_SQRT2 0.70710678118654746172 +#endif +#ifndef M_1_SQRT3 +# define M_1_SQRT3 0.57735026918962584208 +#endif #ifndef M_PI # define M_PI 3.14159265358979323846 #endif @@ -53,6 +65,12 @@ #ifndef M_PI_180 # define M_PI_180 1.74532925199432957692e-2 #endif +#ifndef M_E +# define M_E 2.7182818284590452353602874713527 +#endif +#ifndef M_LIGHT_SPEED +# define M_LIGHT_SPEED 2.99792458e+8 +#endif using std::complex; @@ -70,7 +88,12 @@ const complexd complexd_1(1.); const double deg2rad = M_PI_180; const double rad2deg = M_180_PI; +inline int sign(const float & x) {return (x < 0.) ? -1 : (x > 0. ? 1 : 0);} +inline int sign(const double & x) {return (x < 0.) ? -1 : (x > 0. ? 1 : 0);} +inline complexd sign(const complexd & x) {return complexd(sign(x.real()), sign(x.imag()));} inline int pow2(const int p) {return 1 << p;} +inline double sqr(const int v) {return v * v;} +inline double sqr(const float & v) {return v * v;} inline double sqr(const double & v) {return v * v;} inline double sinc(const double & v) {if (v == 0.) return 1.; double t = M_PI * v; return sin(t) / t;} inline complexd round(const complexd & c) {return complexd(piRound(c.real()), piRound(c.imag()));} @@ -79,23 +102,42 @@ inline complexd ceil(const complexd & c) {return complexd(ceil(c.real()), ceil(c inline complexd atanc(const complexd & c) {return -complexd(-0.5, 1.) * log((complexd_1 + complexd_i * c) / (complexd_1 - complexd_i * c));} inline complexd asinc(const complexd & c) {return -complexd_i * log(complexd_i * c + sqrt(complexd_1 - c * c));} inline complexd acosc(const complexd & c) {return -complexd_i * log(c + complexd_i * sqrt(complexd_1 - c * c));} -#if CC_GCC_VERSION <= 0x025F +#ifdef CC_GCC +# if CC_GCC_VERSION <= 0x025F inline complexd tan(const complexd & c) {return sin(c) / cos(c);} inline complexd tanh(const complexd & c) {return sinh(c) / cosh(c);} inline complexd log2(const complexd & c) {return log(c) / M_LN2;} inline complexd log10(const complexd & c) {return log(c) / M_LN10;} -inline double j0(const double & v) {return v;} -inline double j1(const double & v) {v;} -inline double jn(const int & n, const double & v) {return v;} -inline double y0(const double & v) {return v;} -inline double y1(const double & v) {return v;} -inline double yn(const int & n, const double & v) {return v;} +# endif +#endif +#ifndef PIP_MATH_J0 + __attribute__ ((unused)) static double j0(const double & v); +#endif +#ifndef PIP_MATH_J1 +__attribute__ ((unused)) static double j1(const double & v); +#endif +#ifndef PIP_MATH_JN +__attribute__ ((unused)) static double jn(const int & n, const double & v); +#endif +#ifndef PIP_MATH_Y0 +__attribute__ ((unused)) static double y0(const double & v); +#endif +#ifndef PIP_MATH_Y1 +__attribute__ ((unused)) static double y1(const double & v); +#endif +#ifndef PIP_MATH_YN +__attribute__ ((unused)) static double yn(const int & n, const double & v); #endif inline double toDb(double val) {return 10. * log10(val);} inline double fromDb(double val) {return pow(10., val / 10.);} inline double toRad(double deg) {return deg * M_PI_180;} inline double toDeg(double rad) {return rad * M_180_PI;} +// [-1 ; 1] +inline double randomd() {return (double)random() / RAND_MAX * 2. - 1.;} +// [-1 ; 1] normal +double randomn(double dv = 0., double sv = 1.); + inline PIVector abs(const PIVector & v) { PIVector result; result.resize(v.size()); @@ -152,7 +194,7 @@ public: Type at(uint index) const {return c[index];} Type & operator [](uint index) {return c[index];} Type operator [](uint index) const {return c[index];} - void operator =(const _CVector & v) {c = v.c;} + _CVector & operator =(const _CVector & v) {c = v.c; return *this;} bool operator ==(const _CVector & v) const {PIMV_FOR(i, 0) if (c[i] != v[i]) return false; return true;} bool operator !=(const _CVector & v) const {return !(*this == c);} void operator +=(const _CVector & v) {PIMV_FOR(i, 0) c[i] += v[i];} @@ -166,6 +208,7 @@ public: _CVector operator -(const _CVector & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] -= v[i]; return tv;} _CVector operator *(const Type & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] *= v; return tv;} _CVector operator /(const Type & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] /= v; return tv;} + _CVector operator /(const _CVector & v) const {_CVector tv = _CVector(*this); PIMV_FOR(i, 0) tv[i] /= v[i]; return tv;} _CVector operator *(const _CVector & v) const {if (Size > 3) return _CVector(); _CVector tv; tv.fill(Type(1)); tv[0] = c[1]*v[2] - v[1]*c[2]; tv[1] = v[0]*c[2] - c[0]*v[2]; tv[2] = c[0]*v[1] - v[0]*c[1]; return tv;} Type operator ^(const _CVector & v) const {Type tv(0); PIMV_FOR(i, 0) tv += c[i] * v[i]; return tv;} @@ -187,7 +230,13 @@ private: template inline std::ostream & operator <<(std::ostream & s, const PIMathVectorT & v) {s << '{'; PIMV_FOR(i, 0) {s << v[i]; if (i < Size - 1) s << ", ";} s << '}'; return s;} template +inline PICout operator <<(PICout s, const PIMathVectorT & v) {s << '{'; PIMV_FOR(i, 0) {s << v[i]; if (i < Size - 1) s << ", ";} s << '}'; return s;} +template inline bool operator ||(const PIMathVectorT & f, const PIMathVectorT & s) {return (f * s).isNull();} +template +inline PIMathVectorT sqrt(const PIMathVectorT & v) {PIMathVectorT ret; PIMV_FOR(i, 0) {ret[i] = sqrt(v[i]);} return ret;} +template +inline PIMathVectorT sqr(const PIMathVectorT & v) {PIMathVectorT ret; PIMV_FOR(i, 0) {ret[i] = sqr(v[i]);} return ret;} //template /// vector {Size0, Type0} to vector {Size1, Type1} //inline operator PIMathVectorT(const PIMathVectorT & v) {PIMathVectorT tv; uint sz = piMin(Size0, Size1); for (uint i = 0; i < sz; ++i) tv[i] = v[i]; return tv;} @@ -220,6 +269,10 @@ public: PIMathMatrixT(const PIVector & val) {resize(Cols, Rows); int i = 0; PIMM_FOR_I_WB(c, r) m[c][r] = val[i++];} static _CMatrix identity() {_CMatrix tm = _CMatrix(); PIMM_FOR_WB(c, r) tm.m[c][r] = (c == r ? Type(1) : Type(0)); return tm;} + static _CMatrix rotation(double angle) {return _CMatrix();} + static _CMatrix rotationX(double angle) {return _CMatrix();} + static _CMatrix rotationY(double angle) {return _CMatrix();} + static _CMatrix rotationZ(double angle) {return _CMatrix();} uint cols() const {return Cols;} uint rows() const {return Rows;} @@ -360,8 +413,16 @@ private: }; + +template<> inline PIMathMatrixT<2u, 2u> PIMathMatrixT<2u, 2u>::rotation(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<2u, 2u> tm; tm[0][0] = tm[1][1] = c; tm[0][1] = -s; tm[1][0] = s; return tm;} +template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::rotationX(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<3u, 3u> tm; tm[0][0] = 1.; tm[1][1] = tm[2][2] = c; tm[2][1] = -s; tm[1][2] = s; return tm;} +template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::rotationY(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<3u, 3u> tm; tm[1][1] = 1.; tm[0][0] = tm[2][2] = c; tm[2][0] = s; tm[0][2] = -s; return tm;} +template<> inline PIMathMatrixT<3u, 3u> PIMathMatrixT<3u, 3u>::rotationZ(double angle) {double c = cos(angle), s = sin(angle); PIMathMatrixT<3u, 3u> tm; tm[2][2] = 1.; tm[0][0] = tm[1][1] = c; tm[1][0] = -s; tm[0][1] = s; return tm;} + template inline std::ostream & operator <<(std::ostream & s, const PIMathMatrixT & m) {s << '{'; PIMM_FOR_I(c, r) s << m[c][r]; if (c < Cols - 1 || r < Rows - 1) s << ", ";} if (r < Rows - 1) s << endl << ' ';} s << '}'; return s;} +template +inline PICout operator <<(PICout s, const PIMathMatrixT & m) {s << '{'; PIMM_FOR_I(c, r) s << m[c][r]; if (c < Cols - 1 || r < Rows - 1) s << ", ";} if (r < Rows - 1) s << NewLine << ' ';} s << '}'; return s;} /// Multiply matrices {CR x Rows0} on {Cols1 x CR}, result is {Cols1 x Rows0} template @@ -487,6 +548,8 @@ private: template inline std::ostream & operator <<(std::ostream & s, const PIMathVector & v) {s << '{'; for (uint i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << '}'; return s;} +template +inline PICout operator <<(PICout s, const PIMathVector & v) {s << '{'; for (uint i = 0; i < v.size(); ++i) {s << v[i]; if (i < v.size() - 1) s << ", ";} s << '}'; return s;} typedef PIMathVector PIMathVectori; typedef PIMathVector PIMathVectord; @@ -658,6 +721,8 @@ private: template inline std::ostream & operator <<(std::ostream & s, const PIMathMatrix & m) {s << '{'; for (uint r = 0; r < m.rows(); ++r) { for (uint c = 0; c < m.cols(); ++c) { s << m[c][r]; if (c < m.cols() - 1 || r < m.rows() - 1) s << ", ";} if (r < m.rows() - 1) s << endl << ' ';} s << '}'; return s;} +template +inline PICout operator <<(PICout s, const PIMathMatrix & m) {s << '{'; for (uint r = 0; r < m.rows(); ++r) { for (uint c = 0; c < m.cols(); ++c) { s << m[c][r]; if (c < m.cols() - 1 || r < m.rows() - 1) s << ", ";} if (r < m.rows() - 1) s << NewLine << ' ';} s << '}'; return s;} /// Multiply matrices {CR x Rows0} on {Cols1 x CR}, result is {Cols1 x Rows0} template @@ -829,14 +894,116 @@ private: }; +template class PIP_EXPORT PIStatistic { public: - PIStatistic(); - bool calculate(const PIVector &val); - double mean; - double variance; - double skewness; - double kurtosis; + PIStatistic() { + mean = T(); + variance = T(); + skewness = T(); + kurtosis = T(); + } + bool calculate(const PIVector &val) { + T v = T(), v1 = T(), v2 = T(), stddev = T(); + int i, n = val.size(); + mean = T(); + variance = T(); + skewness = T(); + kurtosis = T(); + if (n < 2) + return false; + /* + * Mean + */ + for (i = 0; i < n; i++) + mean += val[i]; + mean /= n; + /* + * Variance (using corrected two-pass algorithm) + */ + for (i = 0; i < n; i++) { + v1 += sqr(val[i] - mean); + } + for (i = 0; i < n; i++) + v2 += val[i] - mean; + v2 = sqr(v2) / n; + variance = (v1 - v2) / (n - 1); + if (variance < T()) + variance = T(); + stddev = sqrt(variance); + /* + * Skewness and kurtosis + */ + if (stddev != T()) { + for (i = 0; i < n; i++) { + v = (val[i] - mean) / stddev; + v2 = sqr(v); + skewness = skewness + v2 * v; + kurtosis = kurtosis + sqr(v2); + } + skewness /= n; + kurtosis = kurtosis / n - 3.; + } + return true; + } + T mean; + T variance; + T skewness; + T kurtosis; }; +typedef PIStatistic PIStatistici; +typedef PIStatistic PIStatisticf; +typedef PIStatistic PIStatisticd; + + +template +bool OLS_Linear(const PIVector > & input, T * out_a, T * out_b) { + if (input.size_s() < 2) + return false; + int n = input.size_s(); + T a_t0 = T(), a_t1 = T(), a_t2 = T(), a_t3 = T(), a_t4 = T(), a = T(), b = T(); + for (int i = 0; i < n; ++i) { + const PIPair & cv(input[i]); + a_t0 += cv.first * cv.second; + a_t1 += cv.first; + a_t2 += cv.second; + a_t3 += cv.first * cv.first; + } + a_t4 = n * a_t3 - a_t1 * a_t1; + if (a_t4 != T()) + a = (n * a_t0 - a_t1 * a_t2) / a_t4; + b = (a_t2 - a * a_t1) / n; + if (out_a != 0) *out_a = a; + if (out_b != 0) *out_b = b; + return true; +} + + +template +bool WLS_Linear(const PIVector > & input, const PIVector & weights, T * out_a, T * out_b) { + if (input.size_s() < 2) + return false; + if (input.size_s() != weights.size_s()) + return false; + int n = input.size_s(); + T a_t0 = T(), a_t1 = T(), a_t2 = T(), a_t3 = T(), a_t4 = T(), a_n = T(), a = T(), b = T(); + for (int i = 0; i < n; ++i) { + T cp = weights[i]; + const PIPair & cv(input[i]); + a_t0 += cv.first * cv.second * cp; + a_t1 += cv.first * cp; + a_t2 += cv.second * cp; + a_t3 += cv.first * cv.first * cp; + a_n += cp; + } + a_t4 = a_n * a_t3 - a_t1 * a_t1; + if (a_t4 != T()) + a = (a_n * a_t0 - a_t1 * a_t2) / a_t4; + b = (a_t2 - a * a_t1) / a_n; + if (out_a != 0) *out_a = a; + if (out_b != 0) *out_b = b; + return true; +} + #endif // PIMATH_H diff --git a/pimultiprotocol.h b/pimultiprotocol.h index 362fd357..ec91bd53 100644 --- a/pimultiprotocol.h +++ b/pimultiprotocol.h @@ -64,12 +64,12 @@ public: PIRepeater(const PIString & config, const PIString & name) { PIConfig conf(config, PIIODevice::ReadOnly); if (!conf.isOpened()) { - piCout << "[PIRepeater \"" << name << "\"] Can`t open \"" << config << "\"!"; + piCoutObj << "[PIRepeater \"" << name << "\"] Can`t open \"" << config << "\"!"; return; } PIConfig::Entry & b(conf.getValue(name)); if (b.childCount() != 2) { - piCout << "[PIRepeater \"" << name << "\"] \"" << config << "\" should consist 2 nodes!"; + piCoutObj << "[PIRepeater \"" << name << "\"] \"" << config << "\" should consist 2 nodes!"; return; } addProtocol(config, b.child(0)->fullName()); diff --git a/piobject.cpp b/piobject.cpp index fb0d61d5..01997d42 100644 --- a/piobject.cpp +++ b/piobject.cpp @@ -21,6 +21,41 @@ PIVector PIObject::objects; + +void PIObject::piConnect(const PIString & src, const PIString & sig, void * dest, void * ev_h) { + PIObject * o = findByName(src); + if (o == 0) { + piCout << "[PIObject] Can`t find object with name \"" << src << "\"!"; + return; + } + o->connections << Connection(ev_h, 0, sig, dest); +} + + +void PIObject::piConnect(PIObject * src, const PIString & sig, const PIString & dest, void * ev_h) { + PIObject * o = findByName(dest); + if (o == 0) { + piCout << "[PIObject] Can`t find object with name \"" << dest << "\"!"; + return; + } + src->connections << Connection(ev_h, 0, sig, o); +} + + +void PIObject::piConnect(const PIString & src, const PIString & sig, const PIString & dest, void * ev_h) { + PIObject * s = findByName(src); + if (s == 0) { + piCout << "[PIObject] Can`t find object with name \"" << src << "\"!"; + return; + } + PIObject * d = findByName(dest); + if (d == 0) { + piCout << "[PIObject] Can`t find object with name \"" << dest << "\"!"; + return; + } + s->connections << Connection(ev_h, 0, sig, d); +} + /* PIStringList PIObject::events() { PIStringList l; diff --git a/piobject.h b/piobject.h index 58cc2185..c476acbc 100644 --- a/piobject.h +++ b/piobject.h @@ -253,11 +253,22 @@ class PIP_EXPORT PIObject { friend class PIObjectManager; public: - PIObject(const PIString & name = PIString()) {piMonitor.objects++; setName(name); objects << this;} + + //! Contructs PIObject with name "name" + PIObject(const PIString & name = PIString()) {piMonitor.objects++; setName(name); objects << this; debug_ = true;} ~PIObject() {piMonitor.objects--; objects.removeAll(this);} + //! Returns object name const PIString & name() const {return name_;} + + //! Set object name void setName(const PIString & name) {name_ = name;} + + //! Return if debug of this object is active + bool debug() const {return debug_;} + + //! Set object debug active + void setDebug(bool debug) {debug_ = debug;} /* PIStringList events(); PIStringList eventHandlers(); @@ -307,6 +318,11 @@ public: // / Direct connect static void piConnect(PIObject * src, const PIString & sig, void * dest, void * ev_h) {src->connections << Connection(ev_h, 0, sig, dest);} static void piConnect(PIObject * src, const PIString & sig, void * dest, void * ev_h, void * e_h) {src->connections << Connection(ev_h, e_h, sig, dest);} + + static void piConnect(const PIString & src, const PIString & sig, void * dest, void * ev_h); + static void piConnect(PIObject * src, const PIString & sig, const PIString & dest, void * ev_h); + static void piConnect(const PIString & src, const PIString & sig, const PIString & dest, void * ev_h); + static void piDisconnect(PIObject * src, const PIString & sig, void * dest, void * ev_h) { for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); @@ -325,6 +341,8 @@ public: } } } + + //! Disconnect object "src" from all connections with event name "sig" static void piDisconnect(PIObject * src, const PIString & sig) { for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); @@ -334,6 +352,8 @@ public: } } } + + //! Disconnect object "src" from all connections, i.e. all connections where object "src" is emitter static void piDisconnect(PIObject * src) {src->connections.clear();} //static void piConnect(PIObject & src, const PIString & sig, PIObject * dest, void * ev_h) {src.connections << Connection(ev_h, sig, dest);} //static void piConnect(PIObject * src, const PIString & sig, PIObject & dest, void * ev_h) {src->connections << Connection(ev_h, sig, &dest);} @@ -464,6 +484,7 @@ public: protected: PIString name_; + bool debug_; private: struct Connection { diff --git a/pip.cbp b/pip.cbp new file mode 100644 index 00000000..c03c455c --- /dev/null +++ b/pip.cbp @@ -0,0 +1,333 @@ + + + + + + diff --git a/pip.pro b/pip.pro index 19e49c2e..0767f2b3 100644 --- a/pip.pro +++ b/pip.pro @@ -9,7 +9,7 @@ INCLUDEPATH += . QT -= core gui CONFIG -= qt CONFIG += dll -VERSION = 0.3.4 +VERSION = 0.3.6 # Input HEADERS += \ diff --git a/pip.pro.user b/pip.pro.user new file mode 100644 index 00000000..b5855fd8 --- /dev/null +++ b/pip.pro.user @@ -0,0 +1,215 @@ + + + + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + true + 1 + true + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Qt4ProjectManager.Target.DesktopTarget + 0 + 0 + 0 + + ProjectExplorer.ToolChain.Mingw:C:/MinGW/bin//g++.exe.x86-windows-msys-pe-32bit.C:/Qt/qtcreator-2.4.1/pythongdb/gdb-i686-pc-mingw32.exe + + + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + Сборка + + Qt4ProjectManager.MakeStep + false + + + + 2 + Сборка + + ProjectExplorer.BuildSteps.Build + + + + Сборка + + Qt4ProjectManager.MakeStep + true + clean + + + 1 + Очистка + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Qt 4.8.0 (4.8.0) Релиз + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + E:/pprojects/pip + 6 + false + + 1 + + + 0 + Установка + + ProjectExplorer.BuildSteps.Deploy + + 1 + Без установки + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + + + false + false + false + false + false + false + false + false + true + true + 0.01 + 0.01 + 10 + 10 + true + true + 25 + 25 + + + true + true + valgrind + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + pip + + Qt4ProjectManager.Qt4RunConfiguration + 2 + + pip.pro + false + false + + + 3768 + true + false + false + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.EnvironmentId + {e0680b61-7ce7-4775-a872-e83c6b7b3dfe} + + + ProjectExplorer.Project.Updater.FileVersion + 10 + + diff --git a/pip_export.h b/pip_export.h new file mode 100644 index 00000000..4c1e544d --- /dev/null +++ b/pip_export.h @@ -0,0 +1,35 @@ + +#ifndef PIP_EXPORT_H +#define PIP_EXPORT_H + +#ifdef PIP_STATIC_DEFINE +# define PIP_EXPORT +# define PIP_NO_EXPORT +#else +# ifndef PIP_EXPORT +# ifdef pip_EXPORTS + /* We are building this library */ +# define PIP_EXPORT __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define PIP_EXPORT __attribute__((visibility("default"))) +# endif +# endif + +# ifndef PIP_NO_EXPORT +# define PIP_NO_EXPORT __attribute__((visibility("hidden"))) +# endif +#endif + +#ifndef PIP_DEPRECATED +# define PIP_DEPRECATED __attribute__ ((__deprecated__)) +# define PIP_DEPRECATED_EXPORT PIP_EXPORT __attribute__ ((__deprecated__)) +# define PIP_DEPRECATED_NO_EXPORT PIP_NO_EXPORT __attribute__ ((__deprecated__)) +#endif + +#define DEFINE_NO_DEPRECATED 0 +#if DEFINE_NO_DEPRECATED +# define PIP_NO_DEPRECATED +#endif + +#endif diff --git a/pip_resource.rc b/pip_resource.rc new file mode 100644 index 00000000..09e85416 --- /dev/null +++ b/pip_resource.rc @@ -0,0 +1,38 @@ +# if defined(UNDER_CE) +# include +# else +# include +# endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,3,6,0 + PRODUCTVERSION 0,3,6,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "\0" + VALUE "FileVersion", "0.3.6.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "pip0.dll\0" + VALUE "ProductName", "pip\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END +/* End of Version info */ + diff --git a/pip_resource_win.rc b/pip_resource_win.rc new file mode 100644 index 00000000..8b50c77f --- /dev/null +++ b/pip_resource_win.rc @@ -0,0 +1,34 @@ +# if defined(UNDER_CE) +# include +# else +# include +# endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,3,6,0 + PRODUCTVERSION 0,3,6,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Peri4\0" + VALUE "FileDescription", "Platform-Independent Primitives\0" + VALUE "FileVersion", "0.3.6.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "libpip.dll\0" + VALUE "ProductName", "PIP\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END +/* End of Version info */ + diff --git a/pip_test_resource.rc b/pip_test_resource.rc new file mode 100644 index 00000000..dc2c346c --- /dev/null +++ b/pip_test_resource.rc @@ -0,0 +1,38 @@ +# if defined(UNDER_CE) +# include +# else +# include +# endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,3,2,0 + PRODUCTVERSION 0,3,2,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "\0" + VALUE "FileVersion", "0.3.2.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "pip_test.exe\0" + VALUE "ProductName", "pip_test\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END +/* End of Version info */ + diff --git a/pipeer.cpp b/pipeer.cpp index d48de099..23dfa7bb 100644 --- a/pipeer.cpp +++ b/pipeer.cpp @@ -19,45 +19,36 @@ #include "pipeer.h" +#define _PIPEER_PORT_SYNC_START 13313 +#define _PIPEER_PORT_SYNC_END 13353 +#define _PIPEER_IP_MULTICAST "239.13.3.12" +#define _PIPEER_MSG_SIZE 8192 -PIPeer::PIPeer(const PIString & name): PIEthernet() { +PIPeer::PIPeer(const PIString & name): PIObject() { + rec_mc = rec_bc = false; setName(name); - setParameter(PIEthernet::Broadcast); - setReadPort(13312); - setSendPort(13312); + self_info.name = name_; + self_info.dist = 0; + //joinMulticastGroup("239.240.241.242"); srand(uint(PITimer::elapsed_system_m())); //id_ = name() + "_" + PIString::fromNumber(rand()); CONNECT2(void, void * , int, &timer, timeout, this, timerEvent); - - PIEthernet * ce; PIStringList sl = PIEthernet::allAddresses(); - PIString ta; - self_info.name = name_; - self_info.dist = 0; - piForeachC (PIString & i, sl) { - ce = new PIEthernet(this, func_readed); - ce->setReadAddress(i, 13313); - eths << ce; - ce->startThreadedRead(); - self_info.addresses << i; - //cout << i << ": " << ta << endl; - } - eth_send = new PIEthernet(); - eth_send->initialize(); - - startThreadedRead(); - //joinMulticastGroup("239.13.3.12"); - //timer.addDelimiter(5); + sl.removeAll("127.0.0.1"); sl << "127.0.0.1"; + initMulticasts(sl); + initEths(sl); + //piCout << "Peer" << name_; + timer.addDelimiter(5); timer.start(1000); sendSelfInfo(); } PIPeer::~PIPeer() { - terminate(); + piForeach (PIEthernet * i, mc_eths) + i->stopThreadedRead(); sendSelfRemove(); - //leaveMulticastGroup("239.13.3.12"); - delete eth_send; + destroyMulticasts(); piForeach (PIEthernet * i, eths) delete i; eths.clear(); @@ -66,7 +57,7 @@ PIPeer::~PIPeer() { void PIPeer::timerEvent(void * data, int delim) { switch (delim) { - case 1: // 5 s + case 5: // 5 s syncPeers(); break; } @@ -74,46 +65,260 @@ void PIPeer::timerEvent(void * data, int delim) { } -bool PIPeer::threadedRead(uchar * data, int size) { +void PIPeer::initEths(const PIStringList & al) { + PIEthernet * ce; + PIEthernet::InterfaceList il = PIEthernet::interfaces(); + const PIEthernet::Interface * cint = 0; + piForeachC (PIString & a, al) { + ce = new PIEthernet(); + ce->setParameters(0); + ce->setDebug(false); + for (int p = _PIPEER_PORT_SYNC_START; p < 65536; ++p) { + ce->setReadAddress(a, p); + if (ce->open()) { + eths << ce; + cint = il.getByAddress(a); + self_info.addresses << ce->path(); + self_info.netmasks << (cint == 0 ? "255.255.255.0" : cint->netmask); + CONNECT2(bool, void * , int, ce, threadedReadEvent, this, dataRead); + ce->startThreadedRead(); + //piCout << "dc binded to" << ce->path(); + //piCout << "add eth" << ta; + break; + } + } + } +} + + +void PIPeer::initMulticasts(const PIStringList & al) { + destroyMulticasts(); + PIEthernet * ce; + PIEthernet::InterfaceList il = PIEthernet::interfaces(); + const PIEthernet::Interface * cint; + PIStringList nal = al; + PIString nm; + nal << "main" << "bc"; + rec_mc = rec_bc = false; + piForeachC (PIString & a, nal) { + bool is_main = (a == "main"); + bool is_bc = (a == "bc"); + ce = new PIEthernet(); + ce->setParameters((is_main || is_bc) ? PIEthernet::Broadcast : 0); + ce->setDebug(false); + //cint = il.getByAddress(a); + //nm = (cint == 0) ? "255.255.255.0" : cint->netmask; + ce->setSendIP(is_bc ? "255.255.255.255" : _PIPEER_IP_MULTICAST); + //piCout << "mc try" << a << nm << ce->sendIP(); + for (int p = _PIPEER_PORT_SYNC_START; p < _PIPEER_PORT_SYNC_END; ++p) { + ce->setReadAddress(((is_main || is_bc) ? "255.255.255.255" : a) + ":" + PIString::fromNumber(p)); + ce->close(); + if (!ce->open()) continue; + if (is_main) if (!ce->joinMulticastGroup(_PIPEER_IP_MULTICAST)) continue; + //piCout << "mc binded to" << ce->path(); + mc_eths << ce; + if (is_main || is_bc) { + if (is_main) rec_mc = true; + if (is_bc) rec_bc = true; + CONNECT2(bool, void * , int, ce, threadedReadEvent, this, multicastRead); + ce->startThreadedRead(); + if (is_bc) ce->setParameter(PIEthernet::Broadcast, false); + } + break; + } + } + piForeachC (PIString & a, al) { + ce = new PIEthernet(); + ce->setParameters(PIEthernet::Broadcast); + ce->setDebug(false); + cint = il.getByAddress(a); + nm = (cint == 0) ? "255.255.255.0" : cint->netmask; + ce->setSendIP(PIEthernet::getBroadcast(a, nm)); + //piCout << "mc BC try" << a << nm << ce->sendIP(); + for (int p = _PIPEER_PORT_SYNC_START; p < _PIPEER_PORT_SYNC_END; ++p) { + ce->setReadAddress(a + ":" + PIString::fromNumber(p)); + ce->close(); + if (!ce->open()) continue; + //piCout << "BC binded to" << ce->path(); + mc_eths << ce; + break; + } + } + if (!rec_mc) piCoutObj << "[PIPeer \"" + name_ + "\"] Can`t find suitable network interface for multicast receive, check for exists at least one interface with multicasting enabled!"; + if (!rec_bc) piCoutObj << "[PIPeer \"" + name_ + "\"] Can`t find suitable network interface for broadcast receive, check for exists at least one interface with broadcasting enabled!"; +} + + +void PIPeer::destroyMulticasts() { + piForeach (PIEthernet * i, mc_eths) { + i->leaveMulticastGroup(_PIPEER_IP_MULTICAST); + delete i; + } + mc_eths.clear(); +} + + +PIPeer::PeerInfo * PIPeer::quickestPeer(const PIString & to) { + if (!addresses_map.contains(to)) return 0; + //piCout << "*** search quickest peer" << to; + PIVector tp = addresses_map[to]; + PeerInfo * dp = 0; + int mping = 99999; + for (int i = 0; i < tp.size_s(); ++i) { + if (mping > tp[i]->ping) { + mping = tp[i]->ping; + dp = tp[i]; + } + } + //piCout << "*** search quickest peer: found" << dp->name; + return dp; +} + + +bool PIPeer::sendToNeighbour(PIPeer::PeerInfo * peer, const PIByteArray & ba) { + if (peer->_neth == 0) return false; + //piCout << "send to" << peer->name << peer->_naddress << ba.size_s() << "bytes"; + diag_d.sended(ba.size_s()); + return peer->_neth->send(peer->_naddress, ba.data(), ba.size_s()); +} + + +bool PIPeer::send(const PIString & to, const void * data, int size) { + PeerInfo * dp = quickestPeer(to); + if (dp == 0) { + //piCoutObj << "[PIPeer \"" + name_ + "\"] Can`t find peer \"" << to << "\"!"; + return false; + } + PIByteArray ba; + ba << int(4) << self_info.name << to << int(0) << size; + PIByteArray fmsg(data, size), cmsg; + int msg_count = (size - 1) / _PIPEER_MSG_SIZE + 1; + for (int i = 0; i < msg_count; ++i) { + int csize = (i == msg_count - 1) ? ((size - 1) % _PIPEER_MSG_SIZE + 1) : _PIPEER_MSG_SIZE; + cmsg.clear(); + cmsg.append(ba); + cmsg << msg_count << i; + cmsg.append(fmsg.data(i * _PIPEER_MSG_SIZE), csize); + if (!sendToNeighbour(dp, cmsg)) return false; + } + return true; +} + + +bool PIPeer::dataRead(uchar * readed, int size) { + diag_d.received(size); + PIByteArray ba(readed, size), sba; + int type, cnt, rec_size; + PIString from, to; + ba >> type >> from >> to >> cnt >> rec_size; + //piCout << "[PIPeer \"" + name_ + "\"] Received packet" << type << from << to << cnt << rec_size; + if (type == 4) { // data packet + if (to == self_info.name) { // my packet + int msg_count, cmsg; + ba >> msg_count >> cmsg; + //piCout << "[PIPeer \"" + name_ + "\"] Received packet" << type << from << to << cnt << rec_size << msg_count << cmsg; + if (cmsg == 0 && msg_count == 1) { + dataReceived(from, ba); + dataReceivedEvent(from, ba); + return true; + } + PeerInfo * fp = const_cast(getPeerByName(from)); + if (fp == 0) return true; + PeerData & pd(fp->_data); + if (cmsg == 0) { + //piCout << "[PIPeer \"" + name_ + "\"] Packet clear" << rec_size; + pd.clear(); + pd.msg_count = msg_count; + } + //piCout << "[PIPeer \"" + name_ + "\"] Packet add" << cmsg << ba.size_s(); + pd.addData(ba); + if (pd.isFullReceived()) { + dataReceived(from, pd.data); + dataReceivedEvent(from, pd.data); + //piCout << "[PIPeer \"" + name_ + "\"] Packet received" << pd.data.size_s(); + } + return true; + } + PeerInfo * dp = quickestPeer(to); + if (dp == 0) { + //piCoutObj << "[PIPeer \"" + name_ + "\"] Can`t find peer \"" << to << "\"!"; + return true; + } + cnt++; + if (cnt > 100 || from == dp->name) return true; + sba << type << from << to << cnt << rec_size; + sba.append(ba); + //piCoutObj << "[PIPeer \"" + name_ + "\"] Translate data packet" << type << from << to << cnt << rec_size; + sendToNeighbour(dp, sba); + } + return true; +} + + +bool PIPeer::multicastRead(uchar * data, int size) { + PIMutexLocker locker(mc_mutex); + diag_s.received(size); int header; PeerInfo pi; PIByteArray ba(data, size); PIVector rpeers; ba >> header >> pi.name; + //piCout << "read type" << header << "from" << pi.name; if (pi.name == name_) return true; + //piCout << "analyz ..."; switch (header) { case 1: // new peer accepted + //piCout << "new peer packet ..."; if (hasPeer(pi.name)) break; - ba >> pi.dist >> pi.addresses; + ba >> pi.dist >> pi.addresses >> pi.netmasks >> pi.neighbours; pi.sync = 0; + if (pi.dist == 0) { + pi.addNeighbour(self_info.name); + self_info.addNeighbour(pi.name); + } peers << pi; - cout << "[PIPeer \"" + name_ + "\"] new peer \"" << pi.name << "\"" << " dist " << pi.dist << endl; + //piCoutObj << "[PIPeer \"" + name_ + "\"] new peer \"" << pi.name << "\"" << " dist " << pi.dist; pi.dist++; sendSelfInfo(); sendPeerInfo(pi); findNearestAddresses(); + peerConnected(pi.name); + peerConnectedEvent(pi.name); + //piCout << "new peer packet ok"; break; case 2: // remove peer accepted + //piCout << "remove peer packet ..."; if (removePeer(pi.name)) { - cout << "[PIPeer \"" + name_ + "\"] remove peer \"" << pi.name << "\"" << endl; + //piCoutObj << "[PIPeer \"" + name_ + "\"] remove peer \"" << pi.name << "\""; + if (pi.dist == 0) { + pi.removeNeighbour(self_info.name); + self_info.removeNeighbour(pi.name); + } sendPeerRemove(pi.name); findNearestAddresses(); + peerDisconnected(pi.name); + peerDisconnectedEvent(pi.name); } + //piCout << "remove peer packet ok"; break; case 3: // sync peers - ba >> pi.addresses >> rpeers; + //piCout << "sync packet ..."; + ba >> pi.addresses >> pi.netmasks >> pi.neighbours >> rpeers; rpeers << pi; - //cout << "[PIPeer \"" + name_ + "\"] rec sync " << rpeers.size_s() << " peers" << endl; + //piCout << "[PIPeer \"" + name_ + "\"] rec sync " << rpeers.size_s() << " peers"; for (uint i = 0; i < rpeers.size(); ++i) { PeerInfo & rpeer(rpeers[i]); - //cout << " to sync " << rpeer.name << endl; + //piCout << " to sync " << rpeer.name; if (rpeer.name == name_) continue; bool exist = false; for (uint j = 0; j < peers.size(); ++j) { PeerInfo & peer(peers[j]); if (peer.name == rpeer.name) { - //cout << "synced " << peer.name << endl; + //piCout << "synced " << peer.name; peer.addresses == rpeer.addresses; + peer.netmasks == rpeer.netmasks; + peer.addNeighbours(rpeer.neighbours); + rpeer.neighbours = peer.neighbours; if (peer.name == pi.name) peer.sync = 0; exist = true; break; @@ -123,6 +328,17 @@ bool PIPeer::threadedRead(uchar * data, int size) { peers << rpeer; peers.back().dist++; findNearestAddresses(); + peerConnected(rpeer.name); + peerConnectedEvent(rpeer.name); + } + //piCout << "***";; + //piCout << self_info.name << self_info.neighbours; + piForeach (PeerInfo & i, peers) { + if (i.dist == 0) { + self_info.addNeighbour(i.name); + i.addNeighbour(self_info.name); + } + //piCout << i.name << i.neighbours; } break; } @@ -130,57 +346,146 @@ bool PIPeer::threadedRead(uchar * data, int size) { } -bool PIPeer::func_readed(void * peer, uchar * data, int size) { - PIPeer * p = (PIPeer * )peer; - cout << "[PIPeer \"" + p->name_ + "\"] received " << data << endl; - return true; +void PIPeer::sendMulticast(const PIByteArray & ba) { + piForeach (PIEthernet * e, mc_eths) { + for (int p = _PIPEER_PORT_SYNC_START; p < _PIPEER_PORT_SYNC_END; ++p) { + e->setSendPort(p); + //errorClear(); + //piCout << "send to" << e->sendAddress() << e->send(ba); + //piCout << PIEthernet::ethErrorString(); + e->send(ba); + diag_s.sended(ba.size_s()); + } + } } void PIPeer::sendPeerInfo(const PeerInfo & info) { PIByteArray ba; - ba << int(1) << info.name << info.dist << info.addresses; - write(ba); + ba << int(1) << info.name << info.dist << info.addresses << info.netmasks << info.neighbours; + sendMulticast(ba); } void PIPeer::sendPeerRemove(const PIString & peer) { PIByteArray ba; ba << int(2) << peer; - write(ba); + sendMulticast(ba); } void PIPeer::syncPeers() { - //cout << "[PIPeer \"" + name_ + "\"] sync " << peers.size_s() << " peers" << endl; + //piCout << "[PIPeer \"" + name_ + "\"] sync " << peers.size_s() << " peers"; PIString pn; + bool change = false; for (uint i = 0; i < peers.size(); ++i) { PeerInfo & cp(peers[i]); - if (cp.sync > 1 && cp.dist == 0) { + if (cp.sync > 3 && cp.dist == 0) { pn = cp.name; - cout << "[PIPeer \"" + name_ + "\"] sync: remove " << pn << endl; + //piCoutObj << "[PIPeer \"" + name_ + "\"] sync: remove " << pn; peers.remove(i); sendPeerRemove(pn); --i; - findNearestAddresses(); + piForeach (PeerInfo & p, peers) + p.removeNeighbour(pn); + self_info.removeNeighbour(pn); + peerDisconnected(pn); + peerDisconnectedEvent(pn); + change = true; continue; } cp.sync++; } + if (change) findNearestAddresses(); PIByteArray ba; - ba << int(3) << self_info.name << self_info.addresses << peers; - write(ba); + ba << int(3) << self_info.name << self_info.addresses << self_info.netmasks << self_info.neighbours << peers; + sendMulticast(ba); } void PIPeer::findNearestAddresses() { - cout << "[PIPeer \"" + name_ + "\"] findNearestAddresses" << endl; + //piCout << "[PIPeer \"" + name_ + "\"] findNearestAddresses"; + addresses_map.clear(); int max_dist = -1; - piForeach (PeerInfo & i, peers) + static PIMap peers_; + peers_.clear(); + self_info._nuses.resize(self_info.neighbours.size()); + self_info._nuses.fill(0); + self_info._first = &self_info; + peers_[self_info.name] = &self_info; + piForeach (PeerInfo & i, peers) { + i._nuses.resize(i.neighbours.size()); + i._nuses.fill(0); + i._first = 0; + peers_[i.name] = &i; if (max_dist < i.dist) max_dist = i.dist; - PIVector cwave; - for (int d = 0; d <= max_dist; ++d) { - //if () + if (i.dist > 0) continue; + i._naddress.clear(); + i._neth = 0; + PIString mma, ma; + bool af = false; + for (int mi = 0; mi < self_info.addresses.size_s(); ++mi) { + PIString & m(self_info.addresses[mi]), & mmask(self_info.netmasks[mi]); + if (af) break; + ma = m; + //mma = m.left(m.findLast(".")); + mma = PIEthernet::applyMask(m, mmask); + for (int ii = 0; ii < i.addresses.size_s(); ++ii) { + PIString & r(i.addresses[ii]), & rmask(i.netmasks[ii]); + if (mma == PIEthernet::applyMask(r, rmask)) { + i._naddress = r; + //piCout << "_naddress" << i.name << "=" << r; + af = true; + break; + } + } + } + if (!af) continue; + //piCout << " peer" << i.name << ma; + piForeach (PIEthernet * e, eths) + if (e->readAddress() == ma) { + i._neth = e; + break; + } + //piCout << i.name << i._naddress; } + PIVector cwave, nwave; + PeerInfo * npeer; + cwave << &self_info; + for (int d = 0; d <= max_dist; ++d) { + if (cwave.isEmpty()) break; + nwave.clear(); + piForeach (PeerInfo * p, cwave) { + int ns = p->neighbours.size_s(); + for (int n = 0; n < ns; ++n) { + if (p->_nuses[n] >= ns) continue; + p->_nuses[n]++; + npeer = peers_[p->neighbours[n]]; + if (npeer == 0) continue; + if (d == 0) npeer->_first = npeer; + else { + if (d == 1) npeer->_first = p; + else npeer->_first = p->_first; + } + nwave << npeer; + } + } + cwave = nwave; + //piCout << "wave" << d; + for (int i = 0; i < cwave.size_s(); ++i) { + //piCout << " peer" << cwave[i]->name << Hex << (uint)(cwave[i]->_first); + if (cwave[i]->_first == 0) {cwave.remove(i); --i; continue;} + if (addresses_map.contains(cwave[i]->name)) {cwave.remove(i); --i; continue;} + } + for (int i = 0; i < cwave.size_s(); ++i) { + PIVector & pl(addresses_map[cwave[i]->name]); + if (!pl.contains(cwave[i]->_first)) + pl << cwave[i]->_first; + } + } + /*piCout << " ** addresses map **"; + piForeachC (napair & i, addresses_map) + piCout << i.first << i.second; + piCout << " ** addresses map end **";*/ } diff --git a/pipeer.h b/pipeer.h index 698000df..2c971a51 100644 --- a/pipeer.h +++ b/pipeer.h @@ -1,6 +1,6 @@ /* PIP - Platform Independent Primitives - Peer - named I/O ethernet node + Peer - named I/O ethernet node, forming self-organized peering network Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com This program is free software: you can redistribute it and/or modify @@ -21,35 +21,103 @@ #define PIPEER_H #include "piethernet.h" +#include "pidiagnostics.h" -class PIP_EXPORT PIPeer: public PIEthernet +class PIP_EXPORT PIPeer: public PIObject { PIOBJECT(PIPeer) - struct PeerInfo; - friend PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v); - friend PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v); +private: + struct PeerData { + PeerData() {msg_count = msg_rec = 0;} + void clear() {msg_count = msg_rec = 0; data.clear();} + bool isEmpty() const {return msg_count == 0;} + bool isFullReceived() const {return msg_count == msg_rec;} + void addData(const PIByteArray & ba) {data.append(ba); msg_rec++;} + void setData(const PIByteArray & ba) {data = ba; msg_rec = 0; msg_count = (data.size_s() - 1) / 4096 + 1;} + PIByteArray data; + int msg_count; + int msg_rec; + }; + public: PIPeer(const PIString & name); - ~PIPeer(); + virtual ~PIPeer(); - //const PIString & id() const {return id_;} + class PeerInfo { + friend class PIPeer; + friend PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v); + friend PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v); + public: + PeerInfo() {dist = sync = ping = 0; _neth = 0; _first = 0;} + + PIString name; + PIStringList addresses; + int dist; + int ping; + + bool isNeighbour() const {return dist == 0;} + + protected: + void addNeighbour(const PIString & n) {if (!neighbours.contains(n)) neighbours << n;} + void addNeighbours(const PIStringList & l) {piForeachC (PIString & n, l) if (!neighbours.contains(n)) neighbours << n;} + void removeNeighbour(const PIString & n) {neighbours.removeAll(n);} + + PIString nearest_address; + PIStringList netmasks; + PIStringList neighbours; + int sync; + PIString _naddress; + PIEthernet * _neth; + PIVector _nuses; + PeerInfo * _first; + PeerData _data; + + }; + + friend PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v); + friend PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v); + bool send(const PIString & to, const PIByteArray & data) {return send(to, data.data(), data.size_s());} + bool send(const PIString & to, const PIString & data) {return send(to, data.data(), data.size_s());} + bool send(const PIString & to, const void * data, int size); + bool send(const PeerInfo & to, const PIByteArray & data) {return send(to.name, data.data(), data.size_s());} + bool send(const PeerInfo & to, const PIString & data) {return send(to.name, data.data(), data.size_s());} + bool send(const PeerInfo & to, const void * data, int size) {return send(to.name, data, size);} + bool send(const PeerInfo * to, const PIByteArray & data) {if (to == 0) return false; return send(to->name, data.data(), data.size_s());} + bool send(const PeerInfo * to, const PIString & data) {if (to == 0) return false; return send(to->name, data.data(), data.size_s());} + bool send(const PeerInfo * to, const void * data, int size) {if (to == 0) return false; return send(to->name, data, size);} + void sendToAll(const PIByteArray & data) {piForeachC (PeerInfo & i, peers) send(i.name, data.data(), data.size_s());} + void sendToAll(const PIString & data) {piForeachC (PeerInfo & i, peers) send(i.name, data.data(), data.size_s());} + void sendToAll(const void * data, int size) {piForeachC (PeerInfo & i, peers) send(i.name, data, size);} + + bool isMulticastReceive() const {return rec_mc;} + bool isBroadcastReceive() const {return rec_bc;} + + PIDiagnostics & diagnosticService() {return diag_s;} + PIDiagnostics & diagnosticData() {return diag_d;} + + const PIVector & allPeers() const {return peers;} + bool isPeerExists(const PIString & name) const {return getPeerByName(name) != 0;} + + const PeerInfo * getPeerByName(const PIString & name) const {piForeachC (PeerInfo & i, peers) if (i.name == name) return &i; return 0;} + + void lock() {mc_mutex.lock();} + void unlock() {mc_mutex.unlock();} + + EVENT2(dataReceivedEvent, const PIString &, from, const PIByteArray &, data); + EVENT1(peerConnectedEvent, const PIString &, name); + EVENT1(peerDisconnectedEvent, const PIString &, name); + protected: - bool threadedRead(uchar * readed, int size); + virtual void dataReceived(const PIString & from, const PIByteArray & data) {;} + virtual void peerConnected(const PIString & name) {;} + virtual void peerDisconnected(const PIString & name) {;} + + EVENT_HANDLER2(bool, dataRead, uchar *, readed, int, size); + EVENT_HANDLER2(bool, multicastRead, uchar *, readed, int, size); private: EVENT_HANDLER2(void, timerEvent, void * , data, int, delim); - static bool func_readed(void * peer, uchar * data, int size); - - struct PeerInfo { - PIString name; - PIString nearest_address; - PIStringList addresses; - int dist; - int sync; - int _wave; - }; - bool hasPeer(const PIString & name) {piForeachC (PeerInfo & i, peers) if (i.name == name) return true; return false;} bool removePeer(const PIString & name) {for (uint i = 0; i < peers.size(); ++i) if (peers[i].name == name) {peers.remove(i); return true;} return false;} @@ -60,23 +128,36 @@ private: void sendSelfRemove() {sendPeerRemove(name_);} void syncPeers(); void findNearestAddresses(); + void initEths(const PIStringList & al); + void initMulticasts(const PIStringList & al); + void destroyMulticasts(); + void sendMulticast(const PIByteArray & ba); + + PeerInfo * quickestPeer(const PIString & to); + bool sendToNeighbour(PeerInfo * peer, const PIByteArray & ba); struct PeerPacket { - int header; // 1 - new peer, 2 - remove peer, 3 - sync peers - + int header; // 1 - new peer, 2 - remove peer, 3 - sync peers, 4 - data + // Data packet: 4, from, to, ticks, data_size, data }; - PIList eths; - PIEthernet * eth_send; + typedef std::pair > napair; + + PIVector eths; + PIVector mc_eths; PITimer timer; + PIMutex mc_mutex; + bool rec_mc, rec_bc; PeerInfo self_info; PIVector peers; + PIMap > addresses_map; // map {"to" = list of nearest peers} + PIDiagnostics diag_s, diag_d; //PIString id_; }; -inline PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v) {s << v.name << v.addresses << v.dist; return s;} -inline PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v) {s >> v.name >> v.addresses >> v.dist; return s;} +inline PIByteArray & operator <<(PIByteArray & s, const PIPeer::PeerInfo & v) {s << v.name << v.addresses << v.netmasks << v.dist << v.neighbours; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIPeer::PeerInfo & v) {s >> v.name >> v.addresses >> v.netmasks >> v.dist >> v.neighbours; return s;} #endif // PIPEER_H diff --git a/piprocess.cpp b/piprocess.cpp index 537a8c54..1f47de8b 100644 --- a/piprocess.cpp +++ b/piprocess.cpp @@ -141,12 +141,12 @@ void PIProcess::run() { WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); } else - piCout << "[PIProcess] \"CreateProcess\" error, " << errorString(); + piCoutObj << "[PIProcess] \"CreateProcess\" error, " << errorString(); #else //cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl; if (execve(str.c_str(), a, e) < 0) - piCout << "[PIProcess] \"execve\" error, " << errorString(); + piCoutObj << "[PIProcess] \"execve\" error, " << errorString(); } else { msleep(1); //cout << "wait" << endl; diff --git a/piprotocol.cpp b/piprotocol.cpp index 51a00db7..8602db70 100644 --- a/piprotocol.cpp +++ b/piprotocol.cpp @@ -26,7 +26,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re PIObject::setName(name); PIConfig conf(config, PIIODevice::ReadOnly); if (!conf.isOpened()) { - piCout << "[PIProtocol \"" << name << "\"] Can`t open \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Can`t open \"" << config << "\"!"; devReceiverState = devSenderState = "Config error"; return; } @@ -41,7 +41,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re /// receiver section if (rb.isEntryExists("ip") && rb.isEntryExists("device")) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receiver type in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receiver type in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -50,7 +50,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) dev = gdev; if (gok && ok && (dev != gdev)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receiver type in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receiver type in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -59,7 +59,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) ps = gps; if (gok && ok && (ps != gps)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receive port in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receive port in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -74,7 +74,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) flag = gflag; if (gok && ok && (flag != gflag)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -85,18 +85,18 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) freq = gfreq; if (gok && ok && (freq != gfreq)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!"; devReceiverState = "Config error"; return; } eth->setReopenTimeout(freq * 1000); } if (recDataPtr == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null receive data pointer!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null receive data pointer!"; if (recDataSize == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null receive data size!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null receive data size!"; } else { - piCout << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".receiver.port\" or \"" << name << ".port\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".receiver.port\" or \"" << name << ".port\" in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -106,7 +106,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) dev = gdev; if (gok && ok && (dev != gdev)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receiver type in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receiver type in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -115,7 +115,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) ps = gps; if (gok && ok && (ps != gps)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receive \"speed\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receive \"speed\" in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -124,7 +124,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) flag = gflag; if (gok && ok && (flag != gflag)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receive \"parity\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receive \"parity\" in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -135,7 +135,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) flag = gflag; if (gok && ok && (flag != gflag)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receive \"twoStopBits\" parity in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receive \"twoStopBits\" parity in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -154,7 +154,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) ps = gps; if (gok && ok && (ps != gps)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receive \"vtime\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receive \"vtime\" in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -162,11 +162,11 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re } has_dev = true; if (recDataPtr == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null receive data pointer!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null receive data pointer!"; if (recDataSize == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null receive data size!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null receive data size!"; } else { - piCout << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".receiver.speed\" or \"" << name << ".speed\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".receiver.speed\" or \"" << name << ".speed\" in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -176,7 +176,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) history_write_rec = ghist; if (gok && ok && (history_write_rec != ghist)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous receiver history in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous receiver history in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -187,7 +187,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re history_id_rec = rb.getValue("historyID", 0, &ok); if (!ok) { history_id_rec = protName.toByteArray().checksumCRC16(); - piCout << "[PIProtocol \"" << name << "\"] Warning: no receiver history ID defined, write with ID = " << history_id_rec; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: no receiver history ID defined, write with ID = " << history_id_rec; } history_file_rec.open(history_path_rec, PIIODevice::WriteOnly); } @@ -196,21 +196,21 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re gfreq = b.getValue("frequency", -1.f, &gok); if (gok && !ok) freq = gfreq; if (gok && ok && (freq != gfreq)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous expected frequency in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous expected frequency in \"" << config << "\"!"; devReceiverState = "Config error"; return; } if (freq > 0.f && !has_dev) - piCout << "[PIProtocol \"" << name << "\"] Warning: no receiver device and not null expected frequency!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: no receiver device and not null expected frequency!"; float tm = b.getValue("disconnectTimeout", 3.f); if (tm <= 0.f) - piCout << "[PIProtocol \"" << name << "\"] Warning: diconnect timeout <= 0 s!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: diconnect timeout <= 0 s!"; timeout_ = (tm < 0.f) ? 0.f : tm; setExpectedFrequency(freq); /// sender section if (sb.isEntryExists("ip") && sb.isEntryExists("device")) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous sender type in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous sender type in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -220,7 +220,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) dev = gdev; if (gok && ok && (dev != gdev)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous sender type in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous sender type in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -229,7 +229,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) ps = gps; if (gok && ok && (ps != gps)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous send port in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous send port in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -243,7 +243,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) flag = gflag; if (gok && ok && (flag != gflag)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!"; devReceiverState = "Config error"; return; } @@ -254,18 +254,18 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) freq = gfreq; if (gok && ok && (freq != gfreq)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!"; devReceiverState = "Config error"; return; } eth->setReopenTimeout(freq * 1000); } if (sendDataPtr_ == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null send data pointer!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null send data pointer!"; if (sendDataSize_ == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null send data size!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null send data size!"; } else { - piCout << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".sender.port\" or \"" << name << ".port\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".sender.port\" or \"" << name << ".port\" in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -275,7 +275,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) dev = gdev; if (gok && ok && (dev != gdev)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous sender type in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous sender type in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -284,7 +284,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) ps = gps; if (gok && ok && (ps != gps)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous send \"speed\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous send \"speed\" in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -293,7 +293,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) flag = gflag; if (gok && ok && (flag != gflag)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous send \"parity\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous send \"parity\" in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -304,14 +304,14 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re if (ok || gok) { if (gok && !ok) flag = gflag; if (gok && ok && (flag != gflag)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous send \"twoStopBits\" parity in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous send \"twoStopBits\" parity in \"" << config << "\"!"; devSenderState = "Config error"; return; } pp.setFlag(PISerial::TwoStopBits, flag); } } else { - piCout << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".sender.speed\" or \"" << name << ".speed\" in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Can`t find \"" << name << ".sender.speed\" or \"" << name << ".speed\" in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -322,16 +322,16 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re ser->setParameters(pp); has_dev = true; if (sendDataPtr_ == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null send data pointer!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null send data pointer!"; if (sendDataSize_ == 0) - piCout << "[PIProtocol \"" << name << "\"] Warning: null send data size!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: null send data size!"; } history_write_send = sb.getValue("writeHistory", false, &ok); ghist = b.getValue("writeHistory", false, &gok); if (ok || gok) { if (gok && !ok) history_write_send = ghist; if (gok && ok && (history_write_send != ghist)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous sender history in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous sender history in \"" << config << "\"!"; devSenderState = "Config error"; return; } @@ -342,7 +342,7 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re history_id_send = sb.getValue("historyID", 0, &ok); if (!ok) { history_id_send = protName.toByteArray().checksumCRC16() + 1; - piCout << "[PIProtocol \"" << name << "\"] Warning: no sender history ID defined, write with ID = " << history_id_send; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: no sender history ID defined, write with ID = " << history_id_send; } history_file_send.open(history_path_send, PIIODevice::WriteOnly); } @@ -351,12 +351,12 @@ PIProtocol::PIProtocol(const PIString & config, const PIString & name, void * re gfreq = b.getValue("frequency", -1.f, &gok); if (gok && !ok) freq = gfreq; if (gok && ok && (freq != gfreq)) { - piCout << "[PIProtocol \"" << name << "\"] Ambiguous sender frequency in \"" << config << "\"!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Ambiguous sender frequency in \"" << config << "\"!"; devSenderState = "Config error"; return; } if (freq > 0.f && !has_dev) - piCout << "[PIProtocol \"" << name << "\"] Warning: no sender device and not null send frequency!"; + piCoutObj << "[PIProtocol \"" << name << "\"] Warning: no sender device and not null send frequency!"; setSenderFrequency(freq); headerPtr = (uchar * )recHeaderPtr; diff --git a/piprotocol.h b/piprotocol.h index 10afed2a..61163499 100644 --- a/piprotocol.h +++ b/piprotocol.h @@ -29,8 +29,9 @@ class PIProtocol; -class PIP_EXPORT PIMultiProtocolBase +class PIP_EXPORT PIMultiProtocolBase: protected PIObject { + PIOBJECT(PIMultiProtocolBase) friend class PIProtocol; public: PIMultiProtocolBase() {;} diff --git a/piserial.cpp b/piserial.cpp index cefef69b..bcea1723 100644 --- a/piserial.cpp +++ b/piserial.cpp @@ -67,14 +67,14 @@ bool PISerial::setPin(int number, bool on) { case 3: return setST(on); break; case 4: return setDTR(on); break; case 5: - piCout << "[PISerial] Pin number 5 is ground"; + piCoutObj << "[PISerial] Pin number 5 is ground"; return false; case 6: return setDSR(on); break; case 7: return setRTS(on); break; case 8: return setCTS(on); break; case 9: return setRNG(on); break; default: - piCout << "[PISerial] Pin number " << number << " doesn`t exists!"; + piCoutObj << "[PISerial] Pin number " << number << " doesn`t exists!"; return false; } return false; @@ -93,7 +93,7 @@ bool PISerial::isPin(int number) const { case 8: return isCTS(); break; case 9: return isRNG(); break; default: - piCout << "[PISerial] Pin number " << number << " doesn`t exists!"; + piCoutObj << "[PISerial] Pin number " << number << " doesn`t exists!"; return false; } return false; @@ -103,16 +103,16 @@ bool PISerial::isPin(int number) const { bool PISerial::setBit(int bit, bool on, const PIString & bname) { #ifndef WINDOWS if (fd < 0) { - piCout << "[PISerial] set" << bname << " error: \"" << path_ << "\" is not opened!"; + piCoutObj << "[PISerial] set" << bname << " error: \"" << path_ << "\" is not opened!"; return false; } if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) { - piCout << "[PISerial] set" << bname << " error: " << errorString(); + piCoutObj << "[PISerial] set" << bname << " error: " << errorString(); return false; } return true; #else - piCout << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-("; + piCoutObj << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-("; return false; #endif } @@ -121,15 +121,15 @@ bool PISerial::setBit(int bit, bool on, const PIString & bname) { bool PISerial::isBit(int bit, const PIString & bname) const { #ifndef WINDOWS if (fd < 0) { - piCout << "[PISerial] is" << bname << " error: \"" << path_ << "\" is not opened!"; + piCoutObj << "[PISerial] is" << bname << " error: \"" << path_ << "\" is not opened!"; return false; } int ret = 0; if (ioctl(fd, TIOCMGET, &ret) < 0) - piCout << "[PISerial] is" << bname << " error: " << errorString(); + piCoutObj << "[PISerial] is" << bname << " error: " << errorString(); return ret & bit; #else - piCout << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-("; + piCoutObj << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-("; return false; #endif } @@ -301,7 +301,7 @@ bool PISerial::openDevice() { if (isWriteable()) {ds |= GENERIC_WRITE; sm |= FILE_SHARE_WRITE;} hCom = CreateFileA(path_.data(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if(hCom == INVALID_HANDLE_VALUE) { - piCout << "[PISerial] Unable to open \"" << path_ << "\""; + piCoutObj << "[PISerial] Unable to open \"" << path_ << "\""; return false; } fd = 0; @@ -312,7 +312,7 @@ bool PISerial::openDevice() { times.WriteTotalTimeoutConstant = 1; times.WriteTotalTimeoutMultiplier = 0; if (SetCommTimeouts(hCom, ×) == -1) { - piCout << "[PISerial] Unable to set timeouts for \"" << path_ << "\""; + piCoutObj << "[PISerial] Unable to set timeouts for \"" << path_ << "\""; CloseHandle(hCom); fd = -1; return false; @@ -330,7 +330,7 @@ bool PISerial::openDevice() { } desc.StopBits = params[PISerial::TwoStopBits] ? TWOSTOPBITS : ONESTOPBIT; if (SetCommState(hCom, &desc) == -1) { - piCout << "[PISerial] Unable to set comm state for \"" << path_ << "\""; + piCoutObj << "[PISerial] Unable to set comm state for \"" << path_ << "\""; CloseHandle(hCom); fd = -1; return false; @@ -345,7 +345,7 @@ bool PISerial::openDevice() { //cout << "init ser " << path_ << " mode " << om << " param " << params << endl; fd = ::open(path_.data(), O_NOCTTY | om); if(fd == -1) { - piCout << "[PISerial] Unable to open \"" << path_ << "\""; + piCoutObj << "[PISerial] Unable to open \"" << path_ << "\""; return false; } @@ -371,20 +371,20 @@ bool PISerial::openDevice() { fcntl(fd, F_SETFL, 0); if(tcsetattr(fd, TCSANOW, &desc) < 0) { - piCout << "[PISerial] Can`t set attributes for \"" << path_ << "\""; + piCoutObj << "[PISerial] Can`t set attributes for \"" << path_ << "\""; ::close(fd); return false; } - //piCout << "[PISerial] Initialized " << path_; + //piCoutObj << "[PISerial] Initialized " << path_; #endif return true; } int PISerial::write(const void * data, int max_size, bool wait) { - //piCout << "[PISerial] send " << max_size << ": " << PIString((char*)data, max_size); + //piCoutObj << "[PISerial] send " << max_size << ": " << PIString((char*)data, max_size); if (fd == -1 || !canWrite()) { - //piCout << "[PISerial] Can`t write to uninitialized COM"; + //piCoutObj << "[PISerial] Can`t write to uninitialized COM"; return -1; } #ifdef WINDOWS @@ -402,6 +402,6 @@ int PISerial::write(const void * data, int max_size, bool wait) { if (wait) tcdrain(fd); #endif return (int)wrote; - //piCout << "[PISerial] Error while sending"; - //piCout << "[PISerial] Wrote " << wrote << " bytes in " << path_; + //piCoutObj << "[PISerial] Error while sending"; + //piCoutObj << "[PISerial] Wrote " << wrote << " bytes in " << path_; } diff --git a/pistatemachine.h b/pistatemachine.h new file mode 100644 index 00000000..eb146c45 --- /dev/null +++ b/pistatemachine.h @@ -0,0 +1,318 @@ +/*! \file pistatemachine.h + * \brief Base class for custom state machine +*/ +/* + PIP - Platform Independent Primitives + State machine + Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PISTATEMACHINE_H +#define PISTATEMACHINE_H + +#include "piobject.h" + +/*! \brief Base class for custom state machine + * + * \section PIStateMachine_synopsis Synopsis + * This class provide functionality of state machine. + * You should inherit from this class, implement \a execution() + * and \a transition() functions, set rules and periodically + * call \a tick() function to proper work of machine. + * + * \section PIStateMachine_prepare Prepare for work + * %State machine operates with "state", "rule" and "condition". + * * "State" is some class (by default \c int), associated name and + * optional "handler" - pointer to function executed on every \a tick(); + * * "Rule" define rule of transition from one machine state to other. + * It is also has optional "handler"; + * * "Condition" is a part of rule and define possibility of transition. + * + * First of all you should define states of your machine by function + * \a addState(). Then you should define transition rules for machine + * by function \a addRule(). Finally you can set initial state by function + * \a setInitialState() and provide periodically execution of function + * \a tick(). + * + * \section PIStateMachine_principle Principle of work + * At any time the state machine is in some state. You can ask machine + * to enter in new state by function \a switchToState(). If all conditions + * done machine switch it state immediately, else machine remember request + * and will be try switch to the new state every tick. Successfull state + * switching execute function \a transition(), every tick execute + * function \a execution() with current state. On successfull transition + * if rule "handler" is not null it execute. Every \a tick() if current + * state "handler" is not null it execute. + * + * \section PIStateMachine_conditions Conditions + * Each rule has transition condition. Condition is array of pairs + * (string, number). It means that every condition by name "string" + * should be performed as least "number" times. Empty condition always + * permits transition. + * + * %State machine have current performed conditions. You can read this + * conditions by function \a currentConditions() and perform new + * conditions by functions \a performCondition() and \a performConditions(). + * Currend conditions can de erased by function \a resetConditions(). + * + * \section PIStateMachine_example Example + * This is simple example demonstrates all features: + * \snippet pistatemachine.cpp main +*/ +template +class PIP_EXPORT PIStateMachine: public PIObject +{ + PIOBJECT(PIStateMachine) +public: + //! Constructs an empty state machine + PIStateMachine() {resetConditions();} + ~PIStateMachine() {;} + + //! %Condition is a pair (string, number) + typedef PIPair Condition; + + //! %Rule of transition between states of machine + struct Rule { + //! Constuctor + Rule() {;} + //! Constuctor + Rule(Type f, Type t, const PIStringList & c = PIStringList(), Handler h = 0, bool rac = false) { + from = f; + to = t; + for (int i = 0; i < c.size_s(); ++i) + conditions << Condition(c[i], 1); + resetAllConditions = rac; + handler = h; + } + //! Source state + Type from; + //! Destination state + Type to; + //! %Conditions of transition + PIVector conditions; + //! Reset or not all performed conditions of machine on transition + bool resetAllConditions; + //! Pointer to function executed on transition + Handler handler; + //! Add condition of transition + void addCondition(const PIString & name, int times = 1) {if (times > 0) conditions << Condition(name, times);} + bool operator ==(const Rule & other) const {return (from == other.from) && (to == other.to);} + bool operator !=(const Rule & other) const {return (from != other.from) || (to != other.to);} + }; + + //! %State of machine + struct State { + //! Constuctor + State() {;} + //! Constuctor + State(Type v, const PIString & n = "", Handler h = 0) {value = v; name = n; handler = h;} + //! %State value + Type value; + //! %State name + PIString name; + //! Pointer to function executed on tick + Handler handler; + bool operator ==(const State & other) const {return value == other.value;} + bool operator !=(const State & other) const {return value != other.value;} + }; + + + //! Add state of machine + void addState(Type value, const PIString & name = "", Handler handler = 0) {if (states_.contain(State(value, name))) return; states_ << State(value, name, handler);} + + //! States count + int statesCount() const {return states_.size_s();} + + //! Remove all states + void clearStates() {states_.clear();} + + + //! Add rule of transition + void addRule(Type from, Type to, const PIString & condition, Handler handler = 0, bool resetAllConditions = false) {if (rules_.contain(Rule(from, to))) return; rules_ << Rule(from, to, PIStringList(condition), handler, resetAllConditions);} + + //! Add rule of transition + void addRule(Type from, Type to, Handler handler, bool resetAllConditions = false) {if (rules_.contain(Rule(from, to))) return; rules_ << Rule(from, to, PIStringList(), handler, resetAllConditions);} + + //! Add rule of transition + void addRule(Type from, Type to, const PIStringList & conditions = PIStringList(), Handler handler = 0, bool resetAllConditions = false) {if (rules_.contain(Rule(from, to))) return; rules_ << Rule(from, to, conditions, handler, resetAllConditions);} + + //! Add rule of transition + void addRule(const Rule & rule) {if (rules_.contain(rule)) return; rules_ << rule;} + + //! Rules count + int rulesCount() const {return rules_.size_s();} + + //! Remove all rules + void clearRules() {rules_.clear();} + + + //! Setup initial state. \a reset() will set machine state to "value" + void setInitialState(Type value) { + for (int i = 0; i < states_.size_s(); ++i) + if (states_[i].value == value) { + init_ = state_ = states_[i]; + return; + } + } + + /** \brief Try to switch machine state to state "to" + * \details If there is rule of transition exists and this rule conditions + * is performed then machine switched to new state immediately. Otherwise machine + * will be try to enter to new state every \a tick(). + * \return \c true if state switched immediately, otherwise \c false */ + bool switchToState(Type to) { + switch_to = to; + for (int i = 0; i < rules_.size_s(); ++i) { + Rule & r(rules_[i]); + if ((r.from != state_.value) || (r.to != to)) continue; + if (!checkConditions(r)) continue; + State ts = findState(to); + if (r.handler != 0) r.handler(this); + transition(state_, ts); + state_ = ts; + resetConditions(r); + return true; + } + return false; + } + + //! Reset machine state to initial and clear all conditions + void reset() {state_ = init_; resetConditions();} + + //! Returns current state of machine + const State & currentState() const {return state_;} + + + //! Reset all performed conditions + void resetConditions() {cond.clear();} + + //! Reset performed condition with name "name" + void resetCondition(const PIString & name) { + for (int i = 0; i < cond.size_s(); ++i) + if (cond[i].first == name) { + cond.remove(i); + i--; + } + } + + //! Perform condition with name "name" "times" times. + void performCondition(const PIString & name, int times = 1) { + if (times <= 0) return; + for (int i = 0; i < cond.size_s(); ++i) + if (cond[i].first == name) { + cond[i].second += times; + return; + } + cond << Condition(name, times); + } + + //! Perform every condition with name from "names" one time. + void performConditions(const PIStringList & names) { + bool ok; + for (int n = 0; n < names.size_s(); ++n) { + ok = false; + for (int i = 0; i < cond.size_s(); ++i) { + if (cond[i].first == names[n]) { + cond[i].second++; + ok = true; + break; + } + } + if (ok) continue; + cond << Condition(names[n], 1); + } + } + + //! Returns all current performed conditions + const PIVector & currentConditions() const {return cond;} + + Type * currentState_ptr() {return &state_.value;} + int * conditionsCount_ptr() {static int c = 0; c = cond.size_s(); return &c;} + +//! \handlers +//! \{ + + //! \fn void tick() + //! \brief Main function of machine. Execute \a execution() and check if need to switch state + + //! \fn void tick(void * data, int delim) + //! \brief Main function of machine. Execute \a execution() and check if need to switch state + +//! \} + + EVENT_HANDLER(void, tick) {tick(0, 0);} + EVENT_HANDLER2(void, tick, void * , data, int, delim) { + if (switch_to != state_.value) switchToState(switch_to); + execution(state_); + if (state_.handler != 0) state_.handler(this); + } + +protected: + + //! Reimplement this function to process current state of machine + virtual void execution(const State & state) {;} + + //! Reimplement this function to process switching current state of machine + virtual void transition(const State & from, const State & to) {;} + +private: + State findState(Type value) { + for (int i = 0; i < states_.size_s(); ++i) + if (states_[i].value == value) + return states_[i]; + return State(); + } + bool checkConditions(const Rule & rule) { + //if (cond.size_s() < rule.conditions.size_s()) return false; + int oc = 0; + for (int i = 0; i < cond.size_s(); ++i) { + PIString & rn(cond[i].first); + for (int j = 0; j < rule.conditions.size_s(); ++j) { + if (rn != rule.conditions[j].first) continue; + if (cond[i].second < rule.conditions[j].second) return false; + oc++; + } + } + return (rule.conditions.size_s() == oc); + } + void resetConditions(const Rule & rule) { + if (rule.resetAllConditions) { + cond.clear(); + return; + } + for (int i = 0; i < cond.size_s(); ++i) { + PIString & rn(cond[i].first); + for (int j = 0; j < rule.conditions.size_s(); ++j) { + if (rn != rule.conditions[j].first) continue; + cond[i].second -= rule.conditions[j].second; + if (cond[i].second <= 0) { + cond.remove(i); + i--; + } + } + } + } + + PIVector states_; + PIVector rules_; + State init_, state_; + Type switch_to; + PIVector cond; + +}; + + +#endif // PISTATEMACHINE_H diff --git a/pistring.cpp b/pistring.cpp index 51179ac7..d530390d 100644 --- a/pistring.cpp +++ b/pistring.cpp @@ -249,10 +249,10 @@ PIString & PIString::cutMid(const int start, const int len) { PIString & PIString::trim() { int st = 0, fn = 0; for (int i = 0; i < length(); ++i) - if (at(i) != ' ' && at(i) != '\t') + if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r') {st = i; break;} for (int i = length() - 1; i >= 0; --i) - if (at(i) != ' ' && at(i) != '\t') + if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r') {fn = i; break;} *this = mid(st, fn - st + 1); return *this; @@ -262,10 +262,10 @@ PIString & PIString::trim() { PIString PIString::trimmed() const { int st = 0, fn = 0; for (int i = 0; i < length(); ++i) - if (at(i) != ' ' && at(i) != '\t') + if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r') {st = i; break;} for (int i = length() - 1; i >= 0; --i) - if (at(i) != ' ' && at(i) != '\t') + if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r') {fn = i; break;} return mid(st, fn - st + 1); } @@ -293,7 +293,7 @@ PIString & PIString::replace(const PIString & what, const PIString & with, bool PIString & PIString::replaceAll(const PIString & what, const PIString & with) { - if (what.isEmpty()) return *this; + if (what.isEmpty() || what == with) return *this; bool ok = true; while (ok) replace(what, with, &ok); return *this; @@ -356,6 +356,152 @@ int PIString::findLast(const PIString str, const int start) const { } +PIString PIString::takeSymbol() { + PIString ret; + int sz = size_s(), ss = -1; + for (int i = 0; i < sz; ++i) { + PIChar c = at(i); + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + ss = i; + break; + } + if (ss < 0) return ret; + ret = mid(ss, 1); + cutLeft(ss + 1); + return ret; +} + + +PIString PIString::takeWord() { + int sz = size_s(), ws = -1, we = -1; + for (int i = 0; i < sz; ++i) { + PIChar c = at(i); + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + if (we < 0 && ws >= 0) we = i; + } else { + if (ws < 0) ws = i; + if (we >= 0) break; + } + } + PIString ret = mid(ws, we - ws); + cutLeft(we < 0 ? sz : we); + return ret; +} + + +PIString PIString::takeLine() { + int sz = size_s(), le = -1; + for (int i = 0; i < sz; ++i) { + PIChar c = at(i); + if (c == '\n') { + le = i; + break; + } + } + PIString ret = left(le); + if (!ret.isEmpty()) + if (ret.back() == '\r') + ret.cutRight(1); + cutLeft(le < 0 ? sz : le + 1); + return ret; +} + + +PIString PIString::takeNumber() { + PIString ret; + int sz = size_s(), ls = -1, le = -1, phase = 0; + for (int i = 0; i < sz; ++i) { + if (phase > 7) break; + PIChar c = at(i); + //piCout << "char " << c << "phase" << phase; + switch (phase) { + case 0: // trim + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + phase = 7; + case 7: // sign + if (c == '-' || c == '+') {ls = i; phase = 1; break;} + case 1: // search start + if (c >= '0' && c <= '9') {le = i; if (ls < 0) ls = i; phase = 2; break;} + if (c == '.') {le = i; if (ls < 0) ls = i; phase = 3; break;} + phase = 9; + break; + case 2: // integer + if (c == '.') {le = i; phase = 3; break;} + if (c == 'e' || c == 'E') {le = i; phase = 4; break;} + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == 'x') {le = i; break;} + phase = 6; + break; + case 3: // point + if (c == 'e' || c == 'E') {le = i; phase = 4; break;} + if (c >= '0' && c <= '9') {le = i; break;} + phase = 6; + break; + case 4: // exp + if ((c >= '0' && c <= '9') || c == '-' || c == '+') {le = i; phase = 5; break;} + phase = 6; + break; + case 5: // power + if (c >= '0' && c <= '9') {le = i; break;} + phase = 6; + break; + case 6: // suffix + if (c == 'f' || c == 's' || c == 'u' || c == 'l' || c == 'L') {le = i; break;} + phase = 9; + break; + } + if (phase == 6) { + if (c == 'f' || c == 's' || c == 'u' || c == 'l' || c == 'L') le = i; + else phase = 9; + } + } + //piCout << ls << le; + if (le < ls) return ret; + ret = mid(ls, le - ls + 1); + cutLeft(le + 1); + return ret; +} + + +PIString PIString::takeRange(const PIChar & start, const PIChar & end, const PIChar & shield) { + PIString ret; + bool trim_ = (start != ' ' && start != '\t' && start != '\n' && start != '\r'), eq = (start == end); + int sz = size_s(), ls = -1, le = -1, cnt = 0; + for (int i = 0; i < sz; ++i) { + PIChar c = at(i); + if (c == shield) {++i; continue;} + if (trim_) { + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + trim_ = false; + } + if (eq) { + if (c == start) { + if (cnt == 0) ls = i; + else {le = i; cnt = 0; break;} + cnt++; + } + } else { + if (c == start) { + if (cnt == 0) ls = i; + cnt++; + } + if (c == end) { + cnt--; + if (cnt == 0) le = i; + } + } + if (cnt <= 0) break; + } + //piCout << ls << le << cnt; + if (le < ls || ls < 0 || le < 0 || cnt != 0) return ret; + ret = mid(ls + 1, le - ls - 1); + cutLeft(le + 1); + return ret; +} + + PIString PIString::toUpperCase() const { PIString str(*this); int l = str.size(); diff --git a/pistring.h b/pistring.h index 03b679b2..eca88141 100644 --- a/pistring.h +++ b/pistring.h @@ -86,7 +86,7 @@ public: PIString(const int len, const PIChar & c) {reserve(256); piMonitor.strings++; piMonitor.containers--; for (int i = 0; i < len; ++i) push_back(c);} //! Contructs string from other string "str" - PIString(const PIString & str) {reserve(256); piMonitor.strings++; piMonitor.containers--; *this += str;} + PIString(const PIString & str) {reserve(256); piMonitor.strings++; piMonitor.containers--; uint len = str.size(); for (uint i = 0; i < len; ++i) push_back(str[i]);} ~PIString() {piMonitor.strings--; piMonitor.containers++;} @@ -340,12 +340,40 @@ public: * \details Example: \snippet pistring.cpp PIString::reversed * \sa \a reverse() */ PIString reversed() const {PIString str(*this); str.reverse(); return str;} + + + /*! \brief Take a symbol from the begin of this string and return it + * \details Example: \snippet pistring.cpp PIString::takeSymbol + * \sa \a \a takeWord(), takeLine(), \a takeNumber(), \a takeRange() */ + PIString takeSymbol(); + + /*! \brief Take a word from the begin of this string and return it + * \details Example: \snippet pistring.cpp PIString::takeWord + * \sa \a takeSymbol(), \a takeLine(), \a takeNumber(), \a takeRange() */ + PIString takeWord(); + + /*! \brief Take a line from the begin of this string and return it + * \details Example: \snippet pistring.cpp PIString::takeLine + * \sa \a takeSymbol(), \a takeWord(), \a takeNumber(), \a takeRange() */ + PIString takeLine(); + + /*! \brief Take a number with C-format from the begin of this string and return it + * \details Example: \snippet pistring.cpp PIString::takeNumber + * \sa \a takeSymbol(), \a takeWord(), \a takeLine(), \a takeRange() */ + PIString takeNumber(); + + /*! \brief Take a range between "start" and "end" symbols from the begin of this + * string and return it. + * \details "Shield" symbol prevent analysis of the next symbol. + * Example: \snippet pistring.cpp PIString::takeRange + * \sa \a takeSymbol(), \a takeWord(), \a takeLine(), \a takeNumber() */ + PIString takeRange(const PIChar & start, const PIChar & end, const PIChar & shield = '\\'); //const char * data() {return convertToStd().c_str();} /*! \brief Return real bytes count of this string - * \details It`s equivalent length os char sequence + * \details It`s equivalent length of char sequence * returned by function \a data() \n * Example: \snippet pistring.cpp PIString::lengthAscii * \sa \a data() */ @@ -353,7 +381,7 @@ public: /*! \brief Return \c char * representation of this string * \details This function fill buffer by sequence - * of chars. Minimum length of this buffer is count + * of chars. Minimum length of this buffer is count * of symbols. Returned \c char * is valid until next * execution of this function.\n * Example: \snippet pistring.cpp PIString::data @@ -645,10 +673,10 @@ inline PICout operator <<(PICout s, const PIString & v) {s.space(); s.quote(); s //! \relatesalso PIString \relatesalso PIByteArray \brief Output operator to PIByteArray -inline PIByteArray & operator <<(PIByteArray & s, const PIString & v) {s << v.size_s(); for (int i = 0; i < v.length(); ++i) s << v[i]; return s;} +inline PIByteArray & operator <<(PIByteArray & s, const PIString & v) {int l = v.lengthAscii(); s << l; if (l <= 0) return s; int os = s.size_s(); s.enlarge(l); memcpy(s.data(os), v.data(), l); return s;} //! \relatesalso PIString \relatesalso PIByteArray \brief Input operator from PIByteArray -inline PIByteArray & operator >>(PIByteArray & s, PIString & v) {int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PIString & v) {int l; s >> l; if (l <= 0) return s; v = PIString((const char * )s.data(), l); s.remove(0, l); return s;} //! \relatesalso PIString \brief Return concatenated string @@ -728,6 +756,14 @@ public: }; + +//! \relatesalso PIStringList \relatesalso PIByteArray \brief Output operator to PIByteArray +inline PIByteArray & operator <<(PIByteArray & s, const PIStringList & v) {s << v.size_s(); for (int i = 0; i < v.size_s(); ++i) s << v[i]; return s;} + +//! \relatesalso PIStringList \relatesalso PIByteArray \brief Input operator from PIByteArray +inline PIByteArray & operator >>(PIByteArray & s, PIStringList & v) {int sz; s >> sz; v.resize(sz); for (int i = 0; i < sz; ++i) s >> v[i]; return s;} + + //! \relatesalso PIStringList \brief Output operator to std::ostream (cout) inline std::ostream & operator <<(std::ostream & s, const PIStringList & v) {s << "{"; for (uint i = 0; i < v.size(); ++i) {s << '\"' << v[i] << '\"'; if (i < v.size() - 1) s << ", ";} s << "}"; return s;} diff --git a/pisystemmonitor.cpp b/pisystemmonitor.cpp index 774bbfc6..a0d382e5 100644 --- a/pisystemmonitor.cpp +++ b/pisystemmonitor.cpp @@ -42,7 +42,7 @@ bool PISystemMonitor::startOnProcess(int pID) { file.open("/proc/" + PIString::fromNumber(pID_) + "/stat", PIIODevice::ReadOnly); filem.open("/proc/" + PIString::fromNumber(pID_) + "/statm", PIIODevice::ReadOnly); if (!file.isOpened()) { - piCout << "[PISystemMonitor] Can`t find process with ID = " << pID_ << "!"; + piCoutObj << "[PISystemMonitor] Can`t find process with ID = " << pID_ << "!"; return false; } cycle = -1; diff --git a/pitimer.cpp b/pitimer.cpp index 400ee637..384e016d 100644 --- a/pitimer.cpp +++ b/pitimer.cpp @@ -174,7 +174,7 @@ void PITimer::start(double msecs) { ti = timer_create(CLOCK_REALTIME, &se, &timer); //cout << "***create timer " << msecs << " msecs\n"; if (ti == -1) { - piCout << "[PITimer] Can`t create timer for " << msecs << " msecs: " << errorString(); + piCoutObj << "[PITimer] Can`t create timer for " << msecs << " msecs: " << errorString(); return; } timer_settime(timer, 0, &spec, 0); @@ -192,7 +192,7 @@ void PITimer::deferredStart(double interval_msecs, double delay_msecs) { ti = timer_create(CLOCK_REALTIME, &se, &timer); //cout << "***create timer\n"; if (ti == -1) { - piCout << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString(); + piCoutObj << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString(); return; } timer_settime(timer, 0, &spec, 0); @@ -218,7 +218,7 @@ void PITimer::deferredStart(double interval_msecs, const PIDateTime & start_date ti = timer_create(CLOCK_REALTIME, &se, &timer); //cout << "***create timer\n"; if (ti == -1) { - piCout << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString(); + piCoutObj << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString(); return; } timer_settime(timer, TIMER_ABSTIME, &spec, 0); @@ -246,7 +246,7 @@ void PITimer::TimerPool::begin() { sa.sa_handler = empty_handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, 0) == -1) { - piCout << "[PITimer] sigaction error: " << errorString(); + piCoutObj << "[PITimer] sigaction error: " << errorString(); stop(); return; }*/ @@ -260,12 +260,12 @@ void PITimer::TimerPool::begin() { spec.it_value = spec.it_interval; //cout << "***create pool timer\n"; if (timer_create(CLOCK_REALTIME, &se, &timer) == -1) { - piCout << "[PITimer] Can`t create timer for pool: " << errorString(); + piCoutObj << "[PITimer] Can`t create timer for pool: " << errorString(); stop(); return; } if (timer_settime(timer, 0, &spec, 0) == -1) { - piCout << "[PITimer] Can`t set timer for pool: " << errorString(); + piCoutObj << "[PITimer] Can`t set timer for pool: " << errorString(); stop(); return; } @@ -358,7 +358,7 @@ void PITimer::start(double msecs) { void PITimer::deferredStart(double interval_msecs, double delay_msecs) { - //piCout << "defStart exec with" << delay_msecs << interval_msecs; + //piCoutObj << "defStart exec with" << delay_msecs << interval_msecs; if (interval_msecs < 0 || running_) return; interval_ = interval_msecs; PISystemTime cst = currentSystemTime(); @@ -367,12 +367,12 @@ void PITimer::deferredStart(double interval_msecs, double delay_msecs) { if (st_time < cst) st_time = cst; running_ = deferred_ = true; PIThread::start(); - //piCout << "timer start def"; + //piCoutObj << "timer start def"; } void PITimer::deferredStart(double interval_msecs, const PIDateTime & start_datetime) { - //piCout << "defStart exec to" << start_datetime.toString() << interval_msecs; + //piCoutObj << "defStart exec to" << start_datetime.toString() << interval_msecs; if (interval_msecs < 0 || running_) return; interval_ = interval_msecs; PISystemTime cst = currentSystemTime(); @@ -381,7 +381,7 @@ void PITimer::deferredStart(double interval_msecs, const PIDateTime & start_date if (st_time < cst) st_time = cst; running_ = deferred_ = true; PIThread::start(); - //piCout << "timer start def"; + //piCoutObj << "timer start def"; } @@ -621,6 +621,7 @@ PIDateTime currentDateTime() { time_t rt = time(0); tm * pt = localtime(&rt); PIDateTime dt; + dt.milliseconds = 0; dt.seconds = pt->tm_sec; dt.minutes = pt->tm_min; dt.hours = pt->tm_hour; diff --git a/pitimer.h b/pitimer.h index 9359ccbd..5fed2921 100644 --- a/pitimer.h +++ b/pitimer.h @@ -190,6 +190,11 @@ public: void removeDelimiter(int delim) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].delim == delim) {ret_funcs.remove(i); i--;}} void removeDelimiter(TimerEvent slot) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].slot == slot) {ret_funcs.remove(i); i--;}} void removeDelimiter(int delim, TimerEvent slot) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].slot == slot && ret_funcs[i].delim == delim) {ret_funcs.remove(i); i--;}} + void setDelimiterValue(int delim, int value) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].delim == delim) ret_funcs[i].tick = value;} + void setDelimiterValue(TimerEvent slot, int value) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].slot == slot) ret_funcs[i].tick = value;} + void setDelimiterValue(int delim, TimerEvent slot, int value) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].slot == slot && ret_funcs[i].delim == delim) ret_funcs[i].tick = value;} + int delimiterValue(int delim) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].delim == delim) return ret_funcs[i].tick; return -1;} + int delimiterValue(int delim, TimerEvent slot) {for (int i = 0; i < ret_funcs.size_s(); ++i) if (ret_funcs[i].slot == slot && ret_funcs[i].delim == delim) return ret_funcs[i].tick; return -1;} EVENT_HANDLER0(void, clearDelimiters) {ret_funcs.clear();} double elapsed_n(); // nanoseconds diff --git a/piusb.cpp b/piusb.cpp new file mode 100644 index 00000000..a37d2950 --- /dev/null +++ b/piusb.cpp @@ -0,0 +1,357 @@ +#include "piusb.h" + +#ifdef PIP_USB +# ifdef WINDOWS +# include +# else +# include +# endif +#endif + + +PIUSB::PIUSB(ushort vid, ushort pid): PIIODevice("", ReadWrite, false) { + vid_ = vid; + pid_ = pid; + path_ = PIString::fromNumber(vid_, 16).expandLeftTo(4, "0") + ":" + PIString::fromNumber(pid_, 16).expandLeftTo(4, "0"); + dev_num = 1; + intefrace_ = 0; + hdev = 0; + interface_claimed = -1; + timeout_r = timeout_w = 1000; +} + + +void PIUSB::Endpoint::parse() { + direction = Write; + transfer_type = Control; + synchronisation_type = NoSynchonisation; + usage_type = DataEndpoint; + direction = (Direction)((address >> 7) & 1); + transfer_type = (TransferType)(attributes & 3); + if (transfer_type == Isochronous) { + synchronisation_type = (SynchronisationType)((attributes >> 2) & 3); + usage_type = (UsageType)((attributes >> 4) & 3); + } +} + + +PIUSB::Endpoint PIUSB::getEndpointByAddress(uchar address) { + piForeachC (Endpoint & i, eps) + if (i.address == address) + return i; + return Endpoint(); +} + + +PIVector PIUSB::endpointsRead() { + PIVector ret; + piForeachC (Endpoint & i, eps) + if (i.direction == Endpoint::Read) + ret << i; + return ret; +} + + +PIVector PIUSB::endpointsWrite() { + PIVector ret; + piForeachC (Endpoint & i, eps) + if (i.direction == Endpoint::Write) + ret << i; + return ret; +} + + +bool PIUSB::setConfiguration(uchar value) { +#ifdef PIP_USB + if (hdev == 0) return false; + bool found = false; + piForeachC (Configuration & c, desc_.configurations) + if (c.value_to_select == value) {found = true; conf_ = c; break;} + if (!found) { + piCoutObj << "[PIUSB] Can`t find configuration with \"value_to_select\" =" << value; + return false; + } + if (interface_claimed >= 0) + usb_release_interface(hdev, interface_claimed); + interface_claimed = -1; + return setInterface(conf_.interfaces.front().value_to_select); +#else + return false; +#endif +} + + +bool PIUSB::setInterface(uchar value) { +#ifdef PIP_USB + if (hdev == 0) return false; + bool found = false; + piForeachC (Interface & i, conf_.interfaces) + if (i.value_to_select == value) {found = true; iface_ = i; break;} + if (!found) { + piCoutObj << "[PIUSB] Can`t find interface with \"value_to_select\" =" << value; + return false; + } + if (interface_claimed >= 0) + usb_release_interface(hdev, interface_claimed); + interface_claimed = -1; + if (usb_claim_interface(hdev, iface_.value_to_select) < 0) { + piCoutObj << "[PIUSB] Error: Cant`t claim interface!"; + return false; + } + eps.clear(); + eps = iface_.endpoints; + ep_read = ep_write = Endpoint(); + for (int i = 0; i < eps.size_s(); ++i) { + if (eps[i].direction == Endpoint::Read && ep_read.isNull()) + ep_read = eps[i]; + if (eps[i].direction == Endpoint::Write && ep_write.isNull()) + ep_write = eps[i]; + } + interface_claimed = value; + return true; +#else + return false; +#endif +} + + +bool PIUSB::openDevice() { +#ifdef PIP_USB + if (path_.size_s() >= 8) { + vid_ = path_.left(4).toInt(16); + pid_ = path_.right(4).toInt(16); + } + if (hdev != 0) closeDevice(); + hdev = 0; + interface_claimed = -1; + ep_write = ep_read = Endpoint(); + usb_init(); + //usb_set_debug(4); + if (usb_find_busses() < 0) { + piCoutObj << "[PIUSB] Error: Cant`t find busses!"; + return false; + } + if (usb_find_devices() < 0) { + piCoutObj << "[PIUSB] Error: Cant`t find devices!"; + return false; + } + + //piCoutObj << "[PIUSB] Search for device ... " << flush; + int cur_num = 1; + bool found = false; + struct usb_device * dev; + struct usb_bus * bus; + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == vid_ && dev->descriptor.idProduct == pid_) { + if (cur_num == dev_num) { + struct usb_device_descriptor & dd(dev->descriptor); + desc_.usb_spec_number = dd.bcdUSB; + desc_.device_class = dd.bDeviceClass; + desc_.device_subclass = dd.bDeviceSubClass; + desc_.device_protocol = dd.bDeviceProtocol; + desc_.max_packet_size = dd.bMaxPacketSize0; + desc_.id_vendor = dd.idVendor; + desc_.id_product = dd.idProduct; + desc_.id_device_release = dd.bcdDevice; + desc_.index_manufacturer = dd.iManufacturer; + desc_.index_product = dd.iProduct; + desc_.index_serial = dd.iSerialNumber; + desc_.configurations.clear(); + for (int c = 0; c < dd.bNumConfigurations; ++c) { + desc_.configurations << Configuration(); + Configuration & conf(desc_.configurations.back()); + struct usb_config_descriptor & dc(dev->config[c]); + conf.index = c; + conf.value_to_select = dc.bConfigurationValue; + conf.attributes = dc.bmAttributes; + conf.max_power = ushort(dc.MaxPower) * 2; + conf.self_powered = (conf.attributes >> 6) & 1; + conf.remote_wakeup = (conf.attributes >> 5) & 1; + conf.interfaces.clear(); + for (int i = 0; i < dc.bNumInterfaces; ++i) { + conf.interfaces << Interface(); + Interface & infc(conf.interfaces.back()); + struct usb_interface_descriptor * di(dc.interface[c].altsetting); + infc.index = i; + infc.value_to_select = di->bAlternateSetting; + infc.class_code = di->bInterfaceClass; + infc.subclass_code = di->bInterfaceSubClass; + infc.protocol_code = di->bInterfaceProtocol; + infc.endpoints.clear(); + for (int e = 0; e < di->bNumEndpoints; ++e) { + infc.endpoints << Endpoint(di->endpoint[e].bEndpointAddress, + di->endpoint[e].bmAttributes, + di->endpoint[e].wMaxPacketSize); + } + } + } + if (!desc_.configurations.isEmpty()) + conf_ = desc_.configurations.front(); + + struct usb_interface_descriptor * is = dev->config->interface->altsetting; + int epn = is->bNumEndpoints; + eps.clear(); + for (int i = 0; i < epn; ++i) { + eps << Endpoint(is->endpoint[i].bEndpointAddress, + is->endpoint[i].bmAttributes, + is->endpoint[i].wMaxPacketSize); + if (eps.back().direction == Endpoint::Write && ep_write.address == 0) ep_write = eps.back(); + if (eps.back().direction == Endpoint::Read && ep_read.address == 0) ep_read = eps.back(); + } + + //piCoutObj << "[PIUSB] Device found at address:" << "Bus: " << dev->bus->dirname << ", Device: " << dev->filename; + found = true; + break; + } else cur_num++; + } + } + if (found) break; + } + if (!found) { + piCoutObj << "[PIUSB] Error: Cant`t find device!"; + return false; + } + //piCoutObj << "[PIUSB] Open ... " << flush; + hdev = usb_open(dev); + if (hdev == 0) { + piCoutObj << "[PIUSB] Error: Cant`t open device:" << usb_strerror(); + return false; + }// else piCoutObj << "[PIUSB] ok"; + //usb_reset(hdev); + + //usb_set_configuration(hdev, 1); + //usb_set_altinterface(hdev, 0); + +# ifndef WINDOWS + char tbuff[256]; + //piCoutObj << "[PIUSB] Check for bounded driver ... " << flush; + if (usb_get_driver_np(hdev, intefrace_, tbuff, sizeof(tbuff) - 1) >= 0) { + //piCoutObj << "[PIUSB] yes" << "Found driver: " << tbuff; + //piCoutObj << "[PIUSB] Detach driver ... " << flush; + if (usb_detach_kernel_driver_np(hdev, intefrace_)< 0) { + piCoutObj << "[PIUSB] Error: Cant`t detach bounded driver!"; + return false; + }// else piCoutObj << "[PIUSB] ok"; + }// else piCoutObj << "[PIUSB] no"; +# endif + + //piCoutObj << "[PIUSB] Claim interface ... " << flush; + if (usb_claim_interface(hdev, intefrace_) < 0) { + piCoutObj << "[PIUSB] Error: Cant`t claim interface:" << usb_strerror(); + return false; + } // else piCoutObj << "[PIUSB] ok"; + interface_claimed = intefrace_; + + return true; +#else + return false; +#endif +} + + +bool PIUSB::closeDevice() { +#ifdef PIP_USB + if (hdev == 0) return true; + //usb_reset(hdev); + usb_release_interface(hdev, intefrace_); + usb_close(hdev); + hdev = 0; + interface_claimed = -1; + return true; +#else + return false; +#endif +} + + +int PIUSB::read(void * read_to, int max_size) { +#ifdef PIP_USB + if (!opened_ || ep_read.isNull()) return -1; + switch (ep_read.transfer_type) { + case Endpoint::Bulk: /*piCoutObj << "bulk read" << max_size;*/ return usb_bulk_read(hdev, ep_read.address, (char * )read_to, max_size, timeout_r); break; + case Endpoint::Interrupt: return usb_interrupt_read(hdev, ep_read.address, (char * )read_to, max_size, timeout_r); break; + default: break; + } + return -1; +#else + return -1; +#endif +} + + +int PIUSB::write(const void * data, int max_size) { +#ifdef PIP_USB + if (!opened_ || ep_write.isNull()) return -1; + switch (ep_read.transfer_type) { + case Endpoint::Bulk: /*piCoutObj << "bulk write" << max_size;*/ return usb_bulk_write(hdev, ep_write.address, (char * )const_cast(data), max_size, timeout_w); break; + case Endpoint::Interrupt: return usb_interrupt_write(hdev, ep_read.address, (char * )data, max_size, timeout_w); break; + default: break; + } + return -1; +#else + return -1; +#endif +} + + +int PIUSB::controlWrite(const void * data, int max_size) { +#ifdef PIP_USB + if (!opened_) return -1; + //return usb_control_msg(hdev, ); + return -1; +#else + return -1; +#endif +} + + +void PIUSB::flush() { +#ifdef PIP_USB + if (!opened_) return; + if (!ep_read.isNull()) usb_resetep(hdev, ep_read.address); + if (!ep_write.isNull()) usb_resetep(hdev, ep_write.address); +#endif +} + + +PICout operator<<(PICout s, const PIUSB::Endpoint & v) { + s.setControl(0, true); + s << NewLine << "{" << NewLine; + if (v.isNull()) + s << " " << "Null Endpoint"; + else { + s << " " << "Address: " << v.address << NewLine; + s << " " << "Attributes: " << v.attributes << NewLine; + s << " " << "Direction: " << (v.direction == PIUSB::Endpoint::Write ? "Write" : "Read") << NewLine; + s << " " << "Transfer Type: "; + switch (v.transfer_type) { + case PIUSB::Endpoint::Control: s << "Control" << NewLine; break; + case PIUSB::Endpoint::Bulk: s << "Bulk" << NewLine; break; + case PIUSB::Endpoint::Interrupt: s << "Interrupt" << NewLine; break; + case PIUSB::Endpoint::Isochronous: s << "Isochronous" << NewLine; break; + default: break; + } + if (v.transfer_type == PIUSB::Endpoint::Isochronous) { + s << " " << "Synchronisation Type: "; + switch (v.synchronisation_type) { + case PIUSB::Endpoint::NoSynchonisation: s << "No Synchonisation" << NewLine; break; + case PIUSB::Endpoint::Asynchronous: s << "Asynchronous" << NewLine; break; + case PIUSB::Endpoint::Adaptive: s << "Adaptive" << NewLine; break; + case PIUSB::Endpoint::Synchronous: s << "Synchronous" << NewLine; break; + default: break; + } + s << " " << "Usage Type: "; + switch (v.usage_type) { + case PIUSB::Endpoint::DataEndpoint: s << "Data Endpoint" << NewLine; break; + case PIUSB::Endpoint::FeedbackEndpoint: s << "Feedback Endpoint" << NewLine; break; + case PIUSB::Endpoint::ExplicitFeedbackDataEndpoint: s << "Explicit Feedback Data Endpoint" << NewLine; break; + default: break; + } + } + s << " " << "Max Packet Size: " << v.max_packet_size << NewLine; + } + s << "}" << NewLine; + s.restoreControl(); + return s; +} diff --git a/piusb.h b/piusb.h new file mode 100644 index 00000000..5a61ace9 --- /dev/null +++ b/piusb.h @@ -0,0 +1,137 @@ +/* + PIP - Platform Independent Primitives + USB, based on libusb + Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PIUSB_H +#define PIUSB_H + +#include "piiodevice.h" + +struct usb_dev_handle; + +class PIP_EXPORT PIUSB: public PIIODevice { +public: + PIUSB(ushort vid = 0, ushort pid = 0); + + struct Endpoint { + Endpoint(uchar a = 0, uchar at = 0, ushort mps = 0) {address = a; attributes = at; max_packet_size = mps; parse();} + + enum Direction {Write = 0, Read = 1}; + enum TransferType {Control = 0, Isochronous = 1, Bulk = 2, Interrupt = 3}; + enum SynchronisationType {NoSynchonisation= 0, Asynchronous = 2, Adaptive = 1, Synchronous = 3}; + enum UsageType {DataEndpoint = 0, FeedbackEndpoint = 2, ExplicitFeedbackDataEndpoint = 1}; + + void parse(); + bool isNull() const {return address == 0;} + + uchar address; + uchar attributes; + ushort max_packet_size; + Direction direction; + TransferType transfer_type; + SynchronisationType synchronisation_type; + UsageType usage_type; + }; + + struct Interface { + Interface() {index = value_to_select = class_code = subclass_code = protocol_code = 0;} + uchar index; + uchar value_to_select; + ushort class_code; + ushort subclass_code; + ushort protocol_code; + PIVector endpoints; + }; + + struct Configuration { + Configuration() {index = value_to_select = attributes = max_power = 0; self_powered = remote_wakeup = false;} + uchar index; + uchar value_to_select; + uchar attributes; + ushort max_power; // mA + bool self_powered; + bool remote_wakeup; + PIVector interfaces; + }; + + struct Descriptor { + Descriptor() {memset(this, 0, sizeof(Descriptor));} + ushort usb_spec_number; + uchar device_class; + uchar device_subclass; + uchar device_protocol; + uchar max_packet_size; + ushort id_vendor; + ushort id_product; + ushort id_device_release; + uchar index_manufacturer; + uchar index_product; + uchar index_serial; + PIVector configurations; + }; + + const Descriptor & currentDescriptor() const {return desc_;} + const Configuration & currentConfiguration() const {return conf_;} + const Interface & currentInterface() const {return iface_;} + + ushort vendorID() const {return vid_;} + ushort productID() const {return pid_;} + + const PIVector & endpoints() const {return eps;} + PIVector endpointsRead(); + PIVector endpointsWrite(); + Endpoint getEndpointByAddress(uchar address); + + const Endpoint & endpointRead() {return ep_read;} + const Endpoint & endpointWrite() {return ep_write;} + + bool setConfiguration(uchar value); + bool setInterface(uchar value); + + void setEndpointRead(const Endpoint & ep) {ep_read = ep;} + void setEndpointWrite(const Endpoint & ep) {ep_write = ep;} + void setDeviceNumber(int dn) {dev_num = dn;} + void setTimeoutRead(int t) {timeout_r = t;} + void setTimeoutWrite(int t) {timeout_w = t;} + + int read(void * read_to, int max_size); + int write(const void * data, int max_size); + int controlWrite(const void * data, int max_size); + + void flush(); + +protected: + //bool init(); + bool openDevice(); + bool closeDevice(); + + PIVector eps; + ushort vid_, pid_; + int dev_num, intefrace_, timeout_r, timeout_w; + int interface_claimed; + Endpoint ep_read, ep_write; + Descriptor desc_; + Configuration conf_; + Interface iface_; + usb_dev_handle * hdev; + +}; + +PICout operator <<(PICout s, const PIUSB::Endpoint & v); + +#endif // PIUSB_H diff --git a/remote_console/CMakeLists.txt b/remote_console/CMakeLists.txt new file mode 100644 index 00000000..a27e393d --- /dev/null +++ b/remote_console/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.6) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} . ../) +file(GLOB CPPS "*.cpp") +add_definitions(-Wall -O2) +add_executable(pip_remote_console "main.cpp") +target_link_libraries(pip_remote_console pip) diff --git a/remote_console/main.cpp b/remote_console/main.cpp new file mode 100644 index 00000000..faafff9b --- /dev/null +++ b/remote_console/main.cpp @@ -0,0 +1,66 @@ +/* + PIP - Platform Independent Primitives + Remote console viewer + Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "pip.h" + +void key_event(char key, void * ); + +PIConsole console(false, key_event); +PIStringList as; +bool selected = false; + +void key_event(char key, void * ) { + if (key < '1' || key > '9') return; + int ind = key - '1'; + if (ind < 0 || ind >= as.size_s()) return; + selected = true; + console.connectToServer(as[ind]); + console.clearScreen(); + piCout << "Connecting to" << console.selectedServer() << "..."; +} + +int main(int argc, char * argv[]) { + console.enableExitCapture(); + console.listenServers(); + while (!PIKbdListener::exiting) { + msleep(200); + if (selected) break; + console.clearScreen(); + as = console.availableServers(); + if (as.isEmpty()) { + piCout << "No servers are available!"; + } else { + piCout << "Select one with numeric key:"; + for (int i = 0; i < as.size_s(); ++i) + piCout << (i + 1) << as[i]; + } + } + if (!selected) return 0; + console.clearScreen(); + piCout << "Connecting to" << console.selectedServer() << "..."; + while (!PIKbdListener::exiting) { + msleep(20); + if (console.isConnected()) + break; + } + if (PIKbdListener::exiting) + return 0; + console.start(); + console.waitForFinish(); +};