From 646e8d772683730ab93a5277b551f27ca0ce2884 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Thu, 7 Mar 2024 18:04:01 +0100 Subject: [PATCH 01/13] Improved documentation --- Doxyfile | 914 +++++++++++++------ doc/version-history.rst | 1 + include/Modbus/Buffer.h | 51 +- include/Modbus/BusList.h | 142 ++- include/Modbus/Parser.h | 55 +- include/cRIO/ElectromechanicalPneumaticILC.h | 126 ++- include/cRIO/ThermalILC.h | 2 + src/Modbus/BusList.cpp | 3 +- tests/test_ElectromechanicalPneumaticILC.cpp | 4 +- tests/test_Modbus_Parser.cpp | 8 +- 10 files changed, 978 insertions(+), 328 deletions(-) diff --git a/Doxyfile b/Doxyfile index 8f81460b..d02b5c71 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.14 +# Doxyfile 1.9.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,15 +12,25 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # 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 +# This tag specifies the encoding used for all characters in the configuration +# 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 # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. @@ -60,16 +70,28 @@ PROJECT_LOGO = 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 +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 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 causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,14 +103,14 @@ ALLOW_UNICODE_NAMES = 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. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English @@ -189,6 +211,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = 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 Qt-style will behave just like regular Qt-style comments (thus @@ -209,6 +241,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -232,20 +272,18 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # 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 (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. - -ALIASES = "multithreading=\xrefitem multithreading \"MT-Safety\" \"MT-Safety\" " - -# 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. +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) -TCL_SUBST = +ALIASES = "multithreading=\xrefitem multithreading \"MT-Safety\" \"MT-Safety\"" # 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 @@ -275,28 +313,40 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = 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, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. 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. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). 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: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled 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. +# documentation. See https://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. @@ -308,11 +358,22 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 0. +# Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + # 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 putting a % sign in front of the word or @@ -424,6 +485,27 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -444,6 +526,12 @@ EXTRACT_ALL = NO EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -481,6 +569,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -492,14 +587,15 @@ HIDE_UNDOC_MEMBERS = NO # 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, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -518,12 +614,20 @@ HIDE_IN_BODY_DOCS = NO 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. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -541,6 +645,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -698,7 +808,8 @@ FILE_VERSION_FILTER = # 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. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -744,23 +855,50 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag 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. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This 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, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO @@ -771,13 +909,27 @@ WARN_AS_ERROR = NO # 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) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # 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 standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -791,17 +943,30 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = include/cRIO +INPUT = include/cRIO \ + include/Modbus \ + include/ILC # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -810,11 +975,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, +# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be +# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -896,10 +1065,7 @@ EXCLUDE_PATTERNS = # (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 -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = @@ -944,6 +1110,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -985,6 +1156,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1012,7 +1192,7 @@ INLINE_SOURCES = NO 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. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -1049,7 +1229,7 @@ SOURCE_TOOLTIPS = YES # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -1082,17 +1262,11 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -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 a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1171,7 +1345,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1186,9 +1365,22 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # 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 +# this color. Hue is specified as an angle on a color-wheel, see # https://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. @@ -1198,7 +1390,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # 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 +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1216,20 +1408,11 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# 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 YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1244,6 +1427,13 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = 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 @@ -1259,13 +1449,14 @@ 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 (see: https://developer.apple.com/tools/xcode/), 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 +# environment (see: +# https://developer.apple.com/xcode/), 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 https://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1279,6 +1470,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset 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. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # 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. @@ -1304,8 +1502,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1335,7 +1537,7 @@ CHM_FILE = HHC_LOCATION = # 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). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1362,6 +1564,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # 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 @@ -1380,7 +1592,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1388,7 +1601,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1396,28 +1610,30 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. 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 (see: -# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# 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. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1460,16 +1676,28 @@ DISABLE_INDEX = NO # 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. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1494,6 +1722,24 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1503,19 +1749,14 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT 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 directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered 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 @@ -1525,11 +1766,29 @@ FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1542,22 +1801,29 @@ MATHJAX_FORMAT = HTML-CSS # 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 https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1585,7 +1851,7 @@ MATHJAX_CODEFILE = 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 +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching 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 @@ -1604,7 +1870,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1617,8 +1884,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1669,21 +1937,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# 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. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = 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. @@ -1713,29 +1995,31 @@ PAPER_TYPE = a4 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. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined 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. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. 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. See +# The LATEX_FOOTER tag can be used to specify a user-defined 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. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1768,18 +2052,26 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. 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. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1792,16 +2084,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag 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. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -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. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1810,13 +2092,13 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# 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. -# The default value is: NO. +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_TIMESTAMP = NO +LATEX_EMOJI_DIRECTORY = #--------------------------------------------------------------------------- # Configuration options related to the RTF output @@ -1857,9 +2139,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet 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. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1868,22 +2150,12 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -1955,6 +2227,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1973,27 +2252,44 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if an a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2068,7 +2364,8 @@ SEARCH_INCLUDES = YES # 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. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2135,15 +2432,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. 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 +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2156,41 +2453,10 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML 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. -# The default value is: YES. - -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 = - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_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. # The default value is: YES. @@ -2199,7 +2465,7 @@ 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 (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), 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 value is: NO. @@ -2216,49 +2482,73 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates 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. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification 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. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is 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. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is 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. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2281,10 +2571,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2296,7 +2608,9 @@ TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2305,7 +2619,10 @@ INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2345,16 +2662,26 @@ GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is 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. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: 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). @@ -2391,11 +2718,12 @@ DOT_PATH = 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). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2404,10 +2732,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2445,18 +2773,6 @@ DOT_GRAPH_MAX_NODES = 50 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). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to 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 @@ -2469,14 +2785,34 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# 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 = diff --git a/doc/version-history.rst b/doc/version-history.rst index 7eeaeeb7..7841333b 100644 --- a/doc/version-history.rst +++ b/doc/version-history.rst @@ -6,6 +6,7 @@ v1.11.0 ------- * Modbus::, ILC::ILCBusList classes. Communication code moved from ModbusBuffer to FPGA class. +* Improved documentation. v1.10.1 ------- diff --git a/include/Modbus/Buffer.h b/include/Modbus/Buffer.h index bcbe4a6b..efec5a34 100644 --- a/include/Modbus/Buffer.h +++ b/include/Modbus/Buffer.h @@ -28,18 +28,23 @@ #include #include -/*** - * 24 bit type integer.This is used to pass some parameters to ILC calls. +namespace Modbus { + +/** + * 24 bit (3 bytes) integer. This is used to pass some parameters to ILC calls. */ class int24_t { public: - int32_t value; + int32_t value; ///< value stored in the class + /** + * Construct int24_t value. + * + * @param v integer value + */ int24_t(int32_t v) { value = v; } }; -namespace Modbus { - /** * Thrown when ModBus error response is received. */ @@ -61,7 +66,16 @@ class Exception : public std::runtime_error { */ class Buffer : public std::vector { public: + /** + * Construct an empty buffer. + */ Buffer(); + + /** + * Fill newly constructed buffer with data. + * + * @param data Buffer's data + */ Buffer(std::vector data) : std::vector(data) {} /** @@ -70,12 +84,16 @@ class Buffer : public std::vector { * * @param address ModBus address on subnet * @param func ModBus function to call + * @param params ModBus command arguments. */ template Buffer(uint8_t address, uint8_t func, const dt&... params) { callFunction(address, func, params...); } + /** + * Destruct buffer instance. + */ virtual ~Buffer(); /** @@ -111,7 +129,20 @@ class Buffer : public std::vector { */ void writeCRC(); + /** + * Return address stored in the Modbus buffer. Essentially returns the + * first byte. + * + * @return Buffer command address + */ uint8_t address() { return at(0); } + + /** + * Return function call stored in the Modbus buffer. Essentially returns + * the second byte. + * + * @return Function call code + */ uint8_t func() { return at(1); } /** @@ -160,11 +191,21 @@ class Buffer : public std::vector { } }; +/** + * Write 8 bit signed integer to the end of the buffer. + * + * @param data 8 bit signed integer to write + */ template <> inline void Buffer::write(int8_t data) { pushBuffer(reinterpret_cast(&data), 1); } +/** + * Write 16 bit signed integer to the end of the buffer. + * + * @param data 16 bit signed integer to write + */ template <> inline void Buffer::write(int16_t data) { int16_t d = htons(data); diff --git a/include/Modbus/BusList.h b/include/Modbus/BusList.h index 5cae25fc..85f0ed1b 100644 --- a/include/Modbus/BusList.h +++ b/include/Modbus/BusList.h @@ -36,26 +36,58 @@ namespace Modbus { +/** + * Error thrown when a response is missing. This is mostly caused by an ILC on + * the bus being dead/not reacting to the command send. + */ class MissingResponse : std::runtime_error { public: + /** + * Construct missing response exception. + * + * @param address Expected ILC address, for which response wasn't received + * @param func Expected function which wasn't responded by the ILC + */ MissingResponse(uint8_t address, uint8_t func) : std::runtime_error(fmt::format("Missing response for function {} from ILC with address {}", func, address)) {} }; +/** + * Error thrown when response action for the function received in response is + * undefined. + * + * @see BusList::addResponse + */ class UnexpectedResponse : std::runtime_error { public: + /** + * Construct unexpected response. + * + * @param address Modbus address for which the response action was undefined + * @param func Function code for which the response action was undefined + */ UnexpectedResponse(uint8_t address, uint8_t func) : std::runtime_error( fmt::format("Unexpected response - received {} for address {}", address, func)) {} }; +/** + * Utility class to hold command buffer with timinig data. BusList is a collection of those + */ class CommandRecord { public: + /** + * Construct record for the command send. + * + * @param _buffer Modbus buffer with command (address/function with possible arguments) + * @param _timming Maximal response time in microseconds + */ CommandRecord(Buffer _buffer, uint32_t _timming) : buffer(_buffer), timming(_timming) {} - Buffer buffer; - uint32_t timming; + Buffer buffer; ///< Buffer send to the bus + uint32_t timming; ///< Timing value in microseconds. An error shall be thrown when the response isn't + ///< received in the specified time. }; /** @@ -63,41 +95,129 @@ class CommandRecord { */ class ResponseRecord { public: + /** + * Construct callback entry. + * + * @param _func Function code + * @param _action Action to execute when function code is encountered + * @param _error_reply Error reply. That's usually 0x80 | _func + * @param _error_action Error action + */ ResponseRecord(uint8_t _func, std::function _action, uint8_t _error_reply, std::function _error_action) : func(_func), action(_action), error_reply(_error_reply), error_action(_error_action) {} - const uint8_t func; - std::function action; - const uint8_t error_reply; - std::function error_action; + + const uint8_t func; ///< sucessfull response function code + std::function action; ///< action to call on sucessfull response + const uint8_t error_reply; ///< error response code + std::function error_action; ///< action to call on the error response }; /** - * Manages communication with ILCs. Provides methods to call function, - * construct a buffer to send to FPGA, and handles FPGA response. Should be - * sub-classed to form a specialized classes for a devices and purposes (e.g. a - * different ILC types). + * Manages communication with devices on the ModBus bus. Provides methods to + * call function, construct a buffer to send to FPGA, and handles FPGA + * response. Should be sub-classed to form a specialized classes for a devices + * and purposes (e.g. a different ILC types). + * + * When functions are scheduled to be called, the record of the call is stored + * in the classs. When response buffer is parsed, the class check if the passed + * response address and function code matches expected values. + * + * The BusList is ideally suited to send a repated message patterns. Take for + * example the M1M3. The loop to operate the mirror sends to ILC those + * commands: + * + * 1. Broadcast command to set actuators target values. Use of the broadcast, + * which doesn't require any response from the ILCs, significantly decreases + * time needed to send the target values to ILCs. + * + * 2. Query all ILCs in unicast fashion (adressing all ILCs on the bus). The + * responses are checked against target and safe values. + * + * As the second set of commands is the same (query is 4 bytes, address, + * function code and 2 byte CRC, and those values are fixed), those can be + * stored and hence no CRC computation is executed in the loop. Only the + * broadcast command is constructed every time the loop executes. + * + * Usually the commands are send on the FIFO along with communication-specific + * data, e.g. time the port communication logic shall wait for the reply and + * expected length of the reply. So the list is formed in the loop, passed in + * FIFO to FPGA. The FPGA executes commands, and store responses in a FIFO. + * This approach distrubutes communication logic to FPGA, leaving CPU free to + * perform other tasks. + * + * When parsing responses, the class check if responses's addresses matches + * expected addresses. */ class BusList : public std::vector { public: BusList(); /** - * Reset bus list parsing processing. + * Reset bus list parsing processing. Move index of the parsed responses + * back to beggining, indicating a new responses might be parsed. */ void reset() { _parsed_index = 0; } + /** + * Calls Modbus function. Timing parameter is passed to FPGA, which is + * responsible to properly use it. Timing parameter is passed to FPGA, + * which is responsible to pass it to the hardware controller. + * + * @param address ModBus/ILC address + * @param func ModBus/ILC function code + * @param timing function call timing in microseconds (1/10^-6 second) + */ void callFunction(uint8_t address, uint8_t func, uint32_t timming) { emplace(end(), CommandRecord(Buffer(address, func), timming)); } + /** + * Template to call Modbus function with variable arguments. Argument types + * are used to encode data as Modbus command, including possible endian + * conversion. Timing parameter is passed to FPGA, which is responsible to + * pass it to the hardware controller. + * + * @param address ModBus/ILC address + * @param func ModBus/ILC function code + * @param timming function call timing in microseconds (1/10^-6 second) + * @param ...params + * + * Intended usage: + * + * @code{cpp} + * BusList busList; + * + * uint16_t arg1 = 1; + * float arg2 = 2.34; + * uint8_t arg3 = 5; + * + * busList.callFunction(10, 11, 500, arg1, arg2, arg3); + * @endcode + */ template void callFunction(uint8_t address, uint8_t func, uint32_t timming, const dt &...params) { emplace(end(), CommandRecord(Buffer(address, func, params...), timming)); } + /** + * Process ILC response. Address and function of the response are checked if + * + * @throw MissingResponse when response for the address is missing. The + * calling code can catch the exception, pass the same response again, and + * if it pass, then the ILC did not respond to command and can (if that's + * safe) disable the failing ILC. + * + * @throw UnexpectedResponse Throwed when the bus list action for the + * address/function (set with the addResponse call) wasn't set. + */ void parse(Parser parser); + /** + * Construct buffer from passed data and parse the response. + * + * @see ::parse(Parser) + */ void parse(uint8_t *data, size_t len) { parse(std::vector(data, data + len)); } /** diff --git a/include/Modbus/Parser.h b/include/Modbus/Parser.h index 26f14030..d0fb38fe 100644 --- a/include/Modbus/Parser.h +++ b/include/Modbus/Parser.h @@ -60,6 +60,13 @@ static const std::string hexDump(dt *buf, size_t len) { */ class CRCError : public std::runtime_error { public: + /** + * Construct new CRC error. Signal either problem in the sending + * electronics, or problems parsing the received replies. + * + * @param calculated CRC value calculated from the data (=expected) + * @param received CRC value received in response (=produced by the other side). + */ CRCError(uint16_t calculated, uint16_t received); }; @@ -68,29 +75,44 @@ class CRCError : public std::runtime_error { */ class LongResponse : std::runtime_error { public: + /** + * Construct LongResponse from the given buffer. + * + * @param buf Buffer with data received + * @param len Buffer length + */ LongResponse(uint8_t *buf, size_t len) : std::runtime_error(std::string("Too long response - received ") + hexDump(buf, len)) {} }; +/** + * Class parsing a single Modbus response. + */ class Parser : public std::vector { public: + /** + * Construct parser from given input data. Essentially only store the data + * into buffer. + * + * @param buffer Buffer to parse + */ Parser(std::vector buffer) { parse(buffer); } + /** + * Sets the given buffer as the one to be parsed. + */ void parse(std::vector buffer); /** - * Check that accumulated data CRC matches readed CRC. Also stops recording - * of changes, as CRC shall be at the end of buffer. - * - * @see pauseRecordChanges + * Check that so far read data CRC matches calculated CRC. * * @throw CRCError if CRC doesn't match */ void checkCRC(); /** - * Reads data from buffer. Size of * receiving buffer is assumed to be - * equal or greater than len. + * Reads data from the buffer. Size of the receiving buffer is assumed to + * be equal or greater than len argument. * * @param buf target buffer to read the data * @param len how many bytes shall be read @@ -139,17 +161,30 @@ class Parser : public std::vector { */ std::string readString(size_t length); + /** + * Returns address from the Modbus buffer. Actually returns the first byte. + * + * @return Modbus command address + */ const uint8_t address() { return at(0); } + /** + * Returns function code from the Modbus buffer. Actually returns the + * second byte. + * + * @return Modbus function code + */ const uint8_t func() { return at(1); } private: - /*** - * Data pointer. - */ - size_t _data = 0; + size_t _data = 0; ///< Data pointer. }; +/** + * Read 24 bites (3 bytes) integer value from the buffer. + * + * @return 24 bites value read from the buffer. + */ template <> inline int24_t Parser::read() { int32_t db = 0; diff --git a/include/cRIO/ElectromechanicalPneumaticILC.h b/include/cRIO/ElectromechanicalPneumaticILC.h index 3953fa3d..68e7f2d0 100644 --- a/include/cRIO/ElectromechanicalPneumaticILC.h +++ b/include/cRIO/ElectromechanicalPneumaticILC.h @@ -24,14 +24,22 @@ #define _cRIO_ElectromechanicalPneumaticILC_h #include +#include namespace LSST { namespace cRIO { /** - * Class for communication with Electromechanical and Pneumatic ILCs. + * Class for communication with Electromechanical and Pneumatic ILCs. Those are + * both in M1M3 for force actuator controls, as in M2 for stepper driven + * actuator controls. The class handles requests for both, leaving it up to the + * end users which methods will be implemented. * * Replies received from ILCs shall be processed with ILC::processResponse method. + * + * @defgroup M1M3_fa used on M1M3 force actuators + * @defgroup M1M3_hp used on M1M3 hardpoints + * @defgroup M2 used on M2 stepper motor actuators */ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { public: @@ -47,6 +55,11 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * * @param address ILC address * @param steps commanded steps + * + * @note processStepperForceStatus method is called to process replies + * + * @ingroup M1M3_hp + * @ingroup M2 */ void setStepperSteps(uint8_t address, int8_t steps) { callFunction(address, 66, 1800, steps); } @@ -56,26 +69,50 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * as M1M3 hardpoints. * * @param address ILC address + * + * @note processStepperForceStatus method is called to process replies + * + * @ingroup M1M3_hp + * @ingroup M2 */ void reportStepperForceStatus(uint8_t address) { callFunction(address, 67, 1800); } /** - * Unicast command to read hardpoint LVDT. ILC command 122 (0x7a). + * Unicast command to set a single force actuator force offset. * * @param address ILC address + * @param slewFlag DCA booster valve + * @param primary primary actuator force offset (N) + * + * @ingroup M1M3_fa */ - void reportHardpointLVDT(uint8_t address) { callFunction(address, 122, 400); } - void setSAAForceOffset(uint8_t address, bool slewFlag, float primary) { callFunction(address, 75, 1800, static_cast(slewFlag ? 0xFF : 0x00), - int24_t(primary * 1000)); + Modbus::int24_t(primary * 1000)); } + /** + * Unicast command to set a dual force actuator force offsets. + * + * @param address ILC address + * @param slewFlag DCA booster valve + * @param primary primary actuator force offset (N) + * @param secondary secondary actuator force offset (N) + * + * @ingroup M1M3_fa + */ void setDAAForceOffset(uint8_t address, bool slewFlag, float primary, float secondary) { - callFunction(address, 75, 1800, static_cast(slewFlag ? 0xFF : 0x00), int24_t(primary * 1000), - int24_t(secondary * 1000)); + callFunction(address, 75, 1800, static_cast(slewFlag ? 0xFF : 0x00), + Modbus::int24_t(primary * 1000), Modbus::int24_t(secondary * 1000)); } + /** + * Reports force actuator status. + * + * @params address ILC address + * + * @ingroup M1M3_fa + */ void reportForceActuatorForceStatus(uint8_t address) { callFunction(address, 76, 1800); } /** @@ -84,6 +121,10 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @param address ILC address * @param orimaryGain axial booster valve gain * @param secondaryGain lateral booster valve gain + * + * @note no method is called for response, as this only sets the ILC paremeters + * + * @ingroup M1M3_fa */ void setDCAGain(uint8_t address, float primaryGain, float secondaryGain) { callFunction(address, 73, 40000, primaryGain, secondaryGain); @@ -93,6 +134,10 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * Read DCA Gain. ILC Command code 74 (0x4A). * * @param address ILC address + * + * @note processDCAGain method is called to process replies + * + * @ingroup M1M3_fa */ void reportDCAGain(uint8_t address) { callFunction(address, 74, 2000); } @@ -103,6 +148,11 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @param channel ADC channel (1-4) * @param offset ADC offset value * @param sensitivity ADC sensitivity value + * + * @note no method is called for response, as this only sets the ILC paremeters + * + * @ingroup M1M3_fa + * @ingroup M2 */ void setOffsetAndSensitivity(uint8_t address, uint8_t channel, float offset, float sensitivity) { callFunction(address, 81, 36500, channel, offset, sensitivity); @@ -112,6 +162,12 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * Read ILC calibration data. ILC command code 110 (0x6E). * * @param address ILC address + * + * @note processCalibrationData method is called to process replies + * + * @ingroup M1M3_fa + * @ingroup M1M3_hp + * @ingroup M2 */ void reportCalibrationData(uint8_t address) { callFunction(address, 110, 1800); } @@ -119,9 +175,22 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * Read ILC mezzanine pressure. ILC command code 119 (0x77). * * @param address ILC address + * + * @ingroup M1M3_hp */ void reportMezzaninePressure(uint8_t address) { callFunction(address, 119, 1800); } + /** + * Unicast command to read hardpoint LVDT. ILC command 122 (0x7a). + * + * @param address ILC address + * + * @note processHardpointLVDT method is called to process replies + * + * @ingroup M1M3_hp + */ + void reportHardpointLVDT(uint8_t address) { callFunction(address, 122, 400); } + protected: /** * Called when response from call to command unicast 66 (0x42) and 67 @@ -131,6 +200,8 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @param status hardpoint Status * @param encoderPostion encoder position * @param loadCellForce measured load cell/actuator force + * + * @ingroup M2 */ virtual void processStepperForceStatus(uint8_t address, uint8_t status, int32_t encoderPostion, float loadCellForce) = 0; @@ -141,6 +212,8 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @param address returned from this ILC * @param primaryGain axial booster gain value * @param secondaryGain lateral booster gain value + * + * @ingroup M1M3_fa */ virtual void processDCAGain(uint8_t, float primaryGain, float secondaryGain) = 0; @@ -150,10 +223,34 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @param address returned from this ILC * @param breakawayLVDT breakway LVDT value * @param displacementLVDT displacement LVDT value + * + * @ingroup M1M3_hp */ virtual void processHardpointLVDT(uint8_t address, float breakawayLVDT, float displacementLVDT) = 0; + /** + * Called when response from call to command 210 or 220 is received. Called + * for replies containing single axis (1 float for a single load cell) values. + * + * @param address returned from this ILC + * @param status force actuator status mask + * @param primaryLoadCellForce force in Newtons measured on the primary axis load cell + * + * @ingroup M1M3_fa + */ virtual void processSAAForceStatus(uint8_t address, uint8_t status, float primaryLoadCellForce) = 0; + + /** + * Called when response from call to command 210 or 220 is received. Called + * for replies containing double axis (2 floats for two load cells) values. + * + * @param address returned from this ILC + * @param status force actuator status mask + * @param primaryLoadCellForce force in Newtons measured on the primary axis load cell + * @param secondaryLoadCellForce force in Newtons measured on the secondary axis load cell + * + * @ingroup M1M3_fa + */ virtual void processDAAForceStatus(uint8_t address, uint8_t status, float primaryLoadCellForce, float secondaryLoadCellForce) = 0; @@ -167,11 +264,26 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @param backupADCK[4] backup ADC calibration Kn * @param backupOffset[4] backup sensor n offset * @param backupSensitivity[4] backup sensor n sensitivity + * + * @ingroup M1M3_fa + * @ingroup M1M3_hp + * @ingroup M2 */ virtual void processCalibrationData(uint8_t address, float mainADCK[4], float mainOffset[4], float mainSensitivity[4], float backupADCK[4], float backupOffset[4], float backupSensitivity[4]) = 0; + /** + * Called when response from call to command 119 is read. + * + * @param address status returned from this ILC + * @param primaryPush primary push cylinder pressure (psi) + * @param primaryPull primary pull cylinder pressure (psi) + * @param secondaryPush secondary push cylinder pressure. Some value filled for single axis FAs. (psi) + * @param secondaryPull secondary pull cylinder pressure. Some value filled for single axis FAs. (psi) + * + * @ingroup M1M3_fa + */ virtual void processMezzaninePressure(uint8_t address, float primaryPush, float primaryPull, float secondaryPush, float secondaryPull) = 0; }; diff --git a/include/cRIO/ThermalILC.h b/include/cRIO/ThermalILC.h index 5c555f0d..b21b2c30 100644 --- a/include/cRIO/ThermalILC.h +++ b/include/cRIO/ThermalILC.h @@ -105,6 +105,8 @@ class ThermalILC : public virtual ILC::ILCBusList { * Report re-heater gains. Command code 93 (0x5D). * * @param address ILC address to query. + * + * @note processThermalStatus method is called for replies */ void reportReHeaterGains(uint8_t address) { callFunction(address, 93, 300); } diff --git a/src/Modbus/BusList.cpp b/src/Modbus/BusList.cpp index 4423983f..3dc53b7b 100644 --- a/src/Modbus/BusList.cpp +++ b/src/Modbus/BusList.cpp @@ -53,8 +53,7 @@ void BusList::parse(Parser parser) { return; } } - throw std::runtime_error( - fmt::format("Unknown/unexpected Modbusi function {} for address {}", called, address)); + throw UnexpectedResponse(address, called); } void BusList::addResponse(uint8_t func, std::function action, uint8_t error_reply, diff --git a/tests/test_ElectromechanicalPneumaticILC.cpp b/tests/test_ElectromechanicalPneumaticILC.cpp index 4dbf1b29..84fd4d50 100644 --- a/tests/test_ElectromechanicalPneumaticILC.cpp +++ b/tests/test_ElectromechanicalPneumaticILC.cpp @@ -125,7 +125,7 @@ TEST_CASE("Test SSA force set", "[ElectromechanicalPneumaticILC]") { REQUIRE(parser.address() == 18); REQUIRE(parser.func() == 75); REQUIRE(parser.read() == 0xFF); - REQUIRE(parser.read().value == 31450); + REQUIRE(parser.read().value == 31450); REQUIRE_NOTHROW(parser.checkCRC()); Modbus::Buffer response; @@ -152,7 +152,7 @@ TEST_CASE("Test DAA force set", "[ElectromechanicalPneumaticILC]") { REQUIRE(parser.address() == 212); REQUIRE(parser.func() == 75); REQUIRE(parser.read() == 0x00); - REQUIRE(parser.read().value == -123450); + REQUIRE(parser.read().value == -123450); // hex(345100) = 0x05 44 0C REQUIRE(parser.read() == 0x05); REQUIRE(parser.read() == 0x44); diff --git a/tests/test_Modbus_Parser.cpp b/tests/test_Modbus_Parser.cpp index 16476f60..bd8b2f47 100644 --- a/tests/test_Modbus_Parser.cpp +++ b/tests/test_Modbus_Parser.cpp @@ -27,8 +27,9 @@ using namespace Modbus; TEST_CASE("Parser buffer", "[Parsing]") { - std::vector data = {0x81, 0x11, 0x10, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAA, 0xFF, 0xBB, - 0xCC, 0xDD, 0xEE, 0x11, 0x53, 0x74, 0x61, 0x72, 0xA7, 0x9F}; + std::vector data = {0x81, 0x11, 0x10, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAA, 0xFF, + 0xBB, 0xCC, 0xDD, 0xEE, 0x11, 0x53, 0x74, 0x61, 0x72, 0x12, + 0x23, 0x34, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0x84, 0x52}; Parser parser(data); @@ -39,6 +40,9 @@ TEST_CASE("Parser buffer", "[Parsing]") { CHECK(parser.read() == 0x567890AA); CHECK(parser.read() == 0xFFBBCCDDEE115374); CHECK(parser.readString(2) == "ar"); + CHECK(parser.read().value == 0x122334); + CHECK(parser.read().value == -1); + CHECK(parser.read().value == -0x7FFFFF); REQUIRE_NOTHROW(parser.checkCRC()); } From d3415c6733d8f5e795aee055b1f20aeb3e66f5cd Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Mon, 11 Mar 2024 09:03:46 +0100 Subject: [PATCH 02/13] Added glossarry. --- Doxyfile | 1 + include/ILC/ILCBusList.h | 117 ++++++++++++------- include/Modbus/BusList.h | 53 ++++----- include/cRIO/ElectromechanicalPneumaticILC.h | 64 +++++----- include/cRIO/FPGACliApp.h | 14 +-- include/cRIO/PrintILC.h | 55 ++++----- include/cRIO/ThermalILC.h | 2 +- include/cRIO/glossary.dox | 54 +++++++++ src/ILC/ILCBusList.cpp | 14 +-- 9 files changed, 232 insertions(+), 142 deletions(-) create mode 100644 include/cRIO/glossary.dox diff --git a/Doxyfile b/Doxyfile index d02b5c71..deffedac 100644 --- a/Doxyfile +++ b/Doxyfile @@ -284,6 +284,7 @@ TAB_SIZE = 4 # @} or use a double escape (\\{ and \\}) ALIASES = "multithreading=\xrefitem multithreading \"MT-Safety\" \"MT-Safety\"" +ALIASES += "glos{1}=[\1](\ref GLOSSARY_\1)" # 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 diff --git a/include/ILC/ILCBusList.h b/include/ILC/ILCBusList.h index 4f2388a2..0487a0c2 100644 --- a/include/ILC/ILCBusList.h +++ b/include/ILC/ILCBusList.h @@ -32,32 +32,51 @@ namespace ILC { enum Mode { Standby = 0, Disabled = 1, Enabled = 2, FirmwareUpdate = 3, Fault = 4, ClearFaults = 5 }; /** - * Handles basic ILC communication. Provides methods to run ILC functions and - * callback for responses. + * Handles basic @glos{ILC} communication. Provides methods to run @glos{ILC} + * functions and callback for responses. + * + * ILCs are on a bus identified by the bus number. The details how bus commands + * are transferred to FPGA and replies are receieved is left on the FPGA + * handling class. As transfer is ussually done through some @glos{FIFO}s, the + * number of busses is limited by number of target-host FIFOs in the FPGA.a */ class ILCBusList : public Modbus::BusList { public: + /** + * Construct new bus list. + * + * @param bus FPGA ILC bus number. + */ ILCBusList(uint8_t bus); + + /** + * Destroy ILC bus instance. + */ virtual ~ILCBusList(); + /** + * Return the FPGA bus ILCs operates on. + * + * @return FPGA ILC bus indentificator + */ const uint8_t getBus() { return _bus; } /** - * Calls function 17 (0x11), ask for ILC identity. + * Calls function 17 (0x11), ask for @glos{ILC} identity. * - * @param address ILC address + * @param address @glos{ICL} address */ - void reportServerID(uint8_t address) { callFunction(address, 17, 835); } + void reportServerID(uint8_t address) { callFunction(address, ILC_CMD::SERVER_ID, 835); } /** - * Calls function 18 (0x12), ask for ILC status. + * Calls function 18 (0x12), ask for @glos{ILC} status. * - * @param address ILC address + * @param address @glos{ICL} address */ - void reportServerStatus(uint8_t address) { callFunction(address, 18, 270); } + void reportServerStatus(uint8_t address) { callFunction(address, ILC_CMD::SERVER_STATUS, 270); } /** - * Change ILC mode. Calls function 65 (0x41). Supported ILC modes are: + * Change @glos{ILC} mode. Calls function 65 (0x41). Supported @glos{ILC} modes are: * * Mode | Supported by | Description * ---- | ------------ | ------------------------------------ @@ -71,27 +90,29 @@ class ILCBusList : public Modbus::BusList { * where all is Electromechanical (Hard-Point), Pneumatic, Thermal and * Hardpoint Monitoring ILC. HM is Hardpoint Monitoring. * - * @param address ILC address - * @param mode new ILC mode - see above + * @param address @glos{ICL} address + * @param mode new @glos{ILC} mode - see above */ void changeILCMode(uint8_t address, uint16_t mode); /** - * Set temporary ILC address. ILC must be address-less (attached to address - * 255). Can be used only if one ILC on a bus failed to read its address + * Set temporary @glos{ICL} address. @glos{ILC} must be address-less (attached to address + * 255). Can be used only if one @glos{ILC} on a bus failed to read its address * from its network connection and therefore adopts the failure address * 255. * - * @param temporaryAddress new ILC address + * @param temporaryAddress new @glos{ICL} address */ - void setTempILCAddress(uint8_t temporaryAddress) { callFunction(255, 72, 250, temporaryAddress); } + void setTempILCAddress(uint8_t temporaryAddress) { + callFunction(255, ILC_CMD::SET_TEMP_ADDRESS, 250, temporaryAddress); + } /** * Reset ILC. Calls function 107 (0x6b). * - * @param address ILC address + * @param address @glos{ICL} address */ - void resetServer(uint8_t address) { callFunction(address, 107, 86840); } + void resetServer(uint8_t address) { callFunction(address, ILC_CMD::RESET_SERVER, 86840); } protected: /** @@ -104,7 +125,7 @@ class ILCBusList : public Modbus::BusList { * @param counter broadcast counter. ModBus provides method to retrieve this * in unicast function to verify the broadcast was received and processed * @param data function parameters. Usually device's bus ID indexed array - * of values to pass to the devices + _ */ void broadcastFunction(uint8_t address, uint8_t func, uint32_t delay, uint8_t counter, std::vector data); @@ -126,7 +147,7 @@ class ILCBusList : public Modbus::BusList { /** * Return string with current mode description. * - * @param mode ILC mode, as returned by function 18. + * @param mode @glos{ILC} mode, as returned by function 18. * * @return status description (enabled, standby,..). */ @@ -144,20 +165,20 @@ class ILCBusList : public Modbus::BusList { enum ILCStatus { MajorFault = 0x0001, MinorFault = 0x0002, FaultOverride = 0x0008 }; /** - * Returns last know mode (state) of the ILC at the address. + * Returns last know mode (state) of the @glos{ILC} at the address. * - * @param address ILC node address + * @param address @glos{ILC} node address * - * @return last know ILC state + * @return last know @glos{ILC} state * - * @throw std::out_of_range when ILC mode is not known + * @throw std::out_of_range when @glos{ILC} mode is not known */ uint8_t getLastMode(uint8_t address) { return _lastMode.at(address); } /** - * Return ILC fault textual description. + * Return @glos{ILC} fault textual description. * - * @param fault ILC faults returned by function 18. + * @param fault @glos{ILC} faults returned by function 18. * * @return vector of strings with fault description */ @@ -241,15 +262,15 @@ class ILCBusList : public Modbus::BusList { * * * - * @param address ILC address - * @param uniqueID ILC unigue ID - * @param ilcAppType ILC App (Firmware) Type. See Types table for values - * @param networkNodeType ILC Network Node (TEDS) Type. See Types table for values - * @param ilcSelectedOptions ILC Selected Options. See Selected Options table for values - * @param networkNodeOptions ILC Network Node (TEDS) Options. See Selected Options table for values + * @param address @glos{ICL} address + * @param uniqueID @glos{ILC} unigue ID + * @param ilcAppType @glos{ILC} App (Firmware) Type. See Types table for values + * @param networkNodeType @glos{ILC} Network Node (TEDS) Type. See Types table for values + * @param ilcSelectedOptions @glos{ILC} Selected Options. See Selected Options table for values + * @param networkNodeOptions @glos{ILC} Network Node (TEDS) Options. See Selected Options table for values * @param majorRev Firmware major revision number * @param minorRev Firmware minor revision number - * @param firmwareName ASCII name string for ILC firmware + * @param firmwareName ASCII name string for @glos{ILC} firmware */ virtual void processServerID(uint8_t address, uint64_t uniqueID, uint8_t ilcAppType, uint8_t networkNodeType, uint8_t ilcSelectedOptions, @@ -259,18 +280,18 @@ class ILCBusList : public Modbus::BusList { /** * Callback for server status reply. * - * @param address ILC address - * @param mode ILC mode - * @param status ILC status - * @param faults ILC faults + * @param address @glos{ICL} address + * @param mode @glos{ILC{ mode + * @param status @glos{ILC} status + * @param faults @glos{ILC} faults */ virtual void processServerStatus(uint8_t address, uint8_t mode, uint16_t status, uint16_t faults) = 0; /** - * Callback for change ILC mode reply. + * Callback for change @glos{ILC} mode reply. * - * @param address ILC address - * @param mode new (current) ILC mode + * @param address @glos{ICL} address + * @param mode new (current) @glos{ILC} mode */ virtual void processChangeILCMode(uint8_t address, uint16_t mode) = 0; @@ -285,16 +306,26 @@ class ILCBusList : public Modbus::BusList { /** * Callback for reply from server reset. * - * @param address ILC address + * @param address @glos{ICL} address */ virtual void processResetServer(uint8_t address) = 0; private: - const uint8_t _bus; + const int _bus; - // last know ILC mode - std::map _lastMode; + std::map _lastMode; ///< last know @glos{ILC} mode uint8_t _broadcastCounter = 0; + + /** + * ILC commands numbers. See LTS-346 and LTS-646 for details. + */ + enum ILC_CMD { + SERVER_ID = 17, + SERVER_STATUS = 18, + CHANGE_MODE = 65, + SET_TEMP_ADDRESS = 72, + RESET_SERVER = 107 + }; }; } // namespace ILC diff --git a/include/Modbus/BusList.h b/include/Modbus/BusList.h index 85f0ed1b..18a4da85 100644 --- a/include/Modbus/BusList.h +++ b/include/Modbus/BusList.h @@ -37,7 +37,7 @@ namespace Modbus { /** - * Error thrown when a response is missing. This is mostly caused by an ILC on + * Error thrown when a response is missing. This is mostly caused by an @glos{ILC} on * the bus being dead/not reacting to the command send. */ class MissingResponse : std::runtime_error { @@ -45,8 +45,8 @@ class MissingResponse : std::runtime_error { /** * Construct missing response exception. * - * @param address Expected ILC address, for which response wasn't received - * @param func Expected function which wasn't responded by the ILC + * @param address Expected @glos{ILC} address, for which response wasn't received + * @param func Expected function which wasn't responded by the @glos{ILC} */ MissingResponse(uint8_t address, uint8_t func) : std::runtime_error(fmt::format("Missing response for function {} from ILC with address {}", @@ -115,23 +115,23 @@ class ResponseRecord { /** * Manages communication with devices on the ModBus bus. Provides methods to - * call function, construct a buffer to send to FPGA, and handles FPGA - * response. Should be sub-classed to form a specialized classes for a devices - * and purposes (e.g. a different ILC types). + * call function, construct a buffer to send to the @glos{FPGA}, and handles + * the @glos{FPGA} response. Should be sub-classed to form a specialized + * classes for a devices and purposes (e.g. a different @glos{ILC} types). * * When functions are scheduled to be called, the record of the call is stored * in the classs. When response buffer is parsed, the class check if the passed * response address and function code matches expected values. * * The BusList is ideally suited to send a repated message patterns. Take for - * example the M1M3. The loop to operate the mirror sends to ILC those + * example the M1M3. The loop to operate the mirror sends to @glos{ILC} those * commands: * * 1. Broadcast command to set actuators target values. Use of the broadcast, - * which doesn't require any response from the ILCs, significantly decreases - * time needed to send the target values to ILCs. + * which doesn't require any response from the @glos{ILC}s, significantly decreases + * time needed to send the target values to @glos{ILC}s. * - * 2. Query all ILCs in unicast fashion (adressing all ILCs on the bus). The + * 2. Query all @glos{ILC}s in unicast fashion (adressing all @glos{ILC}s on the bus). The * responses are checked against target and safe values. * * As the second set of commands is the same (query is 4 bytes, address, @@ -139,12 +139,12 @@ class ResponseRecord { * stored and hence no CRC computation is executed in the loop. Only the * broadcast command is constructed every time the loop executes. * - * Usually the commands are send on the FIFO along with communication-specific + * Usually the commands are send on the @glos{FIFO} along with communication-specific * data, e.g. time the port communication logic shall wait for the reply and * expected length of the reply. So the list is formed in the loop, passed in - * FIFO to FPGA. The FPGA executes commands, and store responses in a FIFO. - * This approach distrubutes communication logic to FPGA, leaving CPU free to - * perform other tasks. + * FIFO to the @glos{FPGA}. The @glos{FPGA} executes commands, and store + * responses in a FIFO. This approach distrubutes communication logic to the + * @glos{FPGA}, leaving CPU free to perform other tasks. * * When parsing responses, the class check if responses's addresses matches * expected addresses. @@ -160,13 +160,14 @@ class BusList : public std::vector { void reset() { _parsed_index = 0; } /** - * Calls Modbus function. Timing parameter is passed to FPGA, which is - * responsible to properly use it. Timing parameter is passed to FPGA, - * which is responsible to pass it to the hardware controller. + * Calls Modbus function. Timing parameter is passed to the @glos{FPGA}, + * which is responsible to properly use it. Timing parameter is passed to + * the @glos{FPGA}, which is responsible to pass it to the hardware + * controller. * - * @param address ModBus/ILC address - * @param func ModBus/ILC function code - * @param timing function call timing in microseconds (1/10^-6 second) + * @param address ModBus/@glos{ILC} address @param func ModBus/@glos{ILC} + * function code @param timing function call timing in microseconds + * (1/10^-6 second) */ void callFunction(uint8_t address, uint8_t func, uint32_t timming) { emplace(end(), CommandRecord(Buffer(address, func), timming)); @@ -175,11 +176,11 @@ class BusList : public std::vector { /** * Template to call Modbus function with variable arguments. Argument types * are used to encode data as Modbus command, including possible endian - * conversion. Timing parameter is passed to FPGA, which is responsible to + * conversion. Timing parameter is passed to the @glos{FPGA}, which is responsible to * pass it to the hardware controller. * - * @param address ModBus/ILC address - * @param func ModBus/ILC function code + * @param address ModBus/@glos{ILC} address + * @param func ModBus/@glos{ILC} function code * @param timming function call timing in microseconds (1/10^-6 second) * @param ...params * @@ -201,12 +202,12 @@ class BusList : public std::vector { } /** - * Process ILC response. Address and function of the response are checked if + * Process @glos{ILC} response. Address and function of the response are checked if * * @throw MissingResponse when response for the address is missing. The * calling code can catch the exception, pass the same response again, and - * if it pass, then the ILC did not respond to command and can (if that's - * safe) disable the failing ILC. + * if it pass, then the @glos{ILC} did not respond to command and can (if that's + * safe) disable the failing @glos{ILC}. * * @throw UnexpectedResponse Throwed when the bus list action for the * address/function (set with the addResponse call) wasn't set. diff --git a/include/cRIO/ElectromechanicalPneumaticILC.h b/include/cRIO/ElectromechanicalPneumaticILC.h index 68e7f2d0..25221428 100644 --- a/include/cRIO/ElectromechanicalPneumaticILC.h +++ b/include/cRIO/ElectromechanicalPneumaticILC.h @@ -26,6 +26,12 @@ #include #include +/** + * @defgroup M1M3_fa M1M3 force actuators functions + * @defgroup M1M3_hp M1M3 hardpoints functions + * @defgroup M2 M2 stepper motor actuators functions + */ + namespace LSST { namespace cRIO { @@ -36,24 +42,20 @@ namespace cRIO { * end users which methods will be implemented. * * Replies received from ILCs shall be processed with ILC::processResponse method. - * - * @defgroup M1M3_fa used on M1M3 force actuators - * @defgroup M1M3_hp used on M1M3 hardpoints - * @defgroup M2 used on M2 stepper motor actuators */ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { public: /** - * Populate responses for known ILC functions. + * Populate responses for known @glos{ILC} functions. * - * @param bus ILC bus number (1..). + * @param bus @glos{ILC} bus number (1..). */ ElectromechanicalPneumaticILC(uint8_t bus); /** * Unicast command to command stepper motor moves. * - * @param address ILC address + * @param address @glos{ILC} address * @param steps commanded steps * * @note processStepperForceStatus method is called to process replies @@ -64,11 +66,11 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { void setStepperSteps(uint8_t address, int8_t steps) { callFunction(address, 66, 1800, steps); } /** - * Unicast Stepper motor ILC Force [N] and Status Request. ILC command + * Unicast Stepper motor @glos{ILC} Force [N] and Status Request. @glos{ILC} command * code 67 (0x43). Applies for M2 tangent and axials controllers, as well * as M1M3 hardpoints. * - * @param address ILC address + * @param address @glos{ILC} address * * @note processStepperForceStatus method is called to process replies * @@ -80,8 +82,8 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { /** * Unicast command to set a single force actuator force offset. * - * @param address ILC address - * @param slewFlag DCA booster valve + * @param address @glos{ILC} address + * @param slewFlag @glos{DCA} booster valve * @param primary primary actuator force offset (N) * * @ingroup M1M3_fa @@ -94,8 +96,8 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { /** * Unicast command to set a dual force actuator force offsets. * - * @param address ILC address - * @param slewFlag DCA booster valve + * @param address @glos{ILC} address + * @param slewFlag @glos{DCA} booster valve * @param primary primary actuator force offset (N) * @param secondary secondary actuator force offset (N) * @@ -109,20 +111,20 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { /** * Reports force actuator status. * - * @params address ILC address + * @params address @glos{ILC} address * * @ingroup M1M3_fa */ void reportForceActuatorForceStatus(uint8_t address) { callFunction(address, 76, 1800); } /** - * Unicast to set DCA Gain. ILC Command code 73 (0x49). + * Unicast to set @glos{DCA} Gain. @glos{ILC} Command code 73 (0x49). * - * @param address ILC address + * @param address @glos{ILC} address * @param orimaryGain axial booster valve gain * @param secondaryGain lateral booster valve gain * - * @note no method is called for response, as this only sets the ILC paremeters + * @note no method is called for response, as this only sets the @glos{ILC} paremeters * * @ingroup M1M3_fa */ @@ -131,9 +133,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { } /** - * Read DCA Gain. ILC Command code 74 (0x4A). + * Read @glos{DCA} Gain. @glos{ILC} command code 74 (0x4A). * - * @param address ILC address + * @param address @glos{ILC} address * * @note processDCAGain method is called to process replies * @@ -142,14 +144,14 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { void reportDCAGain(uint8_t address) { callFunction(address, 74, 2000); } /** - * Unicast ADC Channel Offset and Sensitivity. ILC command code 81 (0x51). + * Unicast ADC Channel Offset and Sensitivity. @glos{ILC} command code 81 (0x51). * - * @param address ILC address + * @param address @glos{ILC} address * @param channel ADC channel (1-4) * @param offset ADC offset value * @param sensitivity ADC sensitivity value * - * @note no method is called for response, as this only sets the ILC paremeters + * @note no method is called for response, as this only sets the @glos{ILC} paremeters * * @ingroup M1M3_fa * @ingroup M2 @@ -159,9 +161,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { } /** - * Read ILC calibration data. ILC command code 110 (0x6E). + * Read @glos{ILC} calibration data. @glos{ILC} command code 110 (0x6E). * - * @param address ILC address + * @param address @glos{ILC} address * * @note processCalibrationData method is called to process replies * @@ -172,18 +174,18 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { void reportCalibrationData(uint8_t address) { callFunction(address, 110, 1800); } /** - * Read ILC mezzanine pressure. ILC command code 119 (0x77). + * Read @glos{ILC} mezzanine pressure. @glos{ILC} command code 119 (0x77). * - * @param address ILC address + * @param address @glos{ILC} address * * @ingroup M1M3_hp */ void reportMezzaninePressure(uint8_t address) { callFunction(address, 119, 1800); } /** - * Unicast command to read hardpoint LVDT. ILC command 122 (0x7a). + * Unicast command to read hardpoint @glos{LVDT}. @glos{ILC} command 122 (0x7a). * - * @param address ILC address + * @param address @glos{ILC} address * * @note processHardpointLVDT method is called to process replies * @@ -220,9 +222,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { /** * Called when response from call to command 122 (0x7a) is read. * - * @param address returned from this ILC - * @param breakawayLVDT breakway LVDT value - * @param displacementLVDT displacement LVDT value + * @param address returned from this @glos{ILC} + * @param breakawayLVDT breakway @glos{LVDT} value + * @param displacementLVDT displacement @{LVDT} value * * @ingroup M1M3_hp */ diff --git a/include/cRIO/FPGACliApp.h b/include/cRIO/FPGACliApp.h index 8cc648eb..39121f19 100644 --- a/include/cRIO/FPGACliApp.h +++ b/include/cRIO/FPGACliApp.h @@ -34,14 +34,14 @@ typedef std::pair, uint8_t> ILCUnit; typedef std::list ILCUnits; /** - * Prints ILCUnit bus address and ILC address. + * Prints ILCUnit bus address and @glos{ILC} address. */ std::ostream& operator<<(std::ostream& stream, ILCUnit const& u); /** * Class for Command Line Client applications requiring access to FPGA. * Provides generic hook functions, allowing customizing by providing creating - * custom FPGA and ILCs. ILCs are stored in list, as a ILC class address ILCs + * custom FPGA and ILCs. ILCs are stored in list, as a @glos{ILC} class address ILCs * on single bus. */ class FPGACliApp : public CliApp { @@ -103,7 +103,7 @@ class FPGACliApp : public CliApp { std::shared_ptr getMPU(std::string name); /** - * Clears ILC buffers. Shall be called before new instructions are added to + * Clears @glos{ILC} buffers. Shall be called before new instructions are added to * the buffer. */ void clearILCs() { @@ -113,9 +113,9 @@ class FPGACliApp : public CliApp { } /** - * Run prepared ILC commands on ILC busses. + * Run prepared @glos{ILC} commands on @glos{ILC} busses. * - * @param timeout ILC timeout (in ms) + * @param timeout @glos{ILC} timeout (in ms) */ void runILCCommands(int32_t timeout) { for (auto ilcp : _ilcs) { @@ -128,14 +128,14 @@ class FPGACliApp : public CliApp { /** * Disable given ILC. * - * @param u ILC unit to disable. + * @param u @glos{ILC} unit to disable. */ void disableILC(ILCUnit u); /** * Enables given ILC. * - * @param u ILC unit to enable. + * @param u @glos{ILC} unit to enable. */ void enableILC(ILCUnit u); diff --git a/include/cRIO/PrintILC.h b/include/cRIO/PrintILC.h index 5abf2853..5a987135 100644 --- a/include/cRIO/PrintILC.h +++ b/include/cRIO/PrintILC.h @@ -31,9 +31,9 @@ namespace LSST { namespace cRIO { /** - * Virtual class printing out ILC responses to basic (implemented in all ILCs) + * Virtual class printing out @glos{ILC} responses to basic (implemented in all ILCs) * commands. Intended for command line applications. Adds functions to program - * ILC from provided Intel Hex File. + * @glos{ILC} from provided Intel Hex File. */ class PrintILC : public virtual ILC::ILCBusList { public: @@ -41,10 +41,10 @@ class PrintILC : public virtual ILC::ILCBusList { /** * Writes newly programmed application statistics and CRCs. Calculates - * fourth ILC function 100 argument - ModBus 16bit CRC from input + * fourth @glos{ILC} function 100 argument - ModBus 16bit CRC from input * arguments. * - * @param address ILC address + * @param address @glos{ILC} address * @param dataCRC data ModBus 16bit CRC. This is calculated fom * @param startAddress start memory address. Lowest start address from all function 103 calls. * @param dataLength data length (unshrunk). Highest start address from all function 103 + 256. @@ -54,17 +54,17 @@ class PrintILC : public virtual ILC::ILCBusList { void writeApplicationStats(uint8_t address, uint16_t dataCRC, uint16_t startAddress, uint16_t dataLength); /** - * Erase ILC application. + * Erase @glos{ILC} application. * - * @param address ILC address + * @param address @glos{ILC} address */ void eraseILCApplication(uint8_t address) { callFunction(address, 101, 500000); } /** - * Writes ILC firmware page. Calls functions 103. Please note that every + * Writes @glos{ILC} firmware page. Calls functions 103. Please note that every * fourth byte from hexfile is skipped. * - * @param address ILC address + * @param address @glos{ILC} address * @param startAddress firmware start address * @param length data length. Shall be 192 * @param data firmware data @@ -79,24 +79,24 @@ class PrintILC : public virtual ILC::ILCBusList { /** * Verifies firmware upload. * - * @param address ILC address + * @param address @glos{ILC} address */ void writeVerifyApplication(uint8_t address) { callFunction(address, 103, 500000); } /** * Programs ILC. Executes a sequence of commands as follow: * - * 1. put ILC into standby mode - * 2. put ILC into firmware update mode - * 3. clears ILC faults - * 4. erase ILC application - * 5. write ILC application - * 6. write ILC application statistics + * 1. put @glos{ILC} into standby mode + * 2. put @glos{ILC} into firmware update mode + * 3. clears @glos{ILC} faults + * 4. erase @glos{ILC} application + * 5. write @glos{ILC} application + * 6. write @glos{ILC} application statistics * 7. verify applications - * 8. put ILC into standby mode - * 9. put ILC into disabled mode + * 8. put @glos{ILC} into standby mode + * 9. put @glos{ILC} into disabled mode * - * @note Every fourth byte in input hex file is unused. ILC bootloader + * @note Every fourth byte in input hex file is unused. @glos{ILC} bootloader * expand input pages, adds 0x00 after every third byte. Control checksum * passed in function call 100 (Write Application Stats), addresses passed * in functions 100 and 102 (Write Application Page) are calculated and @@ -104,7 +104,7 @@ class PrintILC : public virtual ILC::ILCBusList { * written in hex file). * * @param fpga FPGA object - * @param address ILC address + * @param address @glos{ILC} address * @param hex Intel Hex file to load into ILC */ void programILC(FPGA *fpga, uint8_t address, IntelHex &hex); @@ -123,23 +123,23 @@ class PrintILC : public virtual ILC::ILCBusList { void processResetServer(uint8_t address) override; /** - * Called when Erase ILC Application (ILC function 101) is acknowledged. + * Called when Erase @glos{ILC} Application (@glos{ILC} function 101) is acknowledged. * - * @param address ILC address + * @param address @glos{ILC} address */ virtual void processEraseILCApplication(uint8_t address); /** * Called when Write Application Stats (ILC function 100) is acknowledged. * - * @param address ILC address + * @param address @glos{ILC} address */ virtual void processWriteApplicationStats(uint8_t address); /** * Called when Write Application Page (ILC function 102) is acknowledged. * - * @param address ILC address + * @param address @glos{ILC} address */ virtual void processWriteApplicationPage(uint8_t address); @@ -154,15 +154,16 @@ class PrintILC : public virtual ILC::ILCBusList { *
  • 0xFFFF application stats and application error
  • * * - * @param address ILC address - * @param status program ILC status + * @param address @glos{ILC} address + * @param status program @glos{ILC} status */ virtual void processVerifyUserApplication(uint8_t address, uint16_t status); /** - * Prints bus and ILC address. Used inside various responses to print out which ILC replied. + * Prints bus and @glos{ILC} address. Used inside various responses to + * print out which @glos{ILC} replied. * - * @param address ILC address + * @param address @glos{ILC} address */ virtual void printBusAddress(uint8_t address); diff --git a/include/cRIO/ThermalILC.h b/include/cRIO/ThermalILC.h index b21b2c30..c4a3f7db 100644 --- a/include/cRIO/ThermalILC.h +++ b/include/cRIO/ThermalILC.h @@ -125,7 +125,7 @@ class ThermalILC : public virtual ILC::ILCBusList { * @param address status returned from this ILC * @param status ILC status. See LTS-646 for details. * @param differentialTemperature differential temperature (degC) - * @param fanRPM measure fan RPM - 0 to 255 = 0 to 2550 RPM in 10 RPM increments + * @param fanRPM measure fan @glos{RPM} - 0 to 255 = 0 to 2550 @glos{RPM} in 10 @glos{RPM} increments * @param absoluteTemperature absolute temperature (degC) */ virtual void processThermalStatus(uint8_t address, uint8_t status, float differentialTemperature, diff --git a/include/cRIO/glossary.dox b/include/cRIO/glossary.dox new file mode 100644 index 00000000..95b3cfc0 --- /dev/null +++ b/include/cRIO/glossary.dox @@ -0,0 +1,54 @@ +/* + * Electromechanical and Pneumatic ILC functions. + * + * Developed for the Vera C. Rubin Observatory Telescope & Site Software Systems. + * This product includes software developed by the Vera C.Rubin Observatory Project + * (https://www.lsst.org). See the COPYRIGHT file at the top-level directory of + * this distribution for details of code ownership. + * + * 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 . + */ + +/** + * @page GLOSSARY cRIO Acronyms & Glossary + * + * Purpose of this glossary is to (mostly) expand the acronyms used in the + * text. Please consult M1M3 and M2 electronics and mechanical documentation to + * understand purpose and operation of the parts denoted by the acronyms. + * + * Rubin's documents LTS-646 (M1M3 ILC Coumminication) and LTS-346 (M2 ILC + * Communication) are particularly usefull to understand details of the + * ILC-cRIO communication. + * + * @section GLOSSARY_DCA DCA + * Digitally Controlled Amplifier + * + * @section GLOSSARY_ILC ILC + * Inner Loop Controller + * + * @section GLOSSARY_LVDT LVDT + * Linear Variable Differential Transformer + * + * @section GLOSSARY_FCU FCU + * Fan Coil Unit + * + * @section GLOSSARY_RPM RPM + * Revolutions Per Minute + * + * @section GLOSSARY_FPGA + * Field Programmable Gate Array + * + * @section GLOSSARY_FIFO + * First In First Out + */ diff --git a/src/ILC/ILCBusList.cpp b/src/ILC/ILCBusList.cpp index c8a9a47b..f314de48 100644 --- a/src/ILC/ILCBusList.cpp +++ b/src/ILC/ILCBusList.cpp @@ -28,12 +28,12 @@ using namespace ILC; ILCBusList::ILCBusList(uint8_t bus) : _bus(bus) { addResponse( - 17, + ILC_CMD::SERVER_ID, [this](Modbus::Parser parser) { uint8_t fnLen = parser.read(); if (fnLen < 12) { throw std::runtime_error(fmt::format( - "invalid ILC function 17 response length - except at least 12, got {}", fnLen)); + "invalid ILC function 17 response length - expect at least 12, got {}", fnLen)); } fnLen -= 12; @@ -52,7 +52,7 @@ ILCBusList::ILCBusList(uint8_t bus) : _bus(bus) { 145); addResponse( - 18, + ILC_CMD::SERVER_STATUS, [this](Modbus::Parser parser) { uint8_t mode = parser.read(); uint16_t status = parser.read(); @@ -64,7 +64,7 @@ ILCBusList::ILCBusList(uint8_t bus) : _bus(bus) { 146); addResponse( - 65, + ILC_CMD::CHANGE_MODE, [this](Modbus::Parser parser) { uint16_t mode = parser.read(); parser.checkCRC(); @@ -74,7 +74,7 @@ ILCBusList::ILCBusList(uint8_t bus) : _bus(bus) { 193); addResponse( - 72, + ILC_CMD::SET_TEMP_ADDRESS, [this](Modbus::Parser parser) { uint8_t newAddress = parser.read(); parser.checkCRC(); @@ -83,7 +83,7 @@ ILCBusList::ILCBusList(uint8_t bus) : _bus(bus) { 200); addResponse( - 107, + ILC_CMD::RESET_SERVER, [this](Modbus::Parser parser) { parser.checkCRC(); processResetServer(parser.address()); @@ -195,5 +195,5 @@ void ILCBusList::changeILCMode(uint8_t address, uint16_t mode) { } } catch (std::out_of_range &err) { } - callFunction(address, 65, timeout, mode); + callFunction(address, ILC_CMD::CHANGE_MODE, timeout, mode); } From 8aed213d06e436407e9fae91fe8654e48c3bb6da Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Mon, 11 Mar 2024 12:35:42 +0100 Subject: [PATCH 03/13] Enums for ILC commands codes --- include/cRIO/ElectromechanicalPneumaticILC.h | 103 ++++++++++++------ include/cRIO/PrintILC.h | 20 +++- include/cRIO/ThermalILC.h | 23 +++- .../cRIO/ElectromechanicalPneumaticILC.cpp | 21 ++-- src/LSST/cRIO/PrintILC.cpp | 14 ++- src/LSST/cRIO/ThermalILC.cpp | 8 +- 6 files changed, 128 insertions(+), 61 deletions(-) diff --git a/include/cRIO/ElectromechanicalPneumaticILC.h b/include/cRIO/ElectromechanicalPneumaticILC.h index 25221428..a09af95f 100644 --- a/include/cRIO/ElectromechanicalPneumaticILC.h +++ b/include/cRIO/ElectromechanicalPneumaticILC.h @@ -42,6 +42,9 @@ namespace cRIO { * end users which methods will be implemented. * * Replies received from ILCs shall be processed with ILC::processResponse method. + * + * @note When command numbers are referenced in the documentation, plese + * consult the relavant LTS - LTS-346 and LTS-646 - for details. */ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { public: @@ -63,7 +66,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @ingroup M1M3_hp * @ingroup M2 */ - void setStepperSteps(uint8_t address, int8_t steps) { callFunction(address, 66, 1800, steps); } + void setStepperSteps(uint8_t address, int8_t steps) { + callFunction(address, ILC_EM_CMD::SET_STEPPER_STEPS, 1800, steps); + } /** * Unicast Stepper motor @glos{ILC} Force [N] and Status Request. @glos{ILC} command @@ -77,71 +82,76 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @ingroup M1M3_hp * @ingroup M2 */ - void reportStepperForceStatus(uint8_t address) { callFunction(address, 67, 1800); } + void reportStepperForceStatus(uint8_t address) { + callFunction(address, ILC_EM_CMD::STEPPER_FORCE_STATUS, 1800); + } /** - * Unicast command to set a single force actuator force offset. + * Unicast to set @glos{DCA} Gain. @glos{ILC} Command code 73 (0x49). * * @param address @glos{ILC} address - * @param slewFlag @glos{DCA} booster valve - * @param primary primary actuator force offset (N) + * @param orimaryGain axial booster valve gain + * @param secondaryGain lateral booster valve gain + * + * @note no method is called for response, as this only sets the @glos{ILC} paremeters * * @ingroup M1M3_fa */ - void setSAAForceOffset(uint8_t address, bool slewFlag, float primary) { - callFunction(address, 75, 1800, static_cast(slewFlag ? 0xFF : 0x00), - Modbus::int24_t(primary * 1000)); + void setDCAGain(uint8_t address, float primaryGain, float secondaryGain) { + callFunction(address, ILC_EM_CMD::SET_DCA_GAIN, 40000, primaryGain, secondaryGain); } /** - * Unicast command to set a dual force actuator force offsets. + * Read @glos{DCA} Gain. @glos{ILC} command code 74 (0x4A). * * @param address @glos{ILC} address - * @param slewFlag @glos{DCA} booster valve - * @param primary primary actuator force offset (N) - * @param secondary secondary actuator force offset (N) + * + * @note processDCAGain method is called to process replies * * @ingroup M1M3_fa */ - void setDAAForceOffset(uint8_t address, bool slewFlag, float primary, float secondary) { - callFunction(address, 75, 1800, static_cast(slewFlag ? 0xFF : 0x00), - Modbus::int24_t(primary * 1000), Modbus::int24_t(secondary * 1000)); - } + void reportDCAGain(uint8_t address) { callFunction(address, ILC_EM_CMD::REPORT_DCA_GAIN, 2000); } /** - * Reports force actuator status. + * Unicast command to set a single force actuator force offset. * - * @params address @glos{ILC} address + * @param address @glos{ILC} address + * @param slewFlag @glos{DCA} booster valve + * @param primary primary actuator force offset (N) * * @ingroup M1M3_fa */ - void reportForceActuatorForceStatus(uint8_t address) { callFunction(address, 76, 1800); } + void setSAAForceOffset(uint8_t address, bool slewFlag, float primary) { + callFunction(address, ILC_EM_CMD::SET_FORCE_OFFSET, 1800, + static_cast(slewFlag ? 0xFF : 0x00), Modbus::int24_t(primary * 1000)); + } /** - * Unicast to set @glos{DCA} Gain. @glos{ILC} Command code 73 (0x49). + * Unicast command to set a dual force actuator force offsets. * * @param address @glos{ILC} address - * @param orimaryGain axial booster valve gain - * @param secondaryGain lateral booster valve gain - * - * @note no method is called for response, as this only sets the @glos{ILC} paremeters + * @param slewFlag @glos{DCA} booster valve + * @param primary primary actuator force offset (N) + * @param secondary secondary actuator force offset (N) * * @ingroup M1M3_fa */ - void setDCAGain(uint8_t address, float primaryGain, float secondaryGain) { - callFunction(address, 73, 40000, primaryGain, secondaryGain); + void setDAAForceOffset(uint8_t address, bool slewFlag, float primary, float secondary) { + callFunction(address, ILC_EM_CMD::SET_FORCE_OFFSET, 1800, + static_cast(slewFlag ? 0xFF : 0x00), Modbus::int24_t(primary * 1000), + Modbus::int24_t(secondary * 1000)); } /** - * Read @glos{DCA} Gain. @glos{ILC} command code 74 (0x4A). - * - * @param address @glos{ILC} address + * Reports force actuator status. * - * @note processDCAGain method is called to process replies + * @params address @glos{ILC} address * * @ingroup M1M3_fa */ - void reportDCAGain(uint8_t address) { callFunction(address, 74, 2000); } + void reportForceActuatorForceStatus(uint8_t address) { + callFunction(address, ILC_EM_CMD::REPORT_FA_FORCE_STATUS, 1800); + } /** * Unicast ADC Channel Offset and Sensitivity. @glos{ILC} command code 81 (0x51). @@ -157,7 +167,7 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @ingroup M2 */ void setOffsetAndSensitivity(uint8_t address, uint8_t channel, float offset, float sensitivity) { - callFunction(address, 81, 36500, channel, offset, sensitivity); + callFunction(address, ILC_EM_CMD::SET_OFFSET_AND_SENSITIVITY, 36500, channel, offset, sensitivity); } /** @@ -171,7 +181,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * @ingroup M1M3_hp * @ingroup M2 */ - void reportCalibrationData(uint8_t address) { callFunction(address, 110, 1800); } + void reportCalibrationData(uint8_t address) { + callFunction(address, ILC_EM_CMD::REPORT_CALIBRATION_DATA, 1800); + } /** * Read @glos{ILC} mezzanine pressure. @glos{ILC} command code 119 (0x77). @@ -180,7 +192,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * * @ingroup M1M3_hp */ - void reportMezzaninePressure(uint8_t address) { callFunction(address, 119, 1800); } + void reportMezzaninePressure(uint8_t address) { + callFunction(address, ILC_EM_CMD::REPORT_MEZZANINE_PRESSURE, 1800); + } /** * Unicast command to read hardpoint @glos{LVDT}. @glos{ILC} command 122 (0x7a). @@ -191,7 +205,9 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * * @ingroup M1M3_hp */ - void reportHardpointLVDT(uint8_t address) { callFunction(address, 122, 400); } + void reportHardpointLVDT(uint8_t address) { + callFunction(address, ILC_EM_CMD::REPORT_HARDPOINT_LVDT, 400); + } protected: /** @@ -288,6 +304,23 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { */ virtual void processMezzaninePressure(uint8_t address, float primaryPush, float primaryPull, float secondaryPush, float secondaryPull) = 0; + +private: + /** + * Electromechanical ILC commands. See LTS-346 and LTS-646 for details. + */ + enum ILC_EM_CMD { + SET_STEPPER_STEPS = 66, + STEPPER_FORCE_STATUS = 67, + SET_DCA_GAIN = 73, + REPORT_DCA_GAIN = 74, + SET_FORCE_OFFSET = 75, + REPORT_FA_FORCE_STATUS = 76, + SET_OFFSET_AND_SENSITIVITY = 81, + REPORT_CALIBRATION_DATA = 110, + REPORT_MEZZANINE_PRESSURE = 119, + REPORT_HARDPOINT_LVDT = 122 + }; }; } // namespace cRIO diff --git a/include/cRIO/PrintILC.h b/include/cRIO/PrintILC.h index 5a987135..68ebe395 100644 --- a/include/cRIO/PrintILC.h +++ b/include/cRIO/PrintILC.h @@ -58,7 +58,9 @@ class PrintILC : public virtual ILC::ILCBusList { * * @param address @glos{ILC} address */ - void eraseILCApplication(uint8_t address) { callFunction(address, 101, 500000); } + void eraseILCApplication(uint8_t address) { + callFunction(address, ILC_CLI_CMD::ERASE_APPLICATION, 500000); + } /** * Writes @glos{ILC} firmware page. Calls functions 103. Please note that every @@ -73,7 +75,7 @@ class PrintILC : public virtual ILC::ILCBusList { */ void writeApplicationPage(uint8_t address, uint16_t startAddress, uint16_t length, std::vector data) { - callFunction(address, 102, 500000, startAddress, length, data); + callFunction(address, ILC_CLI_CMD::WRITE_APPLICATION_PAGE, 500000, startAddress, length, data); } /** @@ -81,7 +83,9 @@ class PrintILC : public virtual ILC::ILCBusList { * * @param address @glos{ILC} address */ - void writeVerifyApplication(uint8_t address) { callFunction(address, 103, 500000); } + void writeVerifyApplication(uint8_t address) { + callFunction(address, ILC_CLI_CMD::WRITE_VERIFY_APPLICATION, 500000); + } /** * Programs ILC. Executes a sequence of commands as follow: @@ -170,6 +174,16 @@ class PrintILC : public virtual ILC::ILCBusList { virtual void printSepline(); private: + /** + * Please consult LTS-646 for details about the ILC commands. + */ + enum ILC_CLI_CMD { + WRITE_APPLICATION_STATS = 100, + ERASE_APPLICATION = 101, + WRITE_APPLICATION_PAGE = 102, + WRITE_VERIFY_APPLICATION = 103 + }; + int _printout; uint8_t _lastAddress; ModbusBuffer::CRC _crc; diff --git a/include/cRIO/ThermalILC.h b/include/cRIO/ThermalILC.h index c4a3f7db..e821d65a 100644 --- a/include/cRIO/ThermalILC.h +++ b/include/cRIO/ThermalILC.h @@ -80,7 +80,7 @@ class ThermalILC : public virtual ILC::ILCBusList { * @param fanRPM commanded fan RPM values (0-2550) */ void setThermalDemand(uint8_t address, uint8_t heaterPWM, uint8_t fanRPM) { - callFunction(address, 88, 500, heaterPWM, fanRPM); + callFunction(address, ILC_THERMAL_CMD::SET_THERMAL_DEMAND, 500, heaterPWM, fanRPM); } /** @@ -88,7 +88,9 @@ class ThermalILC : public virtual ILC::ILCBusList { * * @param address ILC address to query. */ - void reportThermalStatus(uint8_t address) { callFunction(address, 89, 300); } + void reportThermalStatus(uint8_t address) { + callFunction(address, ILC_THERMAL_CMD::REPORT_THERMAL_STATUS, 300); + } /** * Set new re-heater gains. @@ -98,7 +100,7 @@ class ThermalILC : public virtual ILC::ILCBusList { * @param integralGain Commanded integral gain */ void setReHeaterGains(uint8_t address, float proportionalGain, float integralGain) { - callFunction(address, 92, 500000, proportionalGain, integralGain); + callFunction(address, ILC_THERMAL_CMD::SET_REHEATER_GAINS, 500000, proportionalGain, integralGain); } /** @@ -108,7 +110,9 @@ class ThermalILC : public virtual ILC::ILCBusList { * * @note processThermalStatus method is called for replies */ - void reportReHeaterGains(uint8_t address) { callFunction(address, 93, 300); } + void reportReHeaterGains(uint8_t address) { + callFunction(address, ILC_THERMAL_CMD::REPORT_REHEATER_GAINS, 300); + } /** * Broadcast heater PWM and fan RPM. ILC command code 88 (0x58). @@ -139,6 +143,17 @@ class ThermalILC : public virtual ILC::ILCBusList { * @param integralGain Re-Heater integral gain */ virtual void processReHeaterGains(uint8_t address, float proportionalGain, float integralGain) = 0; + +private: + /** + * Thermal ILC command numbers. Please consult LTS-646 for details. + */ + enum ILC_THERMAL_CMD { + SET_THERMAL_DEMAND = 88, + REPORT_THERMAL_STATUS = 89, + SET_REHEATER_GAINS = 92, + REPORT_REHEATER_GAINS = 93 + }; }; } // namespace cRIO diff --git a/src/LSST/cRIO/ElectromechanicalPneumaticILC.cpp b/src/LSST/cRIO/ElectromechanicalPneumaticILC.cpp index 8b4e8017..e2a7502c 100644 --- a/src/LSST/cRIO/ElectromechanicalPneumaticILC.cpp +++ b/src/LSST/cRIO/ElectromechanicalPneumaticILC.cpp @@ -99,25 +99,26 @@ ElectromechanicalPneumaticILC::ElectromechanicalPneumaticILC(uint8_t bus) : ILC: processMezzaninePressure(parser.address(), primaryPush, primaryPull, secondaryPush, secondaryPull); }; - addResponse(66, stepperForceStatus, 194); + addResponse(ILC_EM_CMD::SET_STEPPER_STEPS, stepperForceStatus, 194); - addResponse(67, stepperForceStatus, 195); + addResponse(ILC_EM_CMD::STEPPER_FORCE_STATUS, stepperForceStatus, 195); addResponse( - 73, [this](Modbus::Parser parser) { parser.checkCRC(); }, 201); + ILC_EM_CMD::SET_DCA_GAIN, [this](Modbus::Parser parser) { parser.checkCRC(); }, 201); - addResponse(74, dcaGain, 202); + addResponse(ILC_EM_CMD::REPORT_DCA_GAIN, dcaGain, 202); - addResponse(75, forceActuatorForceStatus, 210); + addResponse(ILC_EM_CMD::SET_FORCE_OFFSET, forceActuatorForceStatus, 210); - addResponse(76, forceActuatorForceStatus, 220); + addResponse(ILC_EM_CMD::REPORT_FA_FORCE_STATUS, forceActuatorForceStatus, 220); addResponse( - 81, [this](Modbus::Parser parser) { parser.checkCRC(); }, 235); + ILC_EM_CMD::SET_OFFSET_AND_SENSITIVITY, [this](Modbus::Parser parser) { parser.checkCRC(); }, + 235); - addResponse(110, calibrationData, 238); + addResponse(ILC_EM_CMD::REPORT_CALIBRATION_DATA, calibrationData, 238); - addResponse(119, pressureData, 247); + addResponse(ILC_EM_CMD::REPORT_MEZZANINE_PRESSURE, pressureData, 247); - addResponse(122, hardpointLVDT, 250); + addResponse(ILC_EM_CMD::REPORT_HARDPOINT_LVDT, hardpointLVDT, 250); } diff --git a/src/LSST/cRIO/PrintILC.cpp b/src/LSST/cRIO/PrintILC.cpp index 914e7e0e..c2787e12 100644 --- a/src/LSST/cRIO/PrintILC.cpp +++ b/src/LSST/cRIO/PrintILC.cpp @@ -33,16 +33,19 @@ using namespace LSST::cRIO; PrintILC::PrintILC(uint8_t bus) : ILCBusList(bus), _printout(0), _lastAddress(0) { addResponse( - 100, [this](Modbus::Parser parser) { processWriteApplicationStats(parser.address()); }, 228); + ILC_CLI_CMD::WRITE_APPLICATION_STATS, + [this](Modbus::Parser parser) { processWriteApplicationStats(parser.address()); }, 228); addResponse( - 101, [this](Modbus::Parser parser) { processEraseILCApplication(parser.address()); }, 229); + ILC_CLI_CMD::ERASE_APPLICATION, + [this](Modbus::Parser parser) { processEraseILCApplication(parser.address()); }, 229); addResponse( - 102, [this](Modbus::Parser parser) { processWriteApplicationPage(parser.address()); }, 238); + ILC_CLI_CMD::WRITE_APPLICATION_PAGE, + [this](Modbus::Parser parser) { processWriteApplicationPage(parser.address()); }, 238); addResponse( - 103, + ILC_CLI_CMD::WRITE_VERIFY_APPLICATION, [this](Modbus::Parser parser) { uint16_t status = parser.read(); processVerifyUserApplication(parser.address(), status); @@ -66,7 +69,8 @@ void PrintILC::writeApplicationStats(uint8_t address, uint16_t dataCRC, uint16_t buf.writeBuffer(reinterpret_cast(&v), 2); buf.write(0); - callFunction(address, 100, 500000, dataCRC, startAddress, dataLength, buf.getCalcCrc()); + callFunction(address, ILC_CLI_CMD::WRITE_APPLICATION_STATS, 500000, dataCRC, startAddress, dataLength, + buf.getCalcCrc()); } void PrintILC::programILC(FPGA *fpga, uint8_t address, IntelHex &hex) { diff --git a/src/LSST/cRIO/ThermalILC.cpp b/src/LSST/cRIO/ThermalILC.cpp index 6dc98e32..5ceaad26 100644 --- a/src/LSST/cRIO/ThermalILC.cpp +++ b/src/LSST/cRIO/ThermalILC.cpp @@ -41,14 +41,14 @@ ThermalILC::ThermalILC(uint8_t bus) : ILC::ILCBusList(bus) { processReHeaterGains(parser.address(), proportionalGain, integralGain); }; - addResponse(88, thermalStatus, 216); + addResponse(ILC_THERMAL_CMD::SET_THERMAL_DEMAND, thermalStatus, 216); - addResponse(89, thermalStatus, 217); + addResponse(ILC_THERMAL_CMD::REPORT_THERMAL_STATUS, thermalStatus, 217); addResponse( - 92, [this](Modbus::Parser parser) { parser.checkCRC(); }, 220); + ILC_THERMAL_CMD::SET_REHEATER_GAINS, [this](Modbus::Parser parser) { parser.checkCRC(); }, 220); - addResponse(93, reheaterGains, 221); + addResponse(ILC_THERMAL_CMD::REPORT_REHEATER_GAINS, reheaterGains, 221); } std::vector ThermalILC::getStatusString(uint16_t status) { From 0a4c10bf5c8f08e74f87c6373a97fada2a34ca94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Kub=C3=A1nek?= Date: Thu, 21 Mar 2024 15:35:56 +0000 Subject: [PATCH 04/13] Public command numbers, support for saving calibration values --- include/ILC/ILCBusList.h | 24 +++++++------- include/cRIO/ElectromechanicalPneumaticILC.h | 33 ++++++++++---------- include/cRIO/PrintILC.h | 20 ++++++------ include/cRIO/ThermalILC.h | 21 ++++++------- src/ILC/ILCBusList.cpp | 10 +++--- src/LSST/cRIO/FPGACliApp.cpp | 9 ++++-- src/LSST/cRIO/PrintILC.cpp | 8 ++--- 7 files changed, 63 insertions(+), 62 deletions(-) diff --git a/include/ILC/ILCBusList.h b/include/ILC/ILCBusList.h index 0487a0c2..ce9e8f6c 100644 --- a/include/ILC/ILCBusList.h +++ b/include/ILC/ILCBusList.h @@ -29,7 +29,7 @@ namespace ILC { -enum Mode { Standby = 0, Disabled = 1, Enabled = 2, FirmwareUpdate = 3, Fault = 4, ClearFaults = 5 }; +enum Mode { Standby = 0, Disabled = 1, Enabled = 2, Bootloader = 3, Fault = 4, ClearFaults = 5 }; /** * Handles basic @glos{ILC} communication. Provides methods to run @glos{ILC} @@ -114,6 +114,17 @@ class ILCBusList : public Modbus::BusList { */ void resetServer(uint8_t address) { callFunction(address, ILC_CMD::RESET_SERVER, 86840); } + /** + * ILC commands numbers. See LTS-346 and LTS-646 for details. + */ + enum ILC_CMD { + SERVER_ID = 17, + SERVER_STATUS = 18, + CHANGE_MODE = 65, + SET_TEMP_ADDRESS = 72, + RESET_SERVER = 107 + }; + protected: /** * Call broadcast function. @@ -315,17 +326,6 @@ class ILCBusList : public Modbus::BusList { std::map _lastMode; ///< last know @glos{ILC} mode uint8_t _broadcastCounter = 0; - - /** - * ILC commands numbers. See LTS-346 and LTS-646 for details. - */ - enum ILC_CMD { - SERVER_ID = 17, - SERVER_STATUS = 18, - CHANGE_MODE = 65, - SET_TEMP_ADDRESS = 72, - RESET_SERVER = 107 - }; }; } // namespace ILC diff --git a/include/cRIO/ElectromechanicalPneumaticILC.h b/include/cRIO/ElectromechanicalPneumaticILC.h index a09af95f..621db6f1 100644 --- a/include/cRIO/ElectromechanicalPneumaticILC.h +++ b/include/cRIO/ElectromechanicalPneumaticILC.h @@ -55,6 +55,22 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { */ ElectromechanicalPneumaticILC(uint8_t bus); + /** + * Electromechanical ILC commands. See LTS-346 and LTS-646 for details. + */ + enum ILC_EM_CMD { + SET_STEPPER_STEPS = 66, + STEPPER_FORCE_STATUS = 67, + SET_DCA_GAIN = 73, + REPORT_DCA_GAIN = 74, + SET_FORCE_OFFSET = 75, + REPORT_FA_FORCE_STATUS = 76, + SET_OFFSET_AND_SENSITIVITY = 81, + REPORT_CALIBRATION_DATA = 110, + REPORT_MEZZANINE_PRESSURE = 119, + REPORT_HARDPOINT_LVDT = 122 + }; + /** * Unicast command to command stepper motor moves. * @@ -304,23 +320,6 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { */ virtual void processMezzaninePressure(uint8_t address, float primaryPush, float primaryPull, float secondaryPush, float secondaryPull) = 0; - -private: - /** - * Electromechanical ILC commands. See LTS-346 and LTS-646 for details. - */ - enum ILC_EM_CMD { - SET_STEPPER_STEPS = 66, - STEPPER_FORCE_STATUS = 67, - SET_DCA_GAIN = 73, - REPORT_DCA_GAIN = 74, - SET_FORCE_OFFSET = 75, - REPORT_FA_FORCE_STATUS = 76, - SET_OFFSET_AND_SENSITIVITY = 81, - REPORT_CALIBRATION_DATA = 110, - REPORT_MEZZANINE_PRESSURE = 119, - REPORT_HARDPOINT_LVDT = 122 - }; }; } // namespace cRIO diff --git a/include/cRIO/PrintILC.h b/include/cRIO/PrintILC.h index 68ebe395..f2ad1984 100644 --- a/include/cRIO/PrintILC.h +++ b/include/cRIO/PrintILC.h @@ -113,6 +113,16 @@ class PrintILC : public virtual ILC::ILCBusList { */ void programILC(FPGA *fpga, uint8_t address, IntelHex &hex); + /** + * Please consult LTS-646 for details about the ILC commands. + */ + enum ILC_CLI_CMD { + WRITE_APPLICATION_STATS = 100, + ERASE_APPLICATION = 101, + WRITE_APPLICATION_PAGE = 102, + WRITE_VERIFY_APPLICATION = 103 + }; + protected: void processServerID(uint8_t address, uint64_t uniqueID, uint8_t ilcAppType, uint8_t networkNodeType, uint8_t ilcSelectedOptions, uint8_t networkNodeOptions, uint8_t majorRev, @@ -174,16 +184,6 @@ class PrintILC : public virtual ILC::ILCBusList { virtual void printSepline(); private: - /** - * Please consult LTS-646 for details about the ILC commands. - */ - enum ILC_CLI_CMD { - WRITE_APPLICATION_STATS = 100, - ERASE_APPLICATION = 101, - WRITE_APPLICATION_PAGE = 102, - WRITE_VERIFY_APPLICATION = 103 - }; - int _printout; uint8_t _lastAddress; ModbusBuffer::CRC _crc; diff --git a/include/cRIO/ThermalILC.h b/include/cRIO/ThermalILC.h index e821d65a..d88d5b08 100644 --- a/include/cRIO/ThermalILC.h +++ b/include/cRIO/ThermalILC.h @@ -72,6 +72,16 @@ class ThermalILC : public virtual ILC::ILCBusList { FanBreakerOpen = 0x0008 }; + /** + * Thermal ILC command numbers. Please consult LTS-646 for details. + */ + enum ILC_THERMAL_CMD { + SET_THERMAL_DEMAND = 88, + REPORT_THERMAL_STATUS = 89, + SET_REHEATER_GAINS = 92, + REPORT_REHEATER_GAINS = 93 + }; + /** * Unicast heater PWM and fan RPM. ILC command code 88 (0x58) * @@ -143,17 +153,6 @@ class ThermalILC : public virtual ILC::ILCBusList { * @param integralGain Re-Heater integral gain */ virtual void processReHeaterGains(uint8_t address, float proportionalGain, float integralGain) = 0; - -private: - /** - * Thermal ILC command numbers. Please consult LTS-646 for details. - */ - enum ILC_THERMAL_CMD { - SET_THERMAL_DEMAND = 88, - REPORT_THERMAL_STATUS = 89, - SET_REHEATER_GAINS = 92, - REPORT_REHEATER_GAINS = 93 - }; }; } // namespace cRIO diff --git a/src/ILC/ILCBusList.cpp b/src/ILC/ILCBusList.cpp index f314de48..b9984af7 100644 --- a/src/ILC/ILCBusList.cpp +++ b/src/ILC/ILCBusList.cpp @@ -114,12 +114,12 @@ const char *ILCBusList::getModeStr(uint8_t mode) { return "Disabled"; case Mode::Enabled: return "Enabled"; - case Mode::FirmwareUpdate: - return "Firmware Updade"; + case Mode::Bootloader: + return "Bootloader"; case Mode::Fault: return "Fault"; default: - return "unknow"; + return "unknown"; } } @@ -189,8 +189,8 @@ std::vector ILCBusList::getFaultString(uint16_t fault) { void ILCBusList::changeILCMode(uint8_t address, uint16_t mode) { uint32_t timeout = 335; try { - if ((getLastMode(address) == Mode::Standby && mode == Mode::FirmwareUpdate) || - (getLastMode(address) == Mode::FirmwareUpdate && mode == Mode::Standby)) { + if ((getLastMode(address) == Mode::Standby && mode == Mode::Bootloader) || + (getLastMode(address) == Mode::Bootloader && mode == Mode::Standby)) { timeout = 100000; } } catch (std::out_of_range &err) { diff --git a/src/LSST/cRIO/FPGACliApp.cpp b/src/LSST/cRIO/FPGACliApp.cpp index 7c59a1d8..644e87e6 100644 --- a/src/LSST/cRIO/FPGACliApp.cpp +++ b/src/LSST/cRIO/FPGACliApp.cpp @@ -64,13 +64,16 @@ FPGACliApp::FPGACliApp(const char* name, const char* description) "status", [](ILCUnit u) { u.first->reportServerStatus(u.second); }, "Print ILC status"); addILCCommand( "standby", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::Standby); }, - "Change to standby mode"); + "Change ILC mode to standby"); addILCCommand( "disable", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::Disabled); }, - "Change to disabled mode"); + "Change ILC mode to disabled"); addILCCommand( "enable", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::Enabled); }, - "Change to enabled mode"); + "Change ILC mode to enabled"); + addILCCommand( + "bootloader", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::Bootloader); }, + "Change ILC mode to booloader"); addILCCommand( "clear-faults", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::ClearFaults); }, "Clear ILC faults"); diff --git a/src/LSST/cRIO/PrintILC.cpp b/src/LSST/cRIO/PrintILC.cpp index c2787e12..5363629c 100644 --- a/src/LSST/cRIO/PrintILC.cpp +++ b/src/LSST/cRIO/PrintILC.cpp @@ -99,8 +99,8 @@ void PrintILC::programILC(FPGA *fpga, uint8_t address, IntelHex &hex) { fpga->ilcCommands(*this, ILC_TIMEOUT); clear(); - if (getLastMode(address) != ILC::Mode::FirmwareUpdate) { - changeILCMode(address, ILC::Mode::FirmwareUpdate); + if (getLastMode(address) != ILC::Mode::Bootloader) { + changeILCMode(address, ILC::Mode::Bootloader); fpga->ilcCommands(*this, ILC_TIMEOUT); clear(); } @@ -111,8 +111,8 @@ void PrintILC::programILC(FPGA *fpga, uint8_t address, IntelHex &hex) { clear(); } - if (getLastMode(address) != ILC::Mode::FirmwareUpdate) { - throw std::runtime_error("Cannot transition to FirmwareUpdate mode"); + if (getLastMode(address) != ILC::Mode::Bootloader) { + throw std::runtime_error("Cannot transition to Bootloader mode"); } eraseILCApplication(address); From 26b3612f14c6096432842700ba2211b66dd5ce62 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Fri, 22 Mar 2024 17:38:36 +0100 Subject: [PATCH 05/13] CALIBRATION_LENGTH, changelog entry --- doc/version-history.rst | 1 + include/cRIO/ElectromechanicalPneumaticILC.h | 27 +++++++++++++------- src/LSST/cRIO/FPGACliApp.cpp | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/doc/version-history.rst b/doc/version-history.rst index 7841333b..2873afbe 100644 --- a/doc/version-history.rst +++ b/doc/version-history.rst @@ -7,6 +7,7 @@ v1.11.0 * Modbus::, ILC::ILCBusList classes. Communication code moved from ModbusBuffer to FPGA class. * Improved documentation. +* Public constants to explain meaning of the numbers v1.10.1 ------- diff --git a/include/cRIO/ElectromechanicalPneumaticILC.h b/include/cRIO/ElectromechanicalPneumaticILC.h index 621db6f1..d3736ddd 100644 --- a/include/cRIO/ElectromechanicalPneumaticILC.h +++ b/include/cRIO/ElectromechanicalPneumaticILC.h @@ -225,6 +225,12 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { callFunction(address, ILC_EM_CMD::REPORT_HARDPOINT_LVDT, 400); } + /** + * Number of values stored in calibration registers. The index is used for + * different acctuators/load cells connected to the ILC. + */ + static constexpr int CALIBRATION_LENGTH = 4; + protected: /** * Called when response from call to command unicast 66 (0x42) and 67 @@ -292,20 +298,23 @@ class ElectromechanicalPneumaticILC : public virtual ILC::ILCBusList { * Called when response from call to command 110 (0x6E) is read. * * @param address status returned from this ILC - * @param mainADCK[4] main ADC calibration Kn - * @param mainOffset[4] main sensor n offset - * @param mainSensitivity[4] main sensor n sensitivity - * @param backupADCK[4] backup ADC calibration Kn - * @param backupOffset[4] backup sensor n offset - * @param backupSensitivity[4] backup sensor n sensitivity + * @param mainADCK[CALIBRATION_LENGTH] main ADC calibration Kn + * @param mainOffset[CALIBRATION_LENGTH] main sensor n offset + * @param mainSensitivity[CALIBRATION_LENGTH] main sensor n sensitivity + * @param backupADCK[CALIBRATION_LENGTH] backup ADC calibration Kn + * @param backupOffset[CALIBRATION_LENGTH] backup sensor n offset + * @param backupSensitivity[CALIBRATION_LENGTH] backup sensor n sensitivity * * @ingroup M1M3_fa * @ingroup M1M3_hp * @ingroup M2 */ - virtual void processCalibrationData(uint8_t address, float mainADCK[4], float mainOffset[4], - float mainSensitivity[4], float backupADCK[4], float backupOffset[4], - float backupSensitivity[4]) = 0; + virtual void processCalibrationData(uint8_t address, float mainADCK[CALIBRATION_LENGTH], + float mainOffset[CALIBRATION_LENGTH], + float mainSensitivity[CALIBRATION_LENGTH], + float backupADCK[CALIBRATION_LENGTH], + float backupOffset[CALIBRATION_LENGTH], + float backupSensitivity[CALIBRATION_LENGTH]) = 0; /** * Called when response from call to command 119 is read. diff --git a/src/LSST/cRIO/FPGACliApp.cpp b/src/LSST/cRIO/FPGACliApp.cpp index 644e87e6..3e707cc6 100644 --- a/src/LSST/cRIO/FPGACliApp.cpp +++ b/src/LSST/cRIO/FPGACliApp.cpp @@ -73,7 +73,7 @@ FPGACliApp::FPGACliApp(const char* name, const char* description) "Change ILC mode to enabled"); addILCCommand( "bootloader", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::Bootloader); }, - "Change ILC mode to booloader"); + "Change ILC mode to booloader"); addILCCommand( "clear-faults", [](ILCUnit u) { u.first->changeILCMode(u.second, ILC::Mode::ClearFaults); }, "Clear ILC faults"); From 386ba8995c31ca11eba7f96780db9795e599c256 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Mon, 1 Apr 2024 16:34:39 +0200 Subject: [PATCH 06/13] Change MPU API --- include/Modbus/Buffer.h | 16 +- include/Modbus/BusList.h | 6 +- include/cRIO/MPU.h | 182 +++--------- src/LSST/cRIO/FPGA.cpp | 9 +- src/LSST/cRIO/MPU.cpp | 310 +++++--------------- tests/test_MPU.cpp | 601 ++++++++++++++++++--------------------- 6 files changed, 392 insertions(+), 732 deletions(-) diff --git a/include/Modbus/Buffer.h b/include/Modbus/Buffer.h index efec5a34..4ab12ed9 100644 --- a/include/Modbus/Buffer.h +++ b/include/Modbus/Buffer.h @@ -107,6 +107,13 @@ class Buffer : public std::vector { template void write(dt data); + template + inline void writeVector(std::vector data) { + for (auto d : data) { + write(d); + } + } + /** * Writes 24bit signed integer. * @@ -254,9 +261,12 @@ inline void Buffer::write(float data) { template <> inline void Buffer::write(std::vector data) { - for (auto d : data) { - write(d); - } + writeVector(data); +} + +template <> +inline void Buffer::write(std::vector data) { + writeVector(data); } } // namespace Modbus diff --git a/include/Modbus/BusList.h b/include/Modbus/BusList.h index 18a4da85..362bd03f 100644 --- a/include/Modbus/BusList.h +++ b/include/Modbus/BusList.h @@ -165,9 +165,9 @@ class BusList : public std::vector { * the @glos{FPGA}, which is responsible to pass it to the hardware * controller. * - * @param address ModBus/@glos{ILC} address @param func ModBus/@glos{ILC} - * function code @param timing function call timing in microseconds - * (1/10^-6 second) + * @param address ModBus/@glos{ILC} address + * @param func ModBus/@glos{ILC} function code + * @param timing function call timing in microseconds (1/10^-6 second) */ void callFunction(uint8_t address, uint8_t func, uint32_t timming) { emplace(end(), CommandRecord(Buffer(address, func), timming)); diff --git a/include/cRIO/MPU.h b/include/cRIO/MPU.h index 2a8c5386..607bf6cc 100644 --- a/include/cRIO/MPU.h +++ b/include/cRIO/MPU.h @@ -33,69 +33,29 @@ #include #include -#include +#include namespace LSST { namespace cRIO { -/** - * Modbus Processing Unit (MPU) commands. Please see - * https://github.com/lsst-ts/ts_M1M3Thermal/blob/develop/doc/version-history.rst - */ -namespace MPUCommands { -const static uint8_t WRITE = 1; -const static uint8_t READ_US = 2; -const static uint8_t READ_MS = 3; -const static uint8_t WAIT_US = 100; -const static uint8_t WAIT_MS = 101; -const static uint8_t IRQ = 240; -const static uint8_t TELEMETRY = 254; -const static uint8_t RESET = 255; -} // namespace MPUCommands - -typedef enum { WRITE, READ, IDLE } loop_state_t; - /** * The Modbus Processing Unit class. Prepares buffer with commands to send to * FPGA, read responses & telemetry values. * - * Values can be queried in a loop. Code then calls registered callback - * function everytime new data become available. For code to work that way, - * interrupt ( - * - * If FPGA is instructed to raise an interrupt during (most likely at the end - * of) buffer execution with the 240 opcode, + * Only a single command shall be send by the controlling computer. That's due + * to ModBus protocol, which doesn't include address of register returned - so + * any attempts to process multiple messages fail on the fact one doesn't know + * which registers were returned. */ -class MPU : public ModbusBuffer { +class MPU : public Modbus::BusList { public: /** - * Contruct MPU class. + * Construct MPU class. * * @param bus MPU bus number (internal FPGA identifier) - * @param mpu_address MPU ModBus address - */ - MPU(uint8_t bus, uint8_t mpu_address); - - void clearCommanded(); - - /** - * Reset bus. Clear all FIFOs. - */ - void resetBus(); - - /** - * Wait for given number of microseonds. - * - * @param us number of microseconds to wait - */ - void waitUs(uint16_t us); - - /** - * Wait for given number of milliseconds. - * - * @param ms number of milliseconds to wait + * @param node_address MPU ModBus address */ - void waitMs(uint16_t ms); + MPU(uint8_t bus, uint8_t node_address); /** * Returns bus number (internal FPGA identifier). @@ -104,102 +64,40 @@ class MPU : public ModbusBuffer { */ uint8_t getBus() { return _bus; } - virtual uint32_t getIrq() { return 1 << (10 + getBus()); } - - void setAddress(uint8_t address) { _mpu_address = address; } - - bool containsRead() { return _contains_read; } - - void writeEndOfFrame() override {} - void writeWaitForRx(uint32_t timeoutMicros) override {} - void writeRxEndFrame() override {} - - void readEndOfFrame() override {} - - void readInputStatus(uint16_t address, uint16_t count = 1, uint16_t timeout = 100); + /** + * Read input registers. + */ + void readInputStatus(uint16_t start_register_address, uint16_t count = 1, uint32_t timing = 100000); /** * Reads holding register(s). * - * @param address register address + * @param address register(s) staring address * @param count number of registers to read - * @param timeout timeout for register readout (in ms) + * @param timing timeout for register readout (in microseconds) */ - void readHoldingRegisters(uint16_t address, uint16_t count, uint16_t timeout = 100); + void readHoldingRegisters(uint16_t start_register_address, uint16_t count, uint32_t timing = 1000000); /** * Write single register. * - * @param address register address + * @param address register starting address * @param value register value - * @param timeout timeout (in ms) + * @param timing timeout (in microseconds) */ - void presetHoldingRegister(uint16_t address, uint16_t value, uint16_t timeout = 100); + void presetHoldingRegister(uint16_t register_address, uint16_t value, uint32_t timing = 100000); /** * Sets modbus holding registers. * * @param address register address * @param values register values - * @param count number of registers to write - * @param timeout timeout (in ms) - */ - void presetHoldingRegisters(uint16_t address, uint16_t *values, uint8_t count, uint16_t timeout = 100); - - /** - * Returns commands buffer. - * - * @return current command buffer - */ - std::vector getCommandVector(); - - /*** - * Called to set loop read timeout. - * - * @param callback function to call when new data are available - * @param timeout timeout. If data cannot be retrieved within timeout, - * callback function is called with timedout parameter set to true. - */ - void setLoopTimeOut(std::chrono::microseconds timeout) { _loop_timeout = timeout; } - - /*** - * Called to write commands to retrieve values needed in loopRead. + * @param timing timeout (in microseconds) */ - virtual void loopWrite() = 0; + void presetHoldingRegisters(uint16_t start_register_address, const std::vector &values, + uint32_t timing = 100000); /** - * Exception raised when read timeouts. - */ - class IRQTimeout : public std::runtime_error { - public: - IRQTimeout(std::vector _data); - std::vector data; - }; - - /*** - * Called to process data read in the loop. - * - * @param timeout - */ - virtual void loopRead(bool timedout) = 0; - - /** - * Runs command loop. This is a state machine, with state stored in - * _loopStatus variable. - * - * In WRITE state, it calls loopWrite to prepare commands for FPGA. - * - * In READ state, it waits for bus IRQ. If wait succeed (IRQ was triggered), - * it reads the output data into a buffer, process the acquired buffer, and - * calls callback (if set). - * - * @param FPGA fpga used to process MPU commands. - */ - bool runLoop(FPGA &fpga); - - loop_state_t getLoopState() { return _loop_state; } - - /*** * Returns cached input state. Input state shall be cached - queried with * readInputStatus method. * @@ -209,10 +107,9 @@ class MPU : public ModbusBuffer { * * @throws std::out_of_range if the address isn't cached */ + bool getInputStatus(uint16_t input_address); - bool getInputStatus(uint16_t address) { return _inputStatus.at(address); } - - /*** + /** * Returns register value. This access only cached values - register shall be first read * using readHoldingRegisters method. * @@ -222,37 +119,22 @@ class MPU : public ModbusBuffer { * * @throws std::runtime_error when register value isn't cached */ - uint16_t getRegister(uint16_t address) { - std::lock_guard lg(_registerMutex); - try { - return _registers.at(address); - } catch (std::out_of_range &e) { - throw std::runtime_error(fmt::format("Cannot retrieve holding register {}", address)); - } - } + uint16_t getRegister(uint16_t address); private: - void _pushTimeout(uint16_t timeout); - - std::vector _commands; uint8_t _bus; - uint8_t _mpu_address; - - std::mutex _registerMutex; + uint8_t _node_address; - bool _contains_read; + /** + * Address of register/input which will be read by the command + */ + uint16_t _commanded_address = 0; + uint16_t _commanded_length = 0; - std::list> _readInputStatus; - std::list _readRegisters; - std::list> _presetRegister; - std::list> _presetRegisters; + std::mutex _registerMutex; std::map _inputStatus; std::map _registers; - - std::chrono::microseconds _loop_timeout; - std::chrono::time_point _loop_next_read; - loop_state_t _loop_state; }; } // namespace cRIO diff --git a/src/LSST/cRIO/FPGA.cpp b/src/LSST/cRIO/FPGA.cpp index 35120310..46d88a15 100644 --- a/src/LSST/cRIO/FPGA.cpp +++ b/src/LSST/cRIO/FPGA.cpp @@ -41,7 +41,6 @@ FPGA::FPGA(fpgaType type) : SimpleFPGA(type) { _modbusSoftwareTrigger = 252; break; case M2: - _modbusSoftwareTrigger = 1; case VMS: _modbusSoftwareTrigger = 0; break; @@ -152,13 +151,7 @@ void FPGA::ilcCommands(ILC::ILCBusList &ilc, int32_t timeout) { void FPGA::mpuCommands(MPU &mpu, const std::chrono::duration &timeout) { writeMPUFIFO(mpu); - - if (mpu.containsRead()) { - std::this_thread::sleep_for(timeout); - readMPUFIFO(mpu); - } - - mpu.checkCommandedEmpty(); + readMPUFIFO(mpu); } } // namespace cRIO diff --git a/src/LSST/cRIO/MPU.cpp b/src/LSST/cRIO/MPU.cpp index 96e54e54..1802d9df 100644 --- a/src/LSST/cRIO/MPU.cpp +++ b/src/LSST/cRIO/MPU.cpp @@ -25,297 +25,127 @@ #include #include +#include using namespace LSST::cRIO; -MPU::IRQTimeout::IRQTimeout(std::vector _data) - : std::runtime_error(fmt::format("Din't receive IRQ, reseting MPU. Read data: " + - ModbusBuffer::hexDump(_data.data(), _data.size()))), - data(_data) {} - -MPU::MPU(uint8_t bus, uint8_t mpu_address) : _bus(bus), _mpu_address(mpu_address), _contains_read(false) { - _loop_state = loop_state_t::WRITE; - +MPU::MPU(uint8_t bus, uint8_t node_address) : _bus(bus), _node_address(node_address), _commanded_address(0) { addResponse( 2, - [this](uint8_t address) { - if (_readInputStatus.empty()) { + [this](Modbus::Parser parser) { + if (_commanded_address == 0 || _commanded_length == 0) { throw std::runtime_error("Empty read input status"); } - auto req = _readInputStatus.front(); - if (address != _mpu_address) { - throw std::runtime_error( - fmt::format("Invalid ModBus address {}, expected {}", address, _mpu_address)); - } - uint8_t len = read(); - if (len != ceil(req.second / 8.0)) { - throw std::runtime_error(fmt::format( - "Invalid length for inputs starting at {:04x} - received {}, expected {}", - req.first, len, req.second)); + if (parser.address() != _node_address) { + throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", + parser.address(), _node_address)); } - uint16_t start_address = req.first; - uint16_t end_address = start_address + req.second; + uint8_t len = parser.read(); uint8_t data = 0x00; - for (uint16_t a = start_address; a < end_address; a++) { - if ((a - start_address) % 8 == 0) { - data = read(); + if (_commanded_length / 8 + (_commanded_length % 8 == 0 ? 0 : 1) != len) { + throw std::runtime_error( + fmt::format("Invalid reply length - received {}, ceiling from {} / 8", len, + _commanded_length)); + } + for (uint16_t i = 0; i < _commanded_length; i++) { + if (i % 8 == 0) { + data = parser.read(); } - _inputStatus[a] = data & 0x01; + _inputStatus[_commanded_address + i] = data & 0x01; data >>= 1; } - _readInputStatus.pop_front(); - checkCRC(); + parser.checkCRC(); }, 0x82); addResponse( 3, - [this](uint8_t address) { - if (address != _mpu_address) { - throw std::runtime_error( - fmt::format("Invalid ModBus address {}, expected {}", address, _mpu_address)); + [this](Modbus::Parser parser) { + if (parser.address() != _node_address) { + throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", + parser.address(), _node_address)); } - uint8_t len = read(); - + uint8_t len = parser.read() / 2; { std::lock_guard lg(_registerMutex); - for (size_t i = 0; i < len; i += 2) { - if (_readRegisters.empty()) { - throw std::runtime_error("Too big response"); - } - uint16_t reg = _readRegisters.front(); - uint16_t val = read(); - _registers[reg] = val; - _readRegisters.pop_front(); + for (size_t i = 0; i < len; i++) { + uint16_t val = parser.read(); + _registers[_commanded_address + i] = val; } } - checkCRC(); + parser.checkCRC(); }, 0x83); addResponse( 6, - [this](uint8_t address) { - if (address != _mpu_address) { - throw std::runtime_error( - fmt::format("Invalid ModBus address {}, expected {}", address, _mpu_address)); + [this](Modbus::Parser parser) { + if (parser.address() != _node_address) { + throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", + parser.address(), _node_address)); } - uint16_t reg = read(); - uint16_t value = read(); - auto preset = _presetRegister.front(); - _presetRegister.pop_front(); - if (reg != preset.first) { + uint16_t reg = parser.read(); + uint16_t value = parser.read(); + if (reg != _commanded_address) { throw std::runtime_error( - fmt::format("Invalid register {:04x}, expected {:04x}", reg, preset.first)); + fmt::format("Invalid register {:04x}, expected {:04x}", reg, _commanded_address)); } - if (value != preset.second) { - throw std::runtime_error(fmt::format("Invalid value {} for address {:04x}, expected {}", - value, reg, preset.second)); + { + std::lock_guard lg(_registerMutex); + _registers[_commanded_address] = value; } - checkCRC(); + parser.checkCRC(); }, 0x86); addResponse( 16, - [this](uint8_t address) { - if (address != _mpu_address) { - throw std::runtime_error( - fmt::format("Invalid ModBus address {}, expected {}", address, _mpu_address)); - } - uint16_t reg = read(); - uint16_t len = read(); - auto preset = _presetRegisters.front(); - _presetRegisters.pop_front(); - if (reg != preset.first) { - throw std::runtime_error( - fmt::format("Invalid register {:04x}, expected {:04x}", reg, preset.first)); + [this](Modbus::Parser parser) { + if (parser.address() != _node_address) { + throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", + parser.address(), _node_address)); } - if (len != preset.second) { + uint16_t reg = parser.read(); + uint16_t len = parser.read(); + if (reg != _commanded_address) { throw std::runtime_error( - fmt::format("Invalid lenght {}, expected {}", len, preset.second)); + fmt::format("Invalid register {:04x}, expected {:04x}", reg, _commanded_address)); } - checkCRC(); + parser.checkCRC(); }, 0x90); } -void MPU::clearCommanded() { - clear(); - - _loop_state = loop_state_t::WRITE; - - _commands.clear(); - - _readInputStatus.clear(); - _readRegisters.clear(); - _presetRegister.clear(); - _presetRegisters.clear(); +void MPU::readInputStatus(uint16_t start_register_address, uint16_t count, uint32_t timing) { + callFunction(_node_address, 2, timing, start_register_address, count); + _commanded_address = start_register_address; + _commanded_length = count; } -void MPU::resetBus() { _commands.push_back(MPUCommands::RESET); } - -void MPU::waitUs(uint16_t us) { - _commands.push_back(MPUCommands::WAIT_US); - _pushTimeout(us); +void MPU::readHoldingRegisters(uint16_t start_register_address, uint16_t count, uint32_t timing) { + callFunction(_node_address, 3, timing, start_register_address, count); + _commanded_address = start_register_address; } -void MPU::waitMs(uint16_t ms) { - _commands.push_back(MPUCommands::WAIT_MS); - _pushTimeout(ms); +void MPU::presetHoldingRegister(uint16_t register_address, uint16_t value, uint32_t timing) { + callFunction(_node_address, 6, timing, register_address, value); + _commanded_address = register_address; } -void MPU::readInputStatus(uint16_t address, uint16_t count, uint16_t timeout) { - callFunction(_mpu_address, 2, 0, address, count); - - // write request - _commands.push_back(MPUCommands::WRITE); - _commands.push_back(getLength()); - for (auto b : getBufferVector()) { - _commands.push_back(b); - } - - // read response - _commands.push_back(MPUCommands::READ_MS); - _contains_read = true; - // extras: device address, function, length (all 1 byte), CRC (2 bytes) = 5 total - _commands.push_back(5 + ceil(count / 8.0)); - _pushTimeout(timeout); - - _readInputStatus.push_back(std::pair(address, count)); +void MPU::presetHoldingRegisters(uint16_t start_register_address, const std::vector &values, + uint32_t timing) { + callFunction(_node_address, 16, timing, start_register_address, static_cast(values.size()), + static_cast(values.size() * 2), values); + _commanded_address = start_register_address; } -void MPU::readHoldingRegisters(uint16_t address, uint16_t count, uint16_t timeout) { - clear(true); - - callFunction(_mpu_address, 3, 0, address, count); - - // write request - _commands.push_back(MPUCommands::WRITE); - _commands.push_back(getLength()); - for (auto b : getBufferVector()) { - _commands.push_back(b); - } - - // read response - _commands.push_back(MPUCommands::READ_MS); - _contains_read = true; - // extras: device address, function, length (all 1 byte), CRC (2 bytes) = 5 total - _commands.push_back(5 + count * 2); - _pushTimeout(timeout); +bool MPU::getInputStatus(uint16_t input_address) { return _inputStatus.at(input_address); } - for (uint16_t add = address; add < address + count; add++) { - _readRegisters.push_back(add); +uint16_t MPU::getRegister(uint16_t address) { + std::lock_guard lg(_registerMutex); + try { + return _registers.at(address); + } catch (std::out_of_range &e) { + throw std::runtime_error(fmt::format("Cannot retrieve holding register {}", address)); } } - -void MPU::presetHoldingRegister(uint16_t address, uint16_t value, uint16_t timeout) { - write(_mpu_address); - write(6); - write(address); - write(value); - - writeCRC(); - writeEndOfFrame(); - writeWaitForRx(0); - - pushCommanded(_mpu_address, 6); - - // write request - _commands.push_back(MPUCommands::WRITE); - _commands.push_back(getLength()); - for (auto b : getBufferVector()) { - _commands.push_back(b); - } - - // read response - _commands.push_back(MPUCommands::READ_MS); - _contains_read = true; - // extras: device address, function (all 1 byte), address, number of registers written, CRC (2 bytes) - _commands.push_back(8); - _pushTimeout(timeout); - - _presetRegister.push_back(std::pair(address, value)); -} - -void MPU::presetHoldingRegisters(uint16_t address, uint16_t* values, uint8_t count, uint16_t timeout) { - write(_mpu_address); - write(16); - write(address); - write(count); - write(count * 2); - - for (uint8_t i = 0; i < count; i++) { - write(values[i]); - } - - writeCRC(); - - pushCommanded(_mpu_address, 16); - - // write request - _commands.push_back(MPUCommands::WRITE); - _commands.push_back(getLength()); - for (auto b : getBufferVector()) { - _commands.push_back(b); - } - - // read response - _commands.push_back(MPUCommands::READ_MS); - // extras: device address, function (all 1 byte), address, number of registers written, CRC (2 bytes) - _commands.push_back(8); - _pushTimeout(timeout); - - _presetRegisters.push_back(std::pair(address, count)); -} - -std::vector MPU::getCommandVector() { - _commands.push_back(MPUCommands::IRQ); - return _commands; -} - -bool MPU::runLoop(FPGA& fpga) { - switch (_loop_state) { - case loop_state_t::WRITE: - _loop_next_read = std::chrono::steady_clock::now() + _loop_timeout; - loopWrite(); - fpga.writeMPUFIFO(*this); - _loop_state = loop_state_t::READ; - break; - case loop_state_t::READ: { - bool timedout; - fpga.waitOnIrqs(getIrq(), 0, timedout); - if (timedout) { - // there is still time to retrieve data, wait for IRQ - if (_loop_next_read > std::chrono::steady_clock::now()) { - return false; - } - auto data = fpga.readMPUFIFO(*this); - clearCommanded(); - resetBus(); - fpga.writeMPUFIFO(*this); - fpga.waitOnIrqs(getIrq(), 100, timedout); - fpga.ackIrqs(getIrq()); - throw IRQTimeout(data); - } else { - fpga.ackIrqs(getIrq()); - fpga.readMPUFIFO(*this); - } - - loopRead(timedout); - _loop_state = loop_state_t::IDLE; - } break; - case loop_state_t::IDLE: - if (_loop_next_read > std::chrono::steady_clock::now()) { - return false; - } - _loop_state = loop_state_t::WRITE; - return true; - } - return false; -} - -void MPU::_pushTimeout(uint16_t timeout) { - _commands.push_back(timeout >> 8 & 0xff); - _commands.push_back(timeout & 0xff); -} diff --git a/tests/test_MPU.cpp b/tests/test_MPU.cpp index 6a736486..2f4770d5 100644 --- a/tests/test_MPU.cpp +++ b/tests/test_MPU.cpp @@ -29,295 +29,261 @@ using namespace LSST::cRIO; class TestMPU : public MPU { public: TestMPU(uint8_t bus, uint8_t mpu_address) : MPU(bus, mpu_address) {} - - void loopWrite() override {} - void loopRead(bool timedout) override {} }; TEST_CASE("Test MPU read input status", "[MPU]") { TestMPU mpu(1, 0x11); mpu.readInputStatus(0x00C4, 0x0016, 108); - auto commands = mpu.getCommandVector(); + CHECK(mpu.size() == 1); - REQUIRE(commands.size() == 15); + auto commands = mpu[0].buffer; - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 8); - REQUIRE(commands[2] == 0x11); - REQUIRE(commands[3] == 0x02); - REQUIRE(commands[4] == 0x00); - REQUIRE(commands[5] == 0xC4); - REQUIRE(commands[6] == 0x00); - REQUIRE(commands[7] == 0x16); - REQUIRE(commands[8] == 0xBA); - REQUIRE(commands[9] == 0xA9); - REQUIRE(commands[10] == MPUCommands::READ_MS); - REQUIRE(commands[11] == 8); - REQUIRE(commands[12] == 0); - REQUIRE(commands[13] == 108); - REQUIRE(commands[14] == MPUCommands::IRQ); + CHECK(commands.size() == 8); - std::vector res = {0x11, 0x02, 0x03, 0xAC, 0xDB, 0x35, 0x20, 0x18}; + CHECK(commands[0] == 0x11); + CHECK(commands[1] == 0x02); + CHECK(commands[2] == 0x00); + CHECK(commands[3] == 0xC4); + CHECK(commands[4] == 0x00); + CHECK(commands[5] == 0x16); + CHECK(commands[6] == 0xBA); + CHECK(commands[7] == 0xA9); - REQUIRE_NOTHROW(mpu.processResponse(res.data(), res.size())); + CHECK_NOTHROW(mpu.parse(std::vector({0x11, 0x02, 0x03, 0xAC, 0xDB, 0x35, 0x20, 0x18}))); // input status is not 10001 offseted - REQUIRE_THROWS(mpu.getInputStatus(195)); + CHECK_THROWS(mpu.getInputStatus(195)); // first byte - REQUIRE(mpu.getInputStatus(196) == false); - REQUIRE(mpu.getInputStatus(197) == false); - REQUIRE(mpu.getInputStatus(198) == true); - REQUIRE(mpu.getInputStatus(199) == true); + CHECK(mpu.getInputStatus(196) == false); + CHECK(mpu.getInputStatus(197) == false); + CHECK(mpu.getInputStatus(198) == true); + CHECK(mpu.getInputStatus(199) == true); - REQUIRE(mpu.getInputStatus(200) == false); - REQUIRE(mpu.getInputStatus(201) == true); - REQUIRE(mpu.getInputStatus(202) == false); - REQUIRE(mpu.getInputStatus(203) == true); + CHECK(mpu.getInputStatus(200) == false); + CHECK(mpu.getInputStatus(201) == true); + CHECK(mpu.getInputStatus(202) == false); + CHECK(mpu.getInputStatus(203) == true); // second byte - REQUIRE(mpu.getInputStatus(204) == true); - REQUIRE(mpu.getInputStatus(205) == true); - REQUIRE(mpu.getInputStatus(206) == false); - REQUIRE(mpu.getInputStatus(207) == true); + CHECK(mpu.getInputStatus(204) == true); + CHECK(mpu.getInputStatus(205) == true); + CHECK(mpu.getInputStatus(206) == false); + CHECK(mpu.getInputStatus(207) == true); - REQUIRE(mpu.getInputStatus(208) == true); - REQUIRE(mpu.getInputStatus(209) == false); - REQUIRE(mpu.getInputStatus(210) == true); - REQUIRE(mpu.getInputStatus(211) == true); + CHECK(mpu.getInputStatus(208) == true); + CHECK(mpu.getInputStatus(209) == false); + CHECK(mpu.getInputStatus(210) == true); + CHECK(mpu.getInputStatus(211) == true); // third byte - REQUIRE(mpu.getInputStatus(212) == true); - REQUIRE(mpu.getInputStatus(213) == false); - REQUIRE(mpu.getInputStatus(214) == true); - REQUIRE(mpu.getInputStatus(215) == false); - - REQUIRE(mpu.getInputStatus(216) == true); - REQUIRE(mpu.getInputStatus(217) == true); - REQUIRE_THROWS(mpu.getInputStatus(218)); - REQUIRE_THROWS(mpu.getInputStatus(219)); + CHECK(mpu.getInputStatus(212) == true); + CHECK(mpu.getInputStatus(213) == false); + CHECK(mpu.getInputStatus(214) == true); + CHECK(mpu.getInputStatus(215) == false); + + CHECK(mpu.getInputStatus(216) == true); + CHECK(mpu.getInputStatus(217) == true); + CHECK_THROWS(mpu.getInputStatus(218)); + CHECK_THROWS(mpu.getInputStatus(219)); } TEST_CASE("Test MPU read holding registers", "[MPU]") { TestMPU mpu(1, 12); mpu.readHoldingRegisters(3, 10, 101); - auto commands = mpu.getCommandVector(); - - REQUIRE(commands.size() == 15); - - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 8); - REQUIRE(commands[2] == 12); - REQUIRE(commands[3] == 3); - REQUIRE(commands[4] == 0); - REQUIRE(commands[5] == 3); - REQUIRE(commands[6] == 0); - REQUIRE(commands[7] == 10); - REQUIRE(commands[8] == 0x34); - REQUIRE(commands[9] == 0xD0); - REQUIRE(commands[10] == MPUCommands::READ_MS); - REQUIRE(commands[11] == 25); - REQUIRE(commands[12] == 0); - REQUIRE(commands[13] == 101); - REQUIRE(commands[14] == MPUCommands::IRQ); - - std::vector res = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; + CHECK(mpu.size() == 1); + + auto commands = mpu[0].buffer; + + CHECK(commands.size() == 8); - REQUIRE_NOTHROW(mpu.processResponse(res.data(), res.size())); + CHECK(commands[0] == 12); + CHECK(commands[1] == 3); + CHECK(commands[2] == 0); + CHECK(commands[3] == 3); + CHECK(commands[4] == 0); + CHECK(commands[5] == 10); + CHECK(commands[6] == 0x34); + CHECK(commands[7] == 0xD0); - REQUIRE_THROWS(mpu.getRegister(1)); - REQUIRE_THROWS(mpu.getRegister(2)); + std::vector res = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; - REQUIRE(mpu.getRegister(3) == 0x0102); - REQUIRE(mpu.getRegister(4) == 0x0304); - REQUIRE(mpu.getRegister(5) == 0x0506); - REQUIRE(mpu.getRegister(6) == 0x0708); - REQUIRE(mpu.getRegister(7) == 0x090a); - REQUIRE(mpu.getRegister(8) == 0x0b0c); - REQUIRE(mpu.getRegister(9) == 0x0d0e); - REQUIRE(mpu.getRegister(10) == 0x0f10); - REQUIRE(mpu.getRegister(11) == 0x1112); - REQUIRE(mpu.getRegister(12) == 0x1314); + CHECK_NOTHROW(mpu.parse(res)); - REQUIRE_THROWS(mpu.getRegister(13)); - REQUIRE_THROWS(mpu.getRegister(14)); + CHECK_THROWS(mpu.getRegister(1)); + CHECK_THROWS(mpu.getRegister(2)); + + CHECK(mpu.getRegister(3) == 0x0102); + CHECK(mpu.getRegister(4) == 0x0304); + CHECK(mpu.getRegister(5) == 0x0506); + CHECK(mpu.getRegister(6) == 0x0708); + CHECK(mpu.getRegister(7) == 0x090a); + CHECK(mpu.getRegister(8) == 0x0b0c); + CHECK(mpu.getRegister(9) == 0x0d0e); + CHECK(mpu.getRegister(10) == 0x0f10); + CHECK(mpu.getRegister(11) == 0x1112); + CHECK(mpu.getRegister(12) == 0x1314); + + CHECK_THROWS(mpu.getRegister(13)); + CHECK_THROWS(mpu.getRegister(14)); } TEST_CASE("Test MPU reading multiple registers - failed response", "[MPU]") { TestMPU mpu(1, 12); mpu.readHoldingRegisters(3, 10, 101); + + CHECK(mpu.size() == 1); + + auto commands = mpu[0].buffer; + + CHECK(commands.size() == 8); + + CHECK(commands[0] == 12); + CHECK(commands[1] == 3); + CHECK(commands[2] == 0); + CHECK(commands[3] == 3); + CHECK(commands[4] == 0); + CHECK(commands[5] == 10); + CHECK(commands[6] == 0x34); + CHECK(commands[7] == 0xD0); + + std::vector res1 = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; + + CHECK_NOTHROW(mpu.parse(res1)); + + CHECK_THROWS(mpu.getRegister(1)); + CHECK_THROWS(mpu.getRegister(2)); + + CHECK(mpu.getRegister(3) == 0x0102); + CHECK(mpu.getRegister(4) == 0x0304); + CHECK(mpu.getRegister(5) == 0x0506); + CHECK(mpu.getRegister(6) == 0x0708); + CHECK(mpu.getRegister(7) == 0x090a); + CHECK(mpu.getRegister(8) == 0x0b0c); + CHECK(mpu.getRegister(9) == 0x0d0e); + CHECK(mpu.getRegister(10) == 0x0f10); + CHECK(mpu.getRegister(11) == 0x1112); + CHECK(mpu.getRegister(12) == 0x1314); + + CHECK_THROWS(mpu.getRegister(13)); + CHECK_THROWS(mpu.getRegister(14)); + + CHECK_THROWS(mpu.getRegister(103)); + CHECK_THROWS(mpu.getRegister(104)); + + mpu.clear(); mpu.readHoldingRegisters(103, 10, 101); - auto commands = mpu.getCommandVector(); - - REQUIRE(commands.size() == 29); - - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 8); - REQUIRE(commands[2] == 12); - REQUIRE(commands[3] == 3); - REQUIRE(commands[4] == 0); - REQUIRE(commands[5] == 3); - REQUIRE(commands[6] == 0); - REQUIRE(commands[7] == 10); - REQUIRE(commands[8] == 0x34); - REQUIRE(commands[9] == 0xD0); - REQUIRE(commands[10] == MPUCommands::READ_MS); - REQUIRE(commands[11] == 25); - REQUIRE(commands[12] == 0); - REQUIRE(commands[13] == 101); - - REQUIRE(commands[14] == MPUCommands::WRITE); - REQUIRE(commands[15] == 8); - REQUIRE(commands[16] == 12); - REQUIRE(commands[17] == 3); - REQUIRE(commands[18] == 0); - REQUIRE(commands[19] == 103); - REQUIRE(commands[20] == 0); - REQUIRE(commands[21] == 10); - REQUIRE(commands[22] == 0x75); - REQUIRE(commands[23] == 0x0F); - REQUIRE(commands[24] == MPUCommands::READ_MS); - REQUIRE(commands[25] == 25); - REQUIRE(commands[26] == 0); - REQUIRE(commands[27] == 101); - - REQUIRE(commands[28] == MPUCommands::IRQ); - - std::vector res1 = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; - - REQUIRE_NOTHROW(mpu.processResponse(res1.data(), res1.size())); - - REQUIRE_THROWS(mpu.getRegister(1)); - REQUIRE_THROWS(mpu.getRegister(2)); - - REQUIRE(mpu.getRegister(3) == 0x0102); - REQUIRE(mpu.getRegister(4) == 0x0304); - REQUIRE(mpu.getRegister(5) == 0x0506); - REQUIRE(mpu.getRegister(6) == 0x0708); - REQUIRE(mpu.getRegister(7) == 0x090a); - REQUIRE(mpu.getRegister(8) == 0x0b0c); - REQUIRE(mpu.getRegister(9) == 0x0d0e); - REQUIRE(mpu.getRegister(10) == 0x0f10); - REQUIRE(mpu.getRegister(11) == 0x1112); - REQUIRE(mpu.getRegister(12) == 0x1314); - - REQUIRE_THROWS(mpu.getRegister(13)); - REQUIRE_THROWS(mpu.getRegister(14)); - - REQUIRE_THROWS(mpu.getRegister(103)); - REQUIRE_THROWS(mpu.getRegister(104)); - - REQUIRE_THROWS(mpu.checkCommandedEmpty()); + CHECK(mpu.size() == 1); + + commands = mpu[0].buffer; + + CHECK(commands.size() == 8); + + CHECK(commands[0] == 12); + CHECK(commands[1] == 3); + CHECK(commands[2] == 0); + CHECK(commands[3] == 103); + CHECK(commands[4] == 0); + CHECK(commands[5] == 10); + CHECK(commands[6] == 0x75); + CHECK(commands[7] == 0x0F); } TEST_CASE("Test MPU reading multiple registers - successful response", "[MPU]") { TestMPU mpu(1, 12); mpu.readHoldingRegisters(3, 10, 101); + + auto commands = mpu[0].buffer; + + CHECK(commands.size() == 8); + + CHECK(commands[0] == 12); + CHECK(commands[1] == 3); + CHECK(commands[2] == 0); + CHECK(commands[3] == 3); + CHECK(commands[4] == 0); + CHECK(commands[5] == 10); + CHECK(commands[6] == 0x34); + CHECK(commands[7] == 0xD0); + + CHECK_NOTHROW(mpu.parse(std::vector( + {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}))); + + CHECK_THROWS(mpu.getRegister(1)); + CHECK_THROWS(mpu.getRegister(2)); + + CHECK(mpu.getRegister(3) == 0x0102); + CHECK(mpu.getRegister(4) == 0x0304); + CHECK(mpu.getRegister(5) == 0x0506); + CHECK(mpu.getRegister(6) == 0x0708); + CHECK(mpu.getRegister(7) == 0x090a); + CHECK(mpu.getRegister(8) == 0x0b0c); + CHECK(mpu.getRegister(9) == 0x0d0e); + CHECK(mpu.getRegister(10) == 0x0f10); + CHECK(mpu.getRegister(11) == 0x1112); + CHECK(mpu.getRegister(12) == 0x1314); + + CHECK_THROWS(mpu.getRegister(13)); + CHECK_THROWS(mpu.getRegister(14)); + + CHECK_THROWS(mpu.getRegister(103)); + CHECK_THROWS(mpu.getRegister(104)); + mpu.readHoldingRegisters(103, 10, 101); - auto commands = mpu.getCommandVector(); - - REQUIRE(commands.size() == 29); - - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 8); - REQUIRE(commands[2] == 12); - REQUIRE(commands[3] == 3); - REQUIRE(commands[4] == 0); - REQUIRE(commands[5] == 3); - REQUIRE(commands[6] == 0); - REQUIRE(commands[7] == 10); - REQUIRE(commands[8] == 0x34); - REQUIRE(commands[9] == 0xD0); - REQUIRE(commands[10] == MPUCommands::READ_MS); - REQUIRE(commands[11] == 25); - REQUIRE(commands[12] == 0); - REQUIRE(commands[13] == 101); - - REQUIRE(commands[14] == MPUCommands::WRITE); - REQUIRE(commands[15] == 8); - REQUIRE(commands[16] == 12); - REQUIRE(commands[17] == 3); - REQUIRE(commands[18] == 0); - REQUIRE(commands[19] == 103); - REQUIRE(commands[20] == 0); - REQUIRE(commands[21] == 10); - REQUIRE(commands[22] == 0x75); - REQUIRE(commands[23] == 0x0F); - REQUIRE(commands[24] == MPUCommands::READ_MS); - REQUIRE(commands[25] == 25); - REQUIRE(commands[26] == 0); - REQUIRE(commands[27] == 101); - - REQUIRE(commands[28] == MPUCommands::IRQ); - - std::vector res1 = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; - - REQUIRE_NOTHROW(mpu.processResponse(res1.data(), res1.size())); - - REQUIRE_THROWS(mpu.getRegister(1)); - REQUIRE_THROWS(mpu.getRegister(2)); - - REQUIRE(mpu.getRegister(3) == 0x0102); - REQUIRE(mpu.getRegister(4) == 0x0304); - REQUIRE(mpu.getRegister(5) == 0x0506); - REQUIRE(mpu.getRegister(6) == 0x0708); - REQUIRE(mpu.getRegister(7) == 0x090a); - REQUIRE(mpu.getRegister(8) == 0x0b0c); - REQUIRE(mpu.getRegister(9) == 0x0d0e); - REQUIRE(mpu.getRegister(10) == 0x0f10); - REQUIRE(mpu.getRegister(11) == 0x1112); - REQUIRE(mpu.getRegister(12) == 0x1314); - - REQUIRE_THROWS(mpu.getRegister(13)); - REQUIRE_THROWS(mpu.getRegister(14)); - - REQUIRE_THROWS(mpu.getRegister(103)); - REQUIRE_THROWS(mpu.getRegister(104)); - - std::vector res2 = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; - - REQUIRE_NOTHROW(mpu.processResponse(res2.data(), res2.size())); - - REQUIRE_THROWS(mpu.getRegister(1)); - REQUIRE_THROWS(mpu.getRegister(2)); - - REQUIRE(mpu.getRegister(3) == 0x0102); - REQUIRE(mpu.getRegister(4) == 0x0304); - REQUIRE(mpu.getRegister(5) == 0x0506); - REQUIRE(mpu.getRegister(6) == 0x0708); - REQUIRE(mpu.getRegister(7) == 0x090a); - REQUIRE(mpu.getRegister(8) == 0x0b0c); - REQUIRE(mpu.getRegister(9) == 0x0d0e); - REQUIRE(mpu.getRegister(10) == 0x0f10); - REQUIRE(mpu.getRegister(11) == 0x1112); - REQUIRE(mpu.getRegister(12) == 0x1314); - - REQUIRE_THROWS(mpu.getRegister(13)); - REQUIRE_THROWS(mpu.getRegister(14)); - - REQUIRE(mpu.getRegister(103) == 0x0102); - REQUIRE(mpu.getRegister(104) == 0x0304); - REQUIRE(mpu.getRegister(105) == 0x0506); - REQUIRE(mpu.getRegister(106) == 0x0708); - REQUIRE(mpu.getRegister(107) == 0x090a); - REQUIRE(mpu.getRegister(108) == 0x0b0c); - REQUIRE(mpu.getRegister(109) == 0x0d0e); - REQUIRE(mpu.getRegister(110) == 0x0f10); - REQUIRE(mpu.getRegister(111) == 0x1112); - REQUIRE(mpu.getRegister(112) == 0x1314); - - REQUIRE_THROWS(mpu.getRegister(113)); - REQUIRE_THROWS(mpu.getRegister(114)); - - REQUIRE_NOTHROW(mpu.checkCommandedEmpty()); + CHECK(mpu.size() == 2); + + commands = mpu[1].buffer; + + CHECK(commands[0] == 12); + CHECK(commands[1] == 3); + CHECK(commands[2] == 0); + CHECK(commands[3] == 103); + CHECK(commands[4] == 0); + CHECK(commands[5] == 10); + CHECK(commands[6] == 0x75); + CHECK(commands[7] == 0x0F); + + std::vector res2 = {12, 3, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0xcf, 0xde}; + + CHECK_NOTHROW(mpu.parse(res2)); + + CHECK_THROWS(mpu.getRegister(1)); + CHECK_THROWS(mpu.getRegister(2)); + + CHECK(mpu.getRegister(3) == 0x0102); + CHECK(mpu.getRegister(4) == 0x0304); + CHECK(mpu.getRegister(5) == 0x0506); + CHECK(mpu.getRegister(6) == 0x0708); + CHECK(mpu.getRegister(7) == 0x090a); + CHECK(mpu.getRegister(8) == 0x0b0c); + CHECK(mpu.getRegister(9) == 0x0d0e); + CHECK(mpu.getRegister(10) == 0x0f10); + CHECK(mpu.getRegister(11) == 0x1112); + CHECK(mpu.getRegister(12) == 0x1314); + + CHECK_THROWS(mpu.getRegister(13)); + CHECK_THROWS(mpu.getRegister(14)); + + CHECK(mpu.getRegister(103) == 0x0102); + CHECK(mpu.getRegister(104) == 0x0304); + CHECK(mpu.getRegister(105) == 0x0506); + CHECK(mpu.getRegister(106) == 0x0708); + CHECK(mpu.getRegister(107) == 0x090a); + CHECK(mpu.getRegister(108) == 0x0b0c); + CHECK(mpu.getRegister(109) == 0x0d0e); + CHECK(mpu.getRegister(110) == 0x0f10); + CHECK(mpu.getRegister(111) == 0x1112); + CHECK(mpu.getRegister(112) == 0x1314); + + CHECK_THROWS(mpu.getRegister(113)); + CHECK_THROWS(mpu.getRegister(114)); } TEST_CASE("Test MPU preset holding register", "[MPU]") { @@ -325,99 +291,78 @@ TEST_CASE("Test MPU preset holding register", "[MPU]") { mpu.presetHoldingRegister(0x0001, 0x0003, 102); - auto commands = mpu.getCommandVector(); - - REQUIRE(commands.size() == 15); - - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 8); - REQUIRE(commands[2] == 0x11); - REQUIRE(commands[3] == 0x06); - REQUIRE(commands[4] == 0x00); - REQUIRE(commands[5] == 0x01); - REQUIRE(commands[6] == 0x00); - REQUIRE(commands[7] == 0x03); - REQUIRE(commands[8] == 0x9A); - REQUIRE(commands[9] == 0x9B); - REQUIRE(commands[10] == MPUCommands::READ_MS); - REQUIRE(commands[11] == 8); - REQUIRE(commands[12] == 0); - REQUIRE(commands[13] == 102); - REQUIRE(commands[14] == MPUCommands::IRQ); - - std::vector res = {0x11, 0x06, 0x00, 0x01, 0x00, 0x03, 0x9A, 0x9B}; - - REQUIRE_NOTHROW(mpu.processResponse(res.data(), res.size())); + CHECK(mpu.size() == 1); + + auto commands = mpu[0].buffer; + + CHECK(commands.size() == 8); + + CHECK(commands[0] == 0x11); + CHECK(commands[1] == 0x06); + CHECK(commands[2] == 0x00); + CHECK(commands[3] == 0x01); + CHECK(commands[4] == 0x00); + CHECK(commands[5] == 0x03); + CHECK(commands[6] == 0x9A); + CHECK(commands[7] == 0x9B); + + CHECK_NOTHROW(mpu.parse(std::vector({0x11, 0x06, 0x00, 0x01, 0x00, 0x03, 0x9A, 0x9B}))); } TEST_CASE("Test MPU preset holding registers", "[MPU]") { TestMPU mpu(5, 17); std::vector regs = {0x0102, 0x0304}; - mpu.presetHoldingRegisters(0x1718, regs.data(), regs.size(), 102); - - auto commands = mpu.getCommandVector(); - - REQUIRE(commands.size() == 20); - - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 9 + 2 * regs.size()); - REQUIRE(commands[2] == 17); - REQUIRE(commands[3] == 16); - REQUIRE(commands[4] == 0x17); - REQUIRE(commands[5] == 0x18); - REQUIRE(commands[6] == 0); - REQUIRE(commands[7] == regs.size()); - REQUIRE(commands[8] == regs.size() * 2); - REQUIRE(commands[9] == 0x01); - REQUIRE(commands[10] == 0x02); - REQUIRE(commands[11] == 0x03); - REQUIRE(commands[12] == 0x04); - REQUIRE(commands[13] == 0xED); - REQUIRE(commands[14] == 0x3A); - REQUIRE(commands[15] == MPUCommands::READ_MS); - REQUIRE(commands[16] == 8); - REQUIRE(commands[17] == 0); - REQUIRE(commands[18] == 102); - REQUIRE(commands[19] == MPUCommands::IRQ); - - std::vector res = {17, 16, 0x17, 0x18, 0, 2, 0xC6, 0xEB}; - - REQUIRE_NOTHROW(mpu.processResponse(res.data(), res.size())); + mpu.presetHoldingRegisters(0x1718, regs, 102); + + CHECK(mpu.size() == 1); + + auto commands = mpu[0].buffer; + + CHECK(commands.size() == 9 + 2 * regs.size()); + + CHECK(commands[0] == 17); + CHECK(commands[1] == 16); + CHECK(commands[2] == 0x17); + CHECK(commands[3] == 0x18); + CHECK(commands[4] == 0); + CHECK(commands[5] == regs.size()); + CHECK(commands[6] == regs.size() * 2); + CHECK(commands[7] == 0x01); + CHECK(commands[8] == 0x02); + CHECK(commands[9] == 0x03); + CHECK(commands[10] == 0x04); + CHECK(commands[11] == 0xED); + CHECK(commands[12] == 0x3A); + + CHECK_NOTHROW(mpu.parse(std::vector({17, 16, 0x17, 0x18, 0, 2, 0xC6, 0xEB}))); } TEST_CASE("Test MPU preset holding registers by simplymodbus.ca", "[MPU]") { TestMPU mpu(5, 0x11); std::vector regs = {0x000A, 0x0102}; - mpu.presetHoldingRegisters(0x0001, regs.data(), regs.size(), 102); - - auto commands = mpu.getCommandVector(); - - REQUIRE(commands.size() == 20); - - REQUIRE(commands[0] == MPUCommands::WRITE); - REQUIRE(commands[1] == 9 + 2 * regs.size()); - REQUIRE(commands[2] == 0x11); - REQUIRE(commands[3] == 0x10); - REQUIRE(commands[4] == 0x00); - REQUIRE(commands[5] == 0x01); - REQUIRE(commands[6] == 0x00); - REQUIRE(commands[7] == 0x02); - REQUIRE(commands[8] == 0x04); - REQUIRE(commands[9] == 0x00); - REQUIRE(commands[10] == 0x0A); - REQUIRE(commands[11] == 0x01); - REQUIRE(commands[12] == 0x02); - REQUIRE(commands[13] == 0xC6); - REQUIRE(commands[14] == 0xF0); - REQUIRE(commands[15] == MPUCommands::READ_MS); - REQUIRE(commands[16] == 8); - REQUIRE(commands[17] == 0); - REQUIRE(commands[18] == 102); - REQUIRE(commands[19] == MPUCommands::IRQ); - - std::vector res = {0x11, 0x10, 0x00, 0x01, 0x00, 0x02, 0x12, 0x98}; - - REQUIRE_NOTHROW(mpu.processResponse(res.data(), res.size())); + mpu.presetHoldingRegisters(0x0001, regs, 102); + + CHECK(mpu.size() == 1); + + auto commands = mpu[0].buffer; + + CHECK(commands.size() == 9 + 2 * regs.size()); + + CHECK(commands[0] == 0x11); + CHECK(commands[1] == 0x10); + CHECK(commands[2] == 0x00); + CHECK(commands[3] == 0x01); + CHECK(commands[4] == 0x00); + CHECK(commands[5] == 0x02); + CHECK(commands[6] == 0x04); + CHECK(commands[7] == 0x00); + CHECK(commands[8] == 0x0A); + CHECK(commands[9] == 0x01); + CHECK(commands[10] == 0x02); + CHECK(commands[11] == 0xC6); + CHECK(commands[12] == 0xF0); + + CHECK_NOTHROW(mpu.parse(std::vector({0x11, 0x10, 0x00, 0x01, 0x00, 0x02, 0x12, 0x98}))); } From ed59a93a68d884e6a7ca6be55a96a84da5940d75 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Tue, 9 Apr 2024 18:40:56 +0200 Subject: [PATCH 07/13] Exit command --- include/cRIO/CliApp.h | 14 ++++++++++++-- src/LSST/cRIO/CliApp.cpp | 12 ++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/cRIO/CliApp.h b/include/cRIO/CliApp.h index e4449f06..a8917772 100644 --- a/include/cRIO/CliApp.h +++ b/include/cRIO/CliApp.h @@ -115,9 +115,10 @@ class CliApp : public Application { /** * Construct CliApp. * - * @param _description a short description of the application + * @param name command line application name + * @param description a short description of the application */ - CliApp(const char* name, const char* description) : Application(name, description), _history_fn(NULL) {} + CliApp(const char* name, const char* description); /** * Class destructor. Subclasses are encouraged to include all destruction @@ -242,6 +243,8 @@ get_the_answer command. /** * Process unmatched commands. + * + * @param cmds commands to be processed */ virtual int processUnmached(command_vec cmds); @@ -250,6 +253,13 @@ get_the_answer command. */ void printCommands(); + /** + * Exits the application. + * + * @param cmds commands to be processed + */ + virtual int _exit(command_vec cmds); + int verbose; private: diff --git a/src/LSST/cRIO/CliApp.cpp b/src/LSST/cRIO/CliApp.cpp index 174c4be9..f2750ae5 100644 --- a/src/LSST/cRIO/CliApp.cpp +++ b/src/LSST/cRIO/CliApp.cpp @@ -62,6 +62,12 @@ CliApp::~CliApp() { } } +CliApp::CliApp(const char* name, const char* description) + : Application(name, description), _history_fn(NULL) { + addCommand("exit", std::bind(&CliApp::_exit, this, std::placeholders::_1), "", 0, NULL, + "Exits the application - Ctrl+d or Ctrl+c does the same"); +} + void CliApp::addCommand(const char* command, std::function action, const char* args, int flags, const char* help_args, const char* help) { for (auto iter = _commands.begin(); iter != _commands.end(); iter++) @@ -452,6 +458,12 @@ void CliApp::printCommands() { } } +int CliApp::_exit(command_vec cmds) { + std::cerr << "Exiting " << getName() << " - bye!" << std::endl; + exit(EXIT_SUCCESS); + return 0; +} + Command* CliApp::findCommand(std::string cmd, command_vec& matchedCmds) { Command* ret = NULL; From c7a009ce1c788747e1e470ddec3f797001046120 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Wed, 17 Apr 2024 17:47:49 +0200 Subject: [PATCH 08/13] changed FPGA::writeMPUFIFO signature --- include/cRIO/FPGA.h | 9 ++++----- src/LSST/cRIO/FPGA.cpp | 17 ++++++++++++++++- tests/TestFPGA.h | 2 +- tests/test_CSC.cpp | 2 +- tests/test_FirmwareLoad.cpp | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/cRIO/FPGA.h b/include/cRIO/FPGA.h index 24be7600..628011d3 100644 --- a/include/cRIO/FPGA.h +++ b/include/cRIO/FPGA.h @@ -109,13 +109,12 @@ class FPGA : public SimpleFPGA { void mpuCommands(MPU& mpu, const std::chrono::duration& timeout = 500ms); /** - * Commands FPGA to write to MPU commands buffer. Data to write are passed - * along in mpu parameter - you need to fill the MPU commands prior to - * calling this method. + * Commands FPGA to write to MPU commands buffer. * - * @param mpu Modbus Processing Unit to write + * @param data data to write to the MPU command buffer + * @param timeout timeout in milliseconds */ - virtual void writeMPUFIFO(MPU& mpu) = 0; + virtual void writeMPUFIFO(const std::vector& data, uint32_t timeout) = 0; /** * Commands FPGA to copy MPU output FIFO to FPGA-C/C++ output FIFO. This diff --git a/src/LSST/cRIO/FPGA.cpp b/src/LSST/cRIO/FPGA.cpp index 46d88a15..b0f95a6e 100644 --- a/src/LSST/cRIO/FPGA.cpp +++ b/src/LSST/cRIO/FPGA.cpp @@ -150,7 +150,22 @@ void FPGA::ilcCommands(ILC::ILCBusList &ilc, int32_t timeout) { } void FPGA::mpuCommands(MPU &mpu, const std::chrono::duration &timeout) { - writeMPUFIFO(mpu); + // construct buffer to send + std::vector data; + + data.push_back(mpu.getBus()); + data.push_back(0); + + uint8_t len = 0; + + for (auto cmd : mpu) { + data.insert(data.end(), cmd.buffer.begin(), cmd.buffer.end()); + len += cmd.buffer.size(); + } + + data[1] = len; + + writeMPUFIFO(data, 0); readMPUFIFO(mpu); } diff --git a/tests/TestFPGA.h b/tests/TestFPGA.h index 1d17f557..6bef6159 100644 --- a/tests/TestFPGA.h +++ b/tests/TestFPGA.h @@ -53,7 +53,7 @@ class TestFPGA : public LSST::cRIO::FPGA, public LSST::cRIO::PrintILC { uint16_t getTxCommand(uint8_t bus) override { return FPGAAddress::MODBUS_A_TX; } uint16_t getRxCommand(uint8_t bus) override { return FPGAAddress::MODBUS_A_RX; } uint32_t getIrq(uint8_t bus) override { return 1; } - void writeMPUFIFO(LSST::cRIO::MPU&) override {} + void writeMPUFIFO(const std::vector& data, uint32_t timeout) override {} std::vector readMPUFIFO(LSST::cRIO::MPU&) override { return std::vector({0xff, 0x0fe}); } diff --git a/tests/test_CSC.cpp b/tests/test_CSC.cpp index 2360384c..e6ddce8b 100644 --- a/tests/test_CSC.cpp +++ b/tests/test_CSC.cpp @@ -45,7 +45,7 @@ class TestFPGA : public FPGA { uint16_t getTxCommand(uint8_t) override { return 0; } uint16_t getRxCommand(uint8_t) override { return 0; } uint32_t getIrq(uint8_t) override { return 0; } - void writeMPUFIFO(MPU&) override {} + void writeMPUFIFO(const std::vector& data, uint32_t timeout) override {} std::vector readMPUFIFO(MPU&) override { return std::vector({0x04, 0x05}); } void writeCommandFIFO(uint16_t*, size_t, uint32_t) override {} void writeRequestFIFO(uint16_t*, size_t, uint32_t) override {} diff --git a/tests/test_FirmwareLoad.cpp b/tests/test_FirmwareLoad.cpp index 35f56197..433f98e3 100644 --- a/tests/test_FirmwareLoad.cpp +++ b/tests/test_FirmwareLoad.cpp @@ -54,7 +54,7 @@ class TestFPGA : public FPGA { uint16_t getRxCommand(uint8_t bus) override { return bus + 14; } uint32_t getIrq(uint8_t bus) override { return 1; } - void writeMPUFIFO(MPU& mpu) override {} + void writeMPUFIFO(const std::vector& data, uint32_t timeout) override {} std::vector readMPUFIFO(MPU& mpu) override { return std::vector({0x01, 0x02}); } void writeCommandFIFO(uint16_t* data, size_t length, uint32_t timeout) override; From fe73cddc72c28f8e0b25ce959f2aa939ef368456 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Wed, 17 Apr 2024 23:26:19 +0200 Subject: [PATCH 09/13] mpuCommands, dumpHex from vector --- include/Modbus/Parser.h | 7 ++++++- include/cRIO/SimpleFPGA.h | 18 +++++++++++++++++- src/LSST/cRIO/FPGA.cpp | 26 +++++++++++++------------- src/Modbus/Parser.cpp | 6 ++++-- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/include/Modbus/Parser.h b/include/Modbus/Parser.h index d0fb38fe..13300f92 100644 --- a/include/Modbus/Parser.h +++ b/include/Modbus/Parser.h @@ -42,7 +42,7 @@ namespace Modbus { * @param len length of the buffer */ template -static const std::string hexDump(dt *buf, size_t len) { +static const std::string hexDump(const dt *buf, size_t len) { std::ostringstream os; os << std::setfill('0') << std::hex; for (size_t i = 0; i < len; i++) { @@ -55,6 +55,11 @@ static const std::string hexDump(dt *buf, size_t len) { return os.str(); } +template +static const std::string hexDump(const std::vector
    &data) { + return hexDump
    (data.data(), data.size()); +} + /** * Exception thrown when calculated CRC doesn't match received CRC. */ diff --git a/include/cRIO/SimpleFPGA.h b/include/cRIO/SimpleFPGA.h index 1417b728..423f1bc2 100644 --- a/include/cRIO/SimpleFPGA.h +++ b/include/cRIO/SimpleFPGA.h @@ -33,6 +33,7 @@ #include #include +#include namespace LSST { namespace cRIO { @@ -100,6 +101,21 @@ class SimpleFPGA { void writeDebugFile(const char* message); + template + const void writeDebugFile(const char* message, const std::vector
    & data) { + if (_debug_stream.is_open()) { + try { + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); + _debug_stream << std::put_time(std::gmtime(&in_time_t), "%Y-%m-%dZ%T:") << message << " " + << Modbus::hexDump
    (data) << std::endl; + } catch (const std::ios_base::failure& e) { + SPDLOG_WARN("Cannot write to debug file: {}", e.what()); + closeDebugFile(); + } + } + } + template const void writeDebugFile(const char* message, dt* buf, size_t length) { if (_debug_stream.is_open()) { @@ -107,7 +123,7 @@ class SimpleFPGA { auto now = std::chrono::system_clock::now(); auto in_time_t = std::chrono::system_clock::to_time_t(now); _debug_stream << std::put_time(std::gmtime(&in_time_t), "%Y-%m-%dZ%T:") << message << " " - << ModbusBuffer::hexDump
    (buf, length) << std::endl; + << Modbus::hexDump
    (buf, length) << std::endl; } catch (const std::ios_base::failure& e) { SPDLOG_WARN("Cannot write to debug file: {}", e.what()); closeDebugFile(); diff --git a/src/LSST/cRIO/FPGA.cpp b/src/LSST/cRIO/FPGA.cpp index b0f95a6e..42db0e4b 100644 --- a/src/LSST/cRIO/FPGA.cpp +++ b/src/LSST/cRIO/FPGA.cpp @@ -150,23 +150,23 @@ void FPGA::ilcCommands(ILC::ILCBusList &ilc, int32_t timeout) { } void FPGA::mpuCommands(MPU &mpu, const std::chrono::duration &timeout) { - // construct buffer to send - std::vector data; - - data.push_back(mpu.getBus()); - data.push_back(0); - - uint8_t len = 0; - for (auto cmd : mpu) { + // construct buffer to send + std::vector data; + + data.push_back(mpu.getBus()); + data.push_back(cmd.buffer.size()); data.insert(data.end(), cmd.buffer.begin(), cmd.buffer.end()); - len += cmd.buffer.size(); - } - data[1] = len; + writeMPUFIFO(data, 0); - writeMPUFIFO(data, 0); - readMPUFIFO(mpu); + // read reply + auto answer = readMPUFIFO(mpu); + if (answer.empty()) { + throw std::runtime_error(fmt::format("Empty answer to {}", Modbus::hexDump(data))); + } + mpu.parse(answer); + } } } // namespace cRIO diff --git a/src/Modbus/Parser.cpp b/src/Modbus/Parser.cpp index 6445fb03..e033dcbc 100644 --- a/src/Modbus/Parser.cpp +++ b/src/Modbus/Parser.cpp @@ -34,8 +34,10 @@ CRCError::CRCError(uint16_t calculated, uint16_t received) void Parser::parse(std::vector buffer) { if (buffer.size() < 4) { throw std::runtime_error( - "Cannot parse small buffer - minimal Modbus buffer length is 4 bytes (address, function and " - "2 bytes CRC)"); + fmt::format("Cannot parse small buffer (size {}) - minimal Modbus buffer length is 4 bytes " + "(address, function and " + "2 bytes CRC)", + buffer.size())); } assign(buffer.begin(), buffer.end()); _data = 2; From 8d30a04c8289a1d44c2560d7ea41ad7c8c0459da Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Fri, 19 Apr 2024 11:25:14 +0200 Subject: [PATCH 10/13] Modbus functions constants --- include/cRIO/MPU.h | 10 ++++++++++ src/LSST/cRIO/FPGA.cpp | 1 + src/LSST/cRIO/MPU.cpp | 18 +++++++++--------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/cRIO/MPU.h b/include/cRIO/MPU.h index 607bf6cc..c39077de 100644 --- a/include/cRIO/MPU.h +++ b/include/cRIO/MPU.h @@ -49,6 +49,16 @@ namespace cRIO { */ class MPU : public Modbus::BusList { public: + /** + * Modbus basic functions. + */ + enum MODBUS_CMD { + READ_INPUT_STATUS = 2, + READ_HOLDING_REGISTERS = 3, + PRESET_HOLDING_REGISTER = 6, + PRESET_HOLDING_REGISTERS = 16 + }; + /** * Construct MPU class. * diff --git a/src/LSST/cRIO/FPGA.cpp b/src/LSST/cRIO/FPGA.cpp index 42db0e4b..2722430b 100644 --- a/src/LSST/cRIO/FPGA.cpp +++ b/src/LSST/cRIO/FPGA.cpp @@ -166,6 +166,7 @@ void FPGA::mpuCommands(MPU &mpu, const std::chrono::duration &timeout) { throw std::runtime_error(fmt::format("Empty answer to {}", Modbus::hexDump(data))); } mpu.parse(answer); + mpu.reset(); } } diff --git a/src/LSST/cRIO/MPU.cpp b/src/LSST/cRIO/MPU.cpp index 1802d9df..e67b7f26 100644 --- a/src/LSST/cRIO/MPU.cpp +++ b/src/LSST/cRIO/MPU.cpp @@ -31,7 +31,7 @@ using namespace LSST::cRIO; MPU::MPU(uint8_t bus, uint8_t node_address) : _bus(bus), _node_address(node_address), _commanded_address(0) { addResponse( - 2, + READ_INPUT_STATUS, [this](Modbus::Parser parser) { if (_commanded_address == 0 || _commanded_length == 0) { throw std::runtime_error("Empty read input status"); @@ -59,7 +59,7 @@ MPU::MPU(uint8_t bus, uint8_t node_address) : _bus(bus), _node_address(node_addr 0x82); addResponse( - 3, + READ_HOLDING_REGISTERS, [this](Modbus::Parser parser) { if (parser.address() != _node_address) { throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", @@ -78,7 +78,7 @@ MPU::MPU(uint8_t bus, uint8_t node_address) : _bus(bus), _node_address(node_addr 0x83); addResponse( - 6, + PRESET_HOLDING_REGISTER, [this](Modbus::Parser parser) { if (parser.address() != _node_address) { throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", @@ -99,7 +99,7 @@ MPU::MPU(uint8_t bus, uint8_t node_address) : _bus(bus), _node_address(node_addr 0x86); addResponse( - 16, + PRESET_HOLDING_REGISTERS, [this](Modbus::Parser parser) { if (parser.address() != _node_address) { throw std::runtime_error(fmt::format("Invalid ModBus address {}, expected {}", @@ -117,25 +117,25 @@ MPU::MPU(uint8_t bus, uint8_t node_address) : _bus(bus), _node_address(node_addr } void MPU::readInputStatus(uint16_t start_register_address, uint16_t count, uint32_t timing) { - callFunction(_node_address, 2, timing, start_register_address, count); + callFunction(_node_address, READ_INPUT_STATUS, timing, start_register_address, count); _commanded_address = start_register_address; _commanded_length = count; } void MPU::readHoldingRegisters(uint16_t start_register_address, uint16_t count, uint32_t timing) { - callFunction(_node_address, 3, timing, start_register_address, count); + callFunction(_node_address, READ_HOLDING_REGISTERS, timing, start_register_address, count); _commanded_address = start_register_address; } void MPU::presetHoldingRegister(uint16_t register_address, uint16_t value, uint32_t timing) { - callFunction(_node_address, 6, timing, register_address, value); + callFunction(_node_address, PRESET_HOLDING_REGISTER, timing, register_address, value); _commanded_address = register_address; } void MPU::presetHoldingRegisters(uint16_t start_register_address, const std::vector &values, uint32_t timing) { - callFunction(_node_address, 16, timing, start_register_address, static_cast(values.size()), - static_cast(values.size() * 2), values); + callFunction(_node_address, PRESET_HOLDING_REGISTERS, timing, start_register_address, + static_cast(values.size()), static_cast(values.size() * 2), values); _commanded_address = start_register_address; } From cc479d3198d72c983447ed448b85a1d28c70179f Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Fri, 19 Apr 2024 18:04:55 +0200 Subject: [PATCH 11/13] reintroduce MPUCommands --- doc/version-history.rst | 1 + include/cRIO/FPGA.h | 14 ++++++++++++++ src/LSST/cRIO/FPGA.cpp | 2 ++ 3 files changed, 17 insertions(+) diff --git a/doc/version-history.rst b/doc/version-history.rst index 2873afbe..be42f031 100644 --- a/doc/version-history.rst +++ b/doc/version-history.rst @@ -8,6 +8,7 @@ v1.11.0 to FPGA class. * Improved documentation. * Public constants to explain meaning of the numbers +* mpuCommands redesign v1.10.1 ------- diff --git a/include/cRIO/FPGA.h b/include/cRIO/FPGA.h index 628011d3..6d5b6191 100644 --- a/include/cRIO/FPGA.h +++ b/include/cRIO/FPGA.h @@ -37,6 +37,20 @@ namespace cRIO { class MPU; +/** + * Commands for FPGA MPU unit. + */ +enum MPUCommands { + WRITE = 1, + READ_US = 2, + READ_MS = 3, + WAIT_US = 100, + WAIT_MS = 101, + IRQ = 240, + TELEMETRY = 254, + RESET = 255, +}; + /** * Interface class for cRIO FPGA. Subclasses can talk either to the real HW, or * be a software simulator. diff --git a/src/LSST/cRIO/FPGA.cpp b/src/LSST/cRIO/FPGA.cpp index 2722430b..63f993b1 100644 --- a/src/LSST/cRIO/FPGA.cpp +++ b/src/LSST/cRIO/FPGA.cpp @@ -155,6 +155,8 @@ void FPGA::mpuCommands(MPU &mpu, const std::chrono::duration &timeout) { std::vector data; data.push_back(mpu.getBus()); + data.push_back(cmd.buffer.size() + 2); + data.push_back(MPUCommands::WRITE); data.push_back(cmd.buffer.size()); data.insert(data.end(), cmd.buffer.begin(), cmd.buffer.end()); From 95f550f4f598141656f5f127a717c5c257cf9109 Mon Sep 17 00:00:00 2001 From: Petr Kubanek Date: Thu, 25 Apr 2024 21:18:54 +0200 Subject: [PATCH 12/13] Fix cRIO::Task typo --- include/cRIO/Event.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/cRIO/Event.h b/include/cRIO/Event.h index 6a3c7a95..42713d48 100644 --- a/include/cRIO/Event.h +++ b/include/cRIO/Event.h @@ -43,11 +43,11 @@ class Event : public Task { virtual ~Event(); task_return_t run() override { - recieved(); + received(); return Task::DONT_RESCHEDULE; } - virtual void recieved() = 0; + virtual void received() = 0; }; } // namespace cRIO From 4b385b696d3c4e6f3b05dd1effc43c54e4e0a675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Kub=C3=A1nek?= Date: Mon, 6 May 2024 15:20:11 +0000 Subject: [PATCH 13/13] Updated error responses --- include/Modbus/BusList.h | 9 +++++---- include/Modbus/Parser.h | 8 ++++++-- src/ILC/ILCBusList.cpp | 2 +- src/LSST/cRIO/FPGA.cpp | 5 +++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/Modbus/BusList.h b/include/Modbus/BusList.h index 362bd03f..391864d2 100644 --- a/include/Modbus/BusList.h +++ b/include/Modbus/BusList.h @@ -40,7 +40,7 @@ namespace Modbus { * Error thrown when a response is missing. This is mostly caused by an @glos{ILC} on * the bus being dead/not reacting to the command send. */ -class MissingResponse : std::runtime_error { +class MissingResponse : public std::runtime_error { public: /** * Construct missing response exception. @@ -49,8 +49,9 @@ class MissingResponse : std::runtime_error { * @param func Expected function which wasn't responded by the @glos{ILC} */ MissingResponse(uint8_t address, uint8_t func) - : std::runtime_error(fmt::format("Missing response for function {} from ILC with address {}", - func, address)) {} + : std::runtime_error( + fmt::format("Missing response for function {0} (0x{0:02x}) from ILC with address {1}", + func, address)) {} }; /** @@ -59,7 +60,7 @@ class MissingResponse : std::runtime_error { * * @see BusList::addResponse */ -class UnexpectedResponse : std::runtime_error { +class UnexpectedResponse : public std::runtime_error { public: /** * Construct unexpected response. diff --git a/include/Modbus/Parser.h b/include/Modbus/Parser.h index 13300f92..8aa17f90 100644 --- a/include/Modbus/Parser.h +++ b/include/Modbus/Parser.h @@ -31,6 +31,8 @@ #include #include +#include + #include namespace Modbus { @@ -78,7 +80,7 @@ class CRCError : public std::runtime_error { /** * Thrown when response continue after CRC. */ -class LongResponse : std::runtime_error { +class LongResponse : public std::runtime_error { public: /** * Construct LongResponse from the given buffer. @@ -124,7 +126,9 @@ class Parser : public std::vector { */ void readBuffer(void *buf, size_t len) { if (_data + len > size()) { - throw std::out_of_range("Trying to access data beyond buffer are."); + throw std::out_of_range(fmt::format( + "Attempt to access data beyond buffer end (buffer index {}, but buffer length is {}).", + _data + len, size())); } memcpy(buf, data() + _data, len); _data += len; diff --git a/src/ILC/ILCBusList.cpp b/src/ILC/ILCBusList.cpp index b9984af7..822c7cbe 100644 --- a/src/ILC/ILCBusList.cpp +++ b/src/ILC/ILCBusList.cpp @@ -189,7 +189,7 @@ std::vector ILCBusList::getFaultString(uint16_t fault) { void ILCBusList::changeILCMode(uint8_t address, uint16_t mode) { uint32_t timeout = 335; try { - if ((getLastMode(address) == Mode::Standby && mode == Mode::Bootloader) || + if ((mode == Mode::Bootloader) || (getLastMode(address) == Mode::Bootloader && mode == Mode::Standby)) { timeout = 100000; } diff --git a/src/LSST/cRIO/FPGA.cpp b/src/LSST/cRIO/FPGA.cpp index 63f993b1..31417710 100644 --- a/src/LSST/cRIO/FPGA.cpp +++ b/src/LSST/cRIO/FPGA.cpp @@ -91,12 +91,13 @@ void FPGA::ilcCommands(ILC::ILCBusList &ilc, int32_t timeout) { uint16_t responseLen; readU16ResponseFIFO(&responseLen, 1, 20); - if (responseLen < 4) { + // minimal response is timestamp + 4 ILC bytes + if (responseLen < 8) { if (responseLen > 0) { uint16_t buffer[responseLen]; readU16ResponseFIFO(buffer, responseLen, 10); } - throw std::runtime_error("FPGA::ilcCommands timeout on response: " + std::to_string(responseLen)); + throw Modbus::MissingResponse(ilc[0].buffer.address(), ilc[0].buffer.func()); } uint16_t buffer[responseLen];