From 38203f8f4c153d2bdf8304ddc2712aaab1166a2d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 11:41:21 +0200 Subject: [PATCH 001/786] reduce to template --- .gitignore | 1 + LICENSE | 165 ++++++++++++++++++++++++++ cmake/FindPROJECT_NAME.cmake.in.in | 27 +++++ cmake/Makefile.coverage.in | 8 ++ cmake/PROJECT_NAME-config.in.in | 33 ++++++ cmake/create_cmake_config_files.cmake | 58 +++++++++ doc/DESY_logo.png | Bin 0 -> 7156 bytes 7 files changed, 292 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 cmake/FindPROJECT_NAME.cmake.in.in create mode 100644 cmake/Makefile.coverage.in create mode 100755 cmake/PROJECT_NAME-config.in.in create mode 100644 cmake/create_cmake_config_files.cmake create mode 100644 doc/DESY_logo.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in new file mode 100644 index 0000000..c3f6aa8 --- /dev/null +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -0,0 +1,27 @@ +# +# cmake module for finding @PROJECT_NAME@ +# +# returns: +# @PROJECT_NAME@_FOUND : true or false, depending on whether the package was found +# @PROJECT_NAME@_VERSION : the package version +# @PROJECT_NAME@_INCLUDE_DIRS : path to the include directory +# @PROJECT_NAME@_LIBRARY_DIRS : path to the library directory +# @PROJECT_NAME@_LIBRARIES : the provided libraries +# +# @author Martin Killenberg, DESY +# + +# Since this file is already part of the installation to be found, the configuration can be hard-coded at +# installation time +set(@PROJECT_NAME@_VERSION @@PROJECT_NAME@_SOVERSION@) +set(@PROJECT_NAME@_INCLUDE_DIRS @@PROJECT_NAME@_INCLUDE_DIRS@) +set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) +set(@PROJECT_NAME@_LIBRARIES @@PROJECT_NAME@_LIBRARIES@) + +# Use a macro provided by CMake to check if all the listed arguments are valid and set @PROJECT_NAME@_FOUND accordingly. +# This is mainly important to check the version. +set(@PROJECT_NAME@_FOUND 0) +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_LIBRARIES @PROJECT_NAME@_INCLUDE_DIRS + VERSION_VAR @PROJECT_NAME@_VERSION ) + diff --git a/cmake/Makefile.coverage.in b/cmake/Makefile.coverage.in new file mode 100644 index 0000000..79b08d3 --- /dev/null +++ b/cmake/Makefile.coverage.in @@ -0,0 +1,8 @@ +coverage: + rm -f `find . -name "*\.gcda"` + make test + lcov --capture --directory . --output-file coverage_all.info + #lcov capture also includes external stuff like glibc, boost etc. + #only extract the reports for this project + lcov --extract coverage_all.info "@CMAKE_SOURCE_DIR@*" -o coverage.info + genhtml coverage.info --output-directory coverage_html diff --git a/cmake/PROJECT_NAME-config.in.in b/cmake/PROJECT_NAME-config.in.in new file mode 100755 index 0000000..c97e7c3 --- /dev/null +++ b/cmake/PROJECT_NAME-config.in.in @@ -0,0 +1,33 @@ +#!/bin/sh + +print_usage(){ + echo "usage: $0 [--cppflags] [--ldflags] [--version]" 1>&2 +} + +if [ $# -eq 0 ]; then + print_usage + exit 1 +fi + +#The variables are all prepared in CMAKE and also provided by +#Find@PROJECT_NAME@.cmake, so dependent applications can create consistent +#Makefiles. +while [ $# -gt 0 ]; do + case "$1" in + --cppflags) + OUTPUT="${OUTPUT}@@PROJECT_NAME@_CPPFLAGS_MAKEFILE@" + shift;; + --ldflags) + OUTPUT="${OUTPUT}@@PROJECT_NAME@_LDFLAGS_MAKEFILE@" + shift;; + --version) + OUTPUT="${OUTPUT}@@PROJECT_NAME@_SOVERSION@" + shift;; + *) + echo "invalid option: $1" 1>&2 + print_usage + exit 2;; + esac +done + +echo ${OUTPUT} diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake new file mode 100644 index 0000000..7443799 --- /dev/null +++ b/cmake/create_cmake_config_files.cmake @@ -0,0 +1,58 @@ +####################################################################################################################### +# create_cmake_config_files.cmake +# +# Create the Find${PROJECT_NAME}.cmake cmake macro and the ${PROJECT_NAME}-config shell script and installs them. +# +# Expects the following input variables: +# ${PROJECT_NAME}_SOVERSION - version of the .so library file +# ${PROJECT_NAME}_INCLUDE_DIRS - list include directories needed when compiling against this project +# ${PROJECT_NAME}_LIBRARY_DIRS - list of library directories needed when linking against this project +# ${PROJECT_NAME}_LIBRARIES - list of libraries needed when linking against this project +# ${PROJECT_NAME}_CPPFLAGS - list of C++ compiler flags when compiling against this project +# ${PROJECT_NAME}_LDFLAGS - list of linker flags when linking against this project +# +####################################################################################################################### + +# check that all config variables which have to be replaced are set +foreach(CONFIG_VARIABLE SOVERSION INCLUDE_DIRS LIBRARY_DIRS LIBRARIES CPPFLAGS LDFLAGS) + if(DEFINED ${${PROJECT_NAME}_${CONFIG_VARIABLE}} ) + message(FATAL_ERROR "${PROJECT_NAME}_${CONFIG_VARIABLE} not set in CMakeListst.txt") + endif() +endforeach() + +# create variables for standard makefiles +set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS}") + +string(REPLACE " " ";" LIST ${${PROJECT_NAME}_INCLUDE_DIRS}) +foreach(INCLUDE_DIR ${LIST}) + set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS_MAKEFILE} -I${INCLUDE_DIR}") +endforeach() + +set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS}") + +string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARY_DIRS}) +foreach(LIBRARY_DIR ${LIST}) + set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS_MAKEFILE} -L${LIBRARY_DIR}") +endforeach() + +string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARIES}) +foreach(LIBRARY ${LIST}) + set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS_MAKEFILE} -l${LIBRARY}") +endforeach() + +# we have nested @-statements, so we have to parse twice: + +# create the cmake Find package script +configure_file(cmake/FindPROJECT_NAME.cmake.in.in "${PROJECT_BINARY_DIR}/cmake/Find${PROJECT_NAME}.cmake.in") +configure_file(${PROJECT_BINARY_DIR}/cmake/Find${PROJECT_NAME}.cmake.in "${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake") + +# create the shell script for standard make files +configure_file(cmake/PROJECT_NAME-config.in.in "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.in") +configure_file(${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.in "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config") + +# install the script +install(FILES "${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake" + DESTINATION share/cmake-${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}/Modules COMPONENT dev) + +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config DESTINATION bin COMPONENT dev) + diff --git a/doc/DESY_logo.png b/doc/DESY_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1a68c33660eaba69b35d4d1d8cc68e86f673842d GIT binary patch literal 7156 zcmVT>Os z`=V58Uqef5^CJ|gkW$K0s>;UAeCOQrl%WWP-lrSy-Fx4?F9}W4@Gb#a5vooAK&cb8 zBENUgzo+{W0IUG`N=pM^09a5%SyV~Pp!P>hLMKwb;2z`R5$Hsf zJP&F~bfT6-zS6R&jB}Er5iwD*L`B&W7ZppQpjZ+G*^(-#78^|{ZkCcfEG2n4O7d~E z^1 zN*jZ0dV4RM+SbEm7>Y1DB0(oHW0EAQ1SU|`!We)kF2MY`5CSa#0;m$`g-)I!LPZQM zB#zA$`Tdika5z*D=5n%anPACp?dDQjyV*>agD$&l)OjDPvZ4{uylgoeFLSR=iNc%V zqDxUVa$ScleOV8i-qpvaoK{kSPD%zkajA+VOcF3zfSq3SOGomu#m-O)XCa!}bc(7G2SWw>t5c$I zI8+o$ibmZs)a?K3pck1By40M@Cr zWPSWdvg>fD;NI28$8O&&%=);+Ni*ZFUxwD+y2cT{VTB{n%J~|A7e{jLe|{_7Eh`%F z(eBJ%;W{3lNLa&7J8Beh5pIWROjyT zynkcBmU{T+&XfHDb5YHQMj!xOKWI;0Gr*^wK9%wP)3H>~vuCq`Pi_>ZcP`^HmG5pg z+!-u%8<#}lQV6u6N}kaMP^*ddlaUhl?S0WfFGrRC>Y9!-8-ljC#eiK5G)ZV5S?7x0 zyvh;($7AW>L;GWcM`roxU6*;MNdg|!dOr0=I{-|h#mBi-0xi(TJVW!0)=VJ#{BX|m z=)pwa4TJW`XSaH0&S%iQ-QX;wdgm5*=!!lr`QYB@is=`_>|b2jG3v0Ac2qU5-s1su zgMA7uOf0R8fdqhV=md5`C(dXC00J7oJbEzE{lajr8O)Yc`Y*5ck5q<~GV~dE4nP?C z<7>1b05dp`iPH*!9ncBvgic}?bn=1#f(1#n{q^4Hz<9FE-*<(7P6LHd*FAG!I-F6zjb}^%yLhA6NRdQxcB++(1DqPM_a^| z6D5t_GoJT#aCEVqdr>5TU44A2Bx~AZhm!q6erq;prxoa9q7kq##~l^uvv;Z4XOfHD z&+u&x`_3>`jF$bM2NK=msWNx}wH?Ffv*{MY6GxMQ(PWu#`mMo}kyP==h zD9l~AoR56(<>>O+tjs`nE6sf~_t}R<=8DA#G+Z;oJu_@P9{~HW&SpKo8Oe2gd6$1A zXs65Vc4jgqS)V?g?OMt;xw1k(dNAJ8?mYqEuFJgB0|Fy{b5CTjAgd&F|J>{h7%r*1 za=am$5okk|I78+?_*l5e|F1*IzB{+LCpH9ac~t{3mMq)a>Bynkf0Dp0ne^gOHe0N{0zjC;ypC)lb=VamMH2HiZo@drTHvmseR4m_pIXZa53VY<{ zRr+kZZ&GyL`+RuK-U-pAY9RjUtyJKPzn&fX*w3am{mCz8);{z~Y*|8-8$Jdj@ls2V zZap$3)e_CimZe^&0eJjKvU4&eSvd=-e*GH%@Yz_&@x;+ouyV?LUs3J93r)1JQKxuR zLs90qo*RJm$48Q#ex5FUZmVa;xZtWjE}ba~lmC5xZ23PNrsRYu&xe{qS(OcCW%fX* z;J$B{e`HhL3>wPGZD}q5j3p(TpKpEa&6QQ^+k2w}Gg--Y`7%BQ09_7C`SaVoW8Z%z zHhAMod%T0EQR%A@S2O~$4I|r)G|*t&h@`kHuiX*8X zW}n{dp4mSsdPfpv8+23391J!B!Vc&h&7=MiHlJ8S;_*n4e{;4VJoLWKla0Rc$w-lF zbi5wJiBipZn>*RuPv1;osX?LDekR-Oy)&^A`?cRjhKjOE5r9UKz-KS@jNi0~hqT6T z6}NP;$sZj~_I&fbfnhWz&){T!rO&K^##C;sUD!<*5RV^8b?@rqV*~ES25ec;7Bh)V z=hbTc3J+7bVbDI;%C!b;G^u>20!zCFn7jSE8y!c;E&A}`O!q#e*0!&w%N_s_*-+~20aTO**mv*LP0y- zvS)GIM%Q%E-u&^E>#QQ4S@_5{!ZMeow5yMgJ#{J*FfK(M7pU?Cpq4-55Tg2 zQWRGEteM4vp?!j-wAyD~bdiRp2=%>#_GJBWD@~~1yQ%Z^Wj*YoT4HA?`R?uB(SNtv z8EbaVzDdz_C?vYAG^qf(w*LDy&Za+nJ+2wx6DOl3XHnKj=us$x2QxSGW4$p363`7r zW4X~_*$x1QLj~dHRgUvMW`DNRJ8|#x;nkUvy1+go2xv42?WaeRo%dYspQMS}D2K~R zDt~!Z$Jp-iTyoEt=ow3v`9wisXcF2o7b6aOnA~j}TwxDKH`FE#z>kk4JD)w14c>8y zd-8oN9q}XKqJ6c`DlWDpP>)TWY|cs%>g&^@^QwM6g@tR`Mpgv@Kr^B-HUNVF`T+D- zqz^#>$9MKc z`vGY8UE!aA9#9wuFi~lZ*CPOmh6eJeP53chQ~M_JLcg2Ix4M=gu%!IeD?7$D05fUH z>gQ?69K(C)eVwNt_+4cA7oVG5{f%oo&Mjt&5XAXc*{9Ny^{@9t26+o5|F@fir#x(9 z4P6Sh^>C?24<`Cl4HF~*3JZC$LFcS!jDc2@*i3>$p@MK}7n@q@mmmQ2xGbgm0H~cI zl?QJK4zKbuxqF|VTXQU2Y|E7cfc=ws=e^H|*R1wiv){QXc-d(pb2PS+U1}4+*Z$cI43d9Y{37xUI?vi zGP{eCM#evW{=1kU`D=YKv8UX@9r=_$hXv?{*wPRkoAX`L9C4Y82)lvUBc7{?uFyx3E zf4?-E6HdoU_DEh~d>mC?3_0Tjq5HVcoXL0r-JIHJ@|331lJ%9zybA!XTW(Lb z>O1>5x*Qi3V=^@7?wC0u4Fb)%dB$r?RFo|=NvN%43>bL&bS4;S5Fj7`TsLTs+`Zj9 zWgPqY$xLUWpfvQIJM47v14E9u2H=6;MV2256>9v@7lwgfpUdaIb$#HRogtOOp`z<_ ztmN#l=Zio5VD|~4GI;*vNV4nonSv*1r;Gn#y|8F_-V6^%mB$kELEd>zP|FiZqq;Ih z*9j396^mda+ln-A?O_uuJxso=Xynmw(KQk;+g}{XbqF?6`qUIf*$?A^zhVT{FZIqK;mcl8zMTHIQh)4+(%W2sB7E3laszBG{<5 z2;3C3Wo{XA#sT0{K=^N7kN3PVoC~}-lI!@yM)w?P8nbrva;a;U+Z#?)!-+Bn0PFqM z%!k(K2K1&*TQ0!UrI*I?zC=OqjKvNht@b_2B8M-xWqkkV5Z=n$w=(4 z&t*M($3-6id~%a8gC)YxQ{`+~wI~`0O8>DNaWqdLP&K&-<8`W_wylBnFxz`kQL>l` zeClMT>#38q6a7{FeDu#R_f1|sU{5`BCRdorO6)UdvYpSI$#yanp|18@Gap#xjBe{? zGsYJW*lFp;mG-%3&SX0uJ(%p5RgGBNVar_F#Ws!HG=q<$B~1eoqD)sNo_StOEw$*% zmW?KqqO8>?d$)x|L7Bf7M^iPn;h=}fx!Ky;afO!=0f1*n<&j&uPQ5xMI`@p_JSXN# z&b*}3$HGP7Shy%$*~i7cw97wXTzA_BVQ%-h=+BiDngDS7=A~;N0Em(v&EYKMqHZ7! z-BkgqEAX1}+RakZ$w;Z~kh>WtBUOXB(@`5q{@{>fuGts}=yu~3%eeFv%eb@#;KW>s z|34?wfkUB!`{hZ|`=^pN#a8@Xo*|W;y=?r2;ap(A&E(hk+H>oodD&v62$iq8oAbPw zTE!sHypU|P@UWDWEH*YXmI4jH-U-nI04uyqZYgWX5AKQd9}E>eyOwdWFJA7OBmk@r zSo2m|m^DlrMbm1WzRH*difAtl1OTF-^PEY!s#|Spf~$l}LciM7| zwC~CRd$Px6DSFxVEr>)xVSL=Y3p>vVYI#x(0?VjY|IF85xU3Z8w35=?HD}T&_jX^g3`n1^a5o;IEoy&T@{@b}B zmL`=QJ&nl&fV~rUttbFcl)^jnx-&nG2i;A-C@?~5-fo73` zYDm9ilA=kly_Ze>;EhDT$s%tXss`c*Z^XNIkLCS$ZS{;_*I<2QDY$KeFxP3POMmx9 zVjz^2Y@gZU2^sIDODc6bTH<*NsjTudB15)5hQBf?3d`M0&cn_JQOul}xtb?1kJ^$^ zPPTq#i+kqp-$?eFCnlOCv|Cm?W^do*4%d&vjc{P5AZ+VljUU&j zf->_`L?y+;SQ8-t48XJ&VruR8zZ6;SB{xS(-`*P? z4DgoHSFiGq*2e`6$Qp>B9!&;cofO@3dD&)Us`3meFLyKfJ1%k0tnf4@KAe-7Z~Qj8 zGMbmHa+3_#OS`$`*RS@EF7~}+qGI{X)6*Lsxw-53GQm=UUN$z4>Y9krjseh&Uspr5 z-nPE#(SBJEn||^{rW1Ph`gu`SHR6w-o>`x)#QI1A+6^o0b9Zg^HVr9_Cd;;m_QwV$ zQ__MAhkRM3-~2R(R)YdO6UE4`zjt$Bd8kit8>|o z9le~f3TV{bnK=<*DpE8PD4I?(svUCx@=hzMd|=2K{@-t=x|Jr6hZ!tg`-svcp*j|c zYCrN?thdoxax*-2D%*7~US`e5MiOP4+(M$}M5^5MNBY&-tT$UyEgxI&G9uRtxImRB z0JQ|Fm6OaD7$u|HN7p+e1zDqhc{)>*T~&|aM45f!ShB}%C1nDDg(TGX4>`ik#=T?V zB7b1Iup~1?Q8nVRL&=&HU(<}aD4b2gb7e&%{`Ew<>(({S*-8MxC{{FcVem8WW7p4c z#s!AVmyEJB>H6?mXXwcj>8=}B+7m3jFwFDPXioUS8;OA{`}x@Cw|S>S8HsV&D7hJV z0B}4~v^RTiF^nY3yoPEB!Ozo@m!nE?QL!#|-AWJB(2{uaM7o0_2<_Ij&S>S3xm>oM zlX=v;Q6d_U2fbue`{0lxZlg)%XK$qg=8H4{KRlS|`rFqN1Alahd-Cq>-YJrR*5k62 z79$S;h7*gAd(BXiRcgF0FZ+`lg+(Vhk|4DAue2}7tI3vB>KCW8!T+?uHACz6q6WRs zjYzEVa2olcMsacq)gt8-fFwl{+Q&DzW}ZEh4Gt%&W6=FCMF#gxh~5X@8#r_Gs_If? zD@YgGi}nDz$zlHc+U54tt!tg3Mz)@vA*Fsdoqu3YbY&%pavp#CM!eV0Qzg^vJl|(5 z(5W7gI5jk2^&qS1fglSZxTX%0<+|F-P7?RHOFdIIy4J(k+{xxP1lhEvX&ONgTDQYe*wM=-zjS%W_=i?Iqth9Q`NipM;N~Gm zi~#VD$5MekyD$UN-24bx%dQO>L*FYQ$H5Gq+OH zG<@fV;5m8;Zc;sdbvEn$`vZyorC#UbsM0^&+H<^}duB>1<+PH@-toNa;aB5>U%A3} zZd(s$teY4mUel&_8oJ@UAcwJ8D5;3*{sQI_`8a?CO%mGIuJVtjiYoK){`j(XI$>aY z51ZyKttCL#TvxT#mJFwrRL;gr>?5zo2X5Qonlh1B(@&Em(WLx!v9#P6s%7%#4~_xI zxonjDU#{{GAD9u{|NK^ZL25>;@Nl$z`(|P4{AX6t<)909T;gsyX|f(MQL#L@C%R&L z5107JI+ro)=Aie@OrX*KrU{*k5h$a&pMZIT2BR%3cQeI%cKAmB<#@X1hldhf?LNmh zt#l-I_Hr#vKD>pLzp&FcZfSGaI2SLo_dY*66tvUDdv?sHnG{glH*=pC-DBp0%UuC5 zN5e(?_g;=JU*WOlzjB3dj6Kg%U^6^>Cgc6_(R8noAYPC61Z?Shc6cY2Qe0!k-toNa zk=Nq`JA1jr-P^rWl>Q@JP`T?xDYyo}%w}cgn|mUIng)FB>W-1lC1iA&5y{Jz{ga|= zG*RYWnG(I74!XFdlTEGdu;sS(EPa98z!OJP!KY4Tx<0W{nEG(lSbj$et^wVI=|yer z@Pd;6f~=C?-52XW8!y>Ezs);dDQIpRcmCg*bst;joNaa2jz&nA6zXqYODsDcDLViB za^I*aOTl<-Bd8K+v|+SZ^51IBtpxoSF~-dLUDXr-ewHTf-?*k@^ogUX;P+pN4X*Go zxleC)&#bw?^@(;+k~Q*QPoz73c_tg|w9|#}-xxe)TGz>;*2bHwwBbq{LW>*mETw3< z2wk-eG}@BR2f*{mO+xs_mG=1KN0MD%{Y`lFmM%7V`(}4&nak34U0kbBHQ?E^S?`m_ z(_J)0XrH~*J#pRgn)qJ=wHxltK_}86Zx&0~fM=;y-+9!MXe`PbD^|vah@K9f#+P^b zCq@$GxgQ-)_I%;l(7IiHeC(P5KC!8j&5ajGT&}3`b1u;>vygZS2J$EkKkt?Z| zTh}^6x2|!{|<3eH!jMB!Mt zD9EZttn^y5ex53O=C`?{%YKfMyc{hRWR;5M6-&HkYdnP+OP0B4UbgiKOnz%Oo7&aK zrRwVV-qo9r&0mi;x4#3b8;6~YlTYz3n0u8(yH=#AiyXc$Hz8wS_`2S)X Date: Fri, 27 May 2016 13:35:40 +0200 Subject: [PATCH 002/786] removed check if required input variables are set, since it does not work (and never did) --- cmake/create_cmake_config_files.cmake | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 7443799..b1c6e63 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -13,13 +13,6 @@ # ####################################################################################################################### -# check that all config variables which have to be replaced are set -foreach(CONFIG_VARIABLE SOVERSION INCLUDE_DIRS LIBRARY_DIRS LIBRARIES CPPFLAGS LDFLAGS) - if(DEFINED ${${PROJECT_NAME}_${CONFIG_VARIABLE}} ) - message(FATAL_ERROR "${PROJECT_NAME}_${CONFIG_VARIABLE} not set in CMakeListst.txt") - endif() -endforeach() - # create variables for standard makefiles set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS}") From df9a5e4d36807cd46ee4d157ebb634058a092ea3 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 15:08:22 +0200 Subject: [PATCH 003/786] renamed some variables exported to depending projects, to be more cmake-like ${PROJECT_NAME}_CPPFLAGS -> ${PROJECT_NAME}_CXX_FLAGS ${PROJECT_NAME}_LDFLAGS -> ${PROJECT_NAME}_LINK_FLAGS also make sure that the exported lists are consistently space-separated (sometimes they might be semicolon separated or mixed) --- CMakeLists.txt | 4 ++-- cmake/FindPROJECT_NAME.cmake.in.in | 4 ++++ cmake/create_cmake_config_files.cmake | 19 ++++++++++++------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a2aa15..aca8df1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,8 +108,8 @@ install(FILES ${CMAKE_SOURCE_DIR}/tests/include/IndependentTestCore.h DESTINATI set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIRS}") set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY}") set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS}") -set(${PROJECT_NAME}_CPPFLAGS "-std=c++11") -set(${PROJECT_NAME}_LDFLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags") +set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11") +set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags") include(${CMAKE_SOURCE_DIR}/cmake/create_cmake_config_files.cmake) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index c3f6aa8..1b439a2 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -7,6 +7,8 @@ # @PROJECT_NAME@_INCLUDE_DIRS : path to the include directory # @PROJECT_NAME@_LIBRARY_DIRS : path to the library directory # @PROJECT_NAME@_LIBRARIES : the provided libraries +# @PROJECT_NAME@_CXX_FLAGS : additional C++ compiler flags +# @PROJECT_NAME@_LINKER_FLAGS : additional linker flags # # @author Martin Killenberg, DESY # @@ -17,6 +19,8 @@ set(@PROJECT_NAME@_VERSION @@PROJECT_NAME@_SOVERSION@) set(@PROJECT_NAME@_INCLUDE_DIRS @@PROJECT_NAME@_INCLUDE_DIRS@) set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) set(@PROJECT_NAME@_LIBRARIES @@PROJECT_NAME@_LIBRARIES@) +set(@PROJECT_NAME@_CXX_FLAGS @@PROJECT_NAME@_CXX_FLAGS@) +set(@PROJECT_NAME@_LINKER_FLAGS @@PROJECT_NAME@_LINKER_FLAGS@) # Use a macro provided by CMake to check if all the listed arguments are valid and set @PROJECT_NAME@_FOUND accordingly. # This is mainly important to check the version. diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index b1c6e63..9570438 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -8,31 +8,36 @@ # ${PROJECT_NAME}_INCLUDE_DIRS - list include directories needed when compiling against this project # ${PROJECT_NAME}_LIBRARY_DIRS - list of library directories needed when linking against this project # ${PROJECT_NAME}_LIBRARIES - list of libraries needed when linking against this project -# ${PROJECT_NAME}_CPPFLAGS - list of C++ compiler flags when compiling against this project -# ${PROJECT_NAME}_LDFLAGS - list of linker flags when linking against this project +# ${PROJECT_NAME}_CXX_FLAGS - list of additional C++ compiler flags needed when compiling against this project +# ${PROJECT_NAME}_LINKER_FLAGS - list of additional linker flags needed when linking against this project # ####################################################################################################################### # create variables for standard makefiles -set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS}") +set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS}") string(REPLACE " " ";" LIST ${${PROJECT_NAME}_INCLUDE_DIRS}) foreach(INCLUDE_DIR ${LIST}) - set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS_MAKEFILE} -I${INCLUDE_DIR}") + set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS_MAKEFILE} -I${INCLUDE_DIR}") endforeach() -set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS}") +set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS}") string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARY_DIRS}) foreach(LIBRARY_DIR ${LIST}) - set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS_MAKEFILE} -L${LIBRARY_DIR}") + set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -L${LIBRARY_DIR}") endforeach() string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARIES}) foreach(LIBRARY ${LIST}) - set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS_MAKEFILE} -l${LIBRARY}") + set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -l${LIBRARY}") endforeach() +# force the lists to be space separated (may be sometimes semicolon separated) +string(REPLACE ";" " " ${PROJECT_NAME}_INCLUDE_DIRS ${${PROJECT_NAME}_INCLUDE_DIRS}) +string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARY_DIRS ${${PROJECT_NAME}_LIBRARY_DIRS}) +string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES}) + # we have nested @-statements, so we have to parse twice: # create the cmake Find package script From 03447159e4cc917403f82af57442008298c75c15 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 27 May 2016 15:10:24 +0200 Subject: [PATCH 004/786] ProcessVariable and ProcessVariableImpl are now the same with a typedef --- include/ProcessScalar.h | 64 ++++++++++++++++++++----------------- include/ProcessScalarImpl.h | 56 +++++++++++++++++++++----------- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index f88e581..e136efe 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -16,31 +16,26 @@ namespace mtca4u { * should only be used from a single thread. */ template - class ProcessScalar: public ProcessVariable { + class ProcessScalarHowItLookedLike: public ProcessVariable { public: - /** - * Type alias for a shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - /** * Assign the content of another process variable of type T to this one. * It only assigns the variable content, but not the callback functions. * This operator behaves like set(). */ - ProcessScalar & operator=(ProcessScalar const & other) { - this->set(other); - return *this; - } + // ProcessScalar & operator=(ProcessScalar const & other) { + // this->set(other); + // return *this; + // } /** * Assign the content of the template type. This operator behaves like * set(). */ - ProcessScalar & operator=(T const & t) { - this->set(t); - return *this; - } + // ProcessScalar & operator=(T const & t) { + // this->set(t); + // return *this; + // } /** * Set the value of this process variable to the one of the other process @@ -48,53 +43,64 @@ namespace mtca4u { * notifies the control system that this process variable's value has * changed. */ - virtual void set(ProcessScalar const & other)=0; + // virtual void set(ProcessScalar const & other)=0; /** * Set the value of this process variable to the specified one. This does * not trigger the on-set callback function, however it notifies the control * system that this process variable's value has changed. */ - virtual void set(T const & t)=0; + //virtual void set(T const & t)=0; /** * Automatic conversion operator which returns a \b copy of this process * variable's value. As no reference is returned, this cannot be used for * assignment. */ - virtual operator T() const =0; + //virtual operator T() const =0; /** * Returns a \b copy of this process variable's value. As no reference is * returned, this cannot be used for assignment. */ - virtual T get() const =0; + //virtual T get() const =0; - const std::type_info& getValueType() const { - return typeid(T); - } + // const std::type_info& getValueType() const { + // return typeid(T); + // } - bool isArray() const { - return false; - } + // bool isArray() const { + // return false; + // } protected: /** * Creates a process scalar with the specified name. */ - ProcessScalar(const std::string& name = std::string()) : - ProcessVariable(name) { - } + //ProcessScalar(const std::string& name = std::string()) : + // ProcessVariable(name) { + // } /** * Protected destructor. Instances should not be destroyed through * pointers to this base type. */ - virtual ~ProcessScalar() { - } + // virtual ~ProcessScalar() { + // } }; +} //namespace + +// FIXME ProcessScalarImpl.h must be included before defining the creator functions, because +// these have been hacked to return the impl. +#include "ProcessScalarImpl.h" + +namespace mtca4u{ + // for the transition phase ProcessScalar and Impl are the same + template + using ProcessScalar = impl::ProcessScalarImpl; + /** * Creates a simple process scalar. A simple process scalar just works on its * own and does not implement a synchronization mechanism. Apart from this, diff --git a/include/ProcessScalarImpl.h b/include/ProcessScalarImpl.h index cbca146..46e4454 100644 --- a/include/ProcessScalarImpl.h +++ b/include/ProcessScalarImpl.h @@ -15,26 +15,27 @@ #include "ProcessVariableListener.h" namespace mtca4u { - namespace impl { + namespace impl { /** * Implementation of the ProcessScalar. This implementation is used for all * three use cases (sender, receiver, and stand-alone). */ template - class ProcessScalarImpl: public ProcessScalar { + class ProcessScalarImpl: public ProcessVariable { - // Allow the factory functions to create instances of this class. - friend typename ProcessScalar::SharedPtr createSimpleProcessScalar( - const std::string &, T); + // Allow the factory functions to create instances of this class. + // friend typename ProcessScalarImpl::SharedPtr createSimpleProcessScalar( + // const std::string &, T); - friend typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar< - T>(const std::string &, T, std::size_t, - boost::shared_ptr, - boost::shared_ptr); + // friend typename std::pair::SharedPtr, + // typename ProcessScalarImpl::SharedPtr> createSynchronizedProcessScalar< + // T>(const std::string &, T, std::size_t, + // boost::shared_ptr, + // boost::shared_ptr); - private: + // fixme whould be private: + public: /** * Type of the instance. This defines the behavior (send or receive * possible, modifications allowed, etc.). This enum type is private so @@ -60,6 +61,11 @@ namespace mtca4u { }; public: + /** + * Type alias for a shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + /** * Creates a process scalar that works independently. This means that the * instance is not synchronized with any other instance and thus the send @@ -68,7 +74,7 @@ namespace mtca4u { */ ProcessScalarImpl(InstanceType instanceType, const std::string& name, T initialValue) : - ProcessScalar(name), _instanceType(instanceType), _value( + ProcessVariable(name), _instanceType(instanceType), _value( initialValue) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. @@ -88,7 +94,7 @@ namespace mtca4u { */ ProcessScalarImpl(InstanceType instanceType, const std::string& name, T initialValue, std::size_t numberOfBuffers) : - ProcessScalar(name), _instanceType(instanceType), _value( + ProcessVariable(name), _instanceType(instanceType), _value( initialValue), _bufferQueue( boost::make_shared >( numberOfBuffers)) { @@ -125,7 +131,7 @@ namespace mtca4u { boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener, boost::shared_ptr receiver) : - ProcessScalar(receiver->getName()), _instanceType(instanceType), _timeStamp( + ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( receiver->_timeStamp), _value(receiver->_value), _bufferQueue( receiver->_bufferQueue), _receiver(receiver), _timeStampSource( timeStampSource), _sendNotificationListener( @@ -157,10 +163,12 @@ namespace mtca4u { /** * Assignment operator. This behaves exactly like the set(...) method. */ - ProcessScalarImpl & operator=(ProcessScalar const & other) { - set(other.get()); - return (*this); - } + // currently commented out while removing the impl-inheritance. will be needed later + // in the pimpl pattern + // ProcessScalarImpl & operator=(ProcessScalar const & other) { + // set(other.get()); + // return (*this); + // } /** * Assignment operator. This behaves exactly like the set(...) method. @@ -177,8 +185,18 @@ namespace mtca4u { void set(T const & t) { _value = t; } + + // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl + // inheritence for creating the pimpl pattern, where impl and not are typedefed. + const std::type_info& getValueType() const { + return typeid(T); + } + + bool isArray() const { + return false; + } - void set(ProcessScalar const & other) { + void set(ProcessScalarImpl const & other) { set(other.get()); } From 2a10931230c1802d0ba273297f02f455d760c5fb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 15:08:22 +0200 Subject: [PATCH 005/786] renamed some variables exported to depending projects, to be more cmake-like ${PROJECT_NAME}_CPPFLAGS -> ${PROJECT_NAME}_CXX_FLAGS ${PROJECT_NAME}_LDFLAGS -> ${PROJECT_NAME}_LINK_FLAGS also make sure that the exported lists are consistently space-separated (sometimes they might be semicolon separated or mixed) --- cmake/FindPROJECT_NAME.cmake.in.in | 4 ++++ cmake/create_cmake_config_files.cmake | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index c3f6aa8..1b439a2 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -7,6 +7,8 @@ # @PROJECT_NAME@_INCLUDE_DIRS : path to the include directory # @PROJECT_NAME@_LIBRARY_DIRS : path to the library directory # @PROJECT_NAME@_LIBRARIES : the provided libraries +# @PROJECT_NAME@_CXX_FLAGS : additional C++ compiler flags +# @PROJECT_NAME@_LINKER_FLAGS : additional linker flags # # @author Martin Killenberg, DESY # @@ -17,6 +19,8 @@ set(@PROJECT_NAME@_VERSION @@PROJECT_NAME@_SOVERSION@) set(@PROJECT_NAME@_INCLUDE_DIRS @@PROJECT_NAME@_INCLUDE_DIRS@) set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) set(@PROJECT_NAME@_LIBRARIES @@PROJECT_NAME@_LIBRARIES@) +set(@PROJECT_NAME@_CXX_FLAGS @@PROJECT_NAME@_CXX_FLAGS@) +set(@PROJECT_NAME@_LINKER_FLAGS @@PROJECT_NAME@_LINKER_FLAGS@) # Use a macro provided by CMake to check if all the listed arguments are valid and set @PROJECT_NAME@_FOUND accordingly. # This is mainly important to check the version. diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index b1c6e63..9570438 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -8,31 +8,36 @@ # ${PROJECT_NAME}_INCLUDE_DIRS - list include directories needed when compiling against this project # ${PROJECT_NAME}_LIBRARY_DIRS - list of library directories needed when linking against this project # ${PROJECT_NAME}_LIBRARIES - list of libraries needed when linking against this project -# ${PROJECT_NAME}_CPPFLAGS - list of C++ compiler flags when compiling against this project -# ${PROJECT_NAME}_LDFLAGS - list of linker flags when linking against this project +# ${PROJECT_NAME}_CXX_FLAGS - list of additional C++ compiler flags needed when compiling against this project +# ${PROJECT_NAME}_LINKER_FLAGS - list of additional linker flags needed when linking against this project # ####################################################################################################################### # create variables for standard makefiles -set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS}") +set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS}") string(REPLACE " " ";" LIST ${${PROJECT_NAME}_INCLUDE_DIRS}) foreach(INCLUDE_DIR ${LIST}) - set(${PROJECT_NAME}_CPPFLAGS_MAKEFILE "${${PROJECT_NAME}_CPPFLAGS_MAKEFILE} -I${INCLUDE_DIR}") + set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS_MAKEFILE} -I${INCLUDE_DIR}") endforeach() -set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS}") +set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS}") string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARY_DIRS}) foreach(LIBRARY_DIR ${LIST}) - set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS_MAKEFILE} -L${LIBRARY_DIR}") + set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -L${LIBRARY_DIR}") endforeach() string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARIES}) foreach(LIBRARY ${LIST}) - set(${PROJECT_NAME}_LDFLAGS_MAKEFILE "${${PROJECT_NAME}_LDFLAGS_MAKEFILE} -l${LIBRARY}") + set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -l${LIBRARY}") endforeach() +# force the lists to be space separated (may be sometimes semicolon separated) +string(REPLACE ";" " " ${PROJECT_NAME}_INCLUDE_DIRS ${${PROJECT_NAME}_INCLUDE_DIRS}) +string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARY_DIRS ${${PROJECT_NAME}_LIBRARY_DIRS}) +string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES}) + # we have nested @-statements, so we have to parse twice: # create the cmake Find package script From 06cf7a115511a3e47bd51cf6ececc4fbc12b0158 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 15:39:56 +0200 Subject: [PATCH 006/786] added .project to the gitignore list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 378eac2..a6a4af1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build +.project From 61e0dec5e93a02edce30c843f2ffee93cc292eb5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 15:44:29 +0200 Subject: [PATCH 007/786] added support for the --mexflags option. needs still testing --- cmake/PROJECT_NAME-config.in.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/PROJECT_NAME-config.in.in b/cmake/PROJECT_NAME-config.in.in index c97e7c3..7b87713 100755 --- a/cmake/PROJECT_NAME-config.in.in +++ b/cmake/PROJECT_NAME-config.in.in @@ -1,7 +1,12 @@ #!/bin/sh print_usage(){ - echo "usage: $0 [--cppflags] [--ldflags] [--version]" 1>&2 + echo -n "usage: $0 [--version] [--cppflags] [--ldflags]" 1>&2 + if [ -n "@@PROJECT_NAME@_MEXFLAGS@" ]; then + echo " [--mexflags]" 1>&2 + echo -n " The mexflags are only for use with MATLABs mex." 1>&2 + fi + echo "" } if [ $# -eq 0 ]; then @@ -23,6 +28,9 @@ while [ $# -gt 0 ]; do --version) OUTPUT="${OUTPUT}@@PROJECT_NAME@_SOVERSION@" shift;; + --mexflags) + OUTPUT="${OUTPUT}@@PROJECT_NAME@_MEXFLAGS@" + shift;; *) echo "invalid option: $1" 1>&2 print_usage From 2e2be5cb4b8c6e032257196758659373c1ae672d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 15:55:54 +0200 Subject: [PATCH 008/786] fixed using the wrong variable names --- cmake/PROJECT_NAME-config.in.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/PROJECT_NAME-config.in.in b/cmake/PROJECT_NAME-config.in.in index 7b87713..459561e 100755 --- a/cmake/PROJECT_NAME-config.in.in +++ b/cmake/PROJECT_NAME-config.in.in @@ -14,16 +14,16 @@ if [ $# -eq 0 ]; then exit 1 fi -#The variables are all prepared in CMAKE and also provided by +#The variables are all prepared in CMAKE and also provided by #Find@PROJECT_NAME@.cmake, so dependent applications can create consistent #Makefiles. while [ $# -gt 0 ]; do case "$1" in --cppflags) - OUTPUT="${OUTPUT}@@PROJECT_NAME@_CPPFLAGS_MAKEFILE@" + OUTPUT="${OUTPUT}@@PROJECT_NAME@_CXX_FLAGS_MAKEFILE@" shift;; --ldflags) - OUTPUT="${OUTPUT}@@PROJECT_NAME@_LDFLAGS_MAKEFILE@" + OUTPUT="${OUTPUT}@@PROJECT_NAME@_LINKER_FLAGS_MAKEFILE@" shift;; --version) OUTPUT="${OUTPUT}@@PROJECT_NAME@_SOVERSION@" From d4ddd918a5ce016bdbc56e29c9ce5c0a56505b00 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 15:58:00 +0200 Subject: [PATCH 009/786] added @ONLY to configure_file again, since it destroys some code otherwise --- cmake/create_cmake_config_files.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 9570438..cccf5f9 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -41,12 +41,12 @@ string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES}) # we have nested @-statements, so we have to parse twice: # create the cmake Find package script -configure_file(cmake/FindPROJECT_NAME.cmake.in.in "${PROJECT_BINARY_DIR}/cmake/Find${PROJECT_NAME}.cmake.in") -configure_file(${PROJECT_BINARY_DIR}/cmake/Find${PROJECT_NAME}.cmake.in "${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake") +configure_file(cmake/FindPROJECT_NAME.cmake.in.in "${PROJECT_BINARY_DIR}/cmake/Find${PROJECT_NAME}.cmake.in" @ONLY) +configure_file(${PROJECT_BINARY_DIR}/cmake/Find${PROJECT_NAME}.cmake.in "${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake" @ONLY) # create the shell script for standard make files -configure_file(cmake/PROJECT_NAME-config.in.in "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.in") -configure_file(${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.in "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config") +configure_file(cmake/PROJECT_NAME-config.in.in "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.in" @ONLY) +configure_file(${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}-config.in "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config" @ONLY) # install the script install(FILES "${PROJECT_BINARY_DIR}/Find${PROJECT_NAME}.cmake" From bf679dd1a1d18f33610f472e3a8ee370a24c61d3 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 16:14:23 +0200 Subject: [PATCH 010/786] fixed issue with compiler/linker flags being interpreted as lists and thus having semicolons in between --- cmake/FindPROJECT_NAME.cmake.in.in | 6 +++--- cmake/create_cmake_config_files.cmake | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 1b439a2..570821c 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -15,12 +15,12 @@ # Since this file is already part of the installation to be found, the configuration can be hard-coded at # installation time -set(@PROJECT_NAME@_VERSION @@PROJECT_NAME@_SOVERSION@) +set(@PROJECT_NAME@_VERSION "@@PROJECT_NAME@_SOVERSION@") set(@PROJECT_NAME@_INCLUDE_DIRS @@PROJECT_NAME@_INCLUDE_DIRS@) set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) set(@PROJECT_NAME@_LIBRARIES @@PROJECT_NAME@_LIBRARIES@) -set(@PROJECT_NAME@_CXX_FLAGS @@PROJECT_NAME@_CXX_FLAGS@) -set(@PROJECT_NAME@_LINKER_FLAGS @@PROJECT_NAME@_LINKER_FLAGS@) +set(@PROJECT_NAME@_CXX_FLAGS "@@PROJECT_NAME@_CXX_FLAGS@") +set(@PROJECT_NAME@_LINKER_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@") # Use a macro provided by CMake to check if all the listed arguments are valid and set @PROJECT_NAME@_FOUND accordingly. # This is mainly important to check the version. diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index cccf5f9..45f52be 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -10,6 +10,7 @@ # ${PROJECT_NAME}_LIBRARIES - list of libraries needed when linking against this project # ${PROJECT_NAME}_CXX_FLAGS - list of additional C++ compiler flags needed when compiling against this project # ${PROJECT_NAME}_LINKER_FLAGS - list of additional linker flags needed when linking against this project +# ${PROJECT_NAME}_MEXFLAGS - (optional) mex compiler flags # ####################################################################################################################### @@ -34,9 +35,13 @@ foreach(LIBRARY ${LIST}) endforeach() # force the lists to be space separated (may be sometimes semicolon separated) +string(REPLACE ";" " " ${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_SOVERSION}) string(REPLACE ";" " " ${PROJECT_NAME}_INCLUDE_DIRS ${${PROJECT_NAME}_INCLUDE_DIRS}) string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARY_DIRS ${${PROJECT_NAME}_LIBRARY_DIRS}) string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES}) +string(REPLACE ";" " " ${PROJECT_NAME}_CXX_FLAGS ${${PROJECT_NAME}_CXX_FLAGS}) +string(REPLACE ";" " " ${PROJECT_NAME}_LINKER_FLAGS ${${PROJECT_NAME}_LINKER_FLAGS}) +string(REPLACE ";" " " ${PROJECT_NAME}_MEXFLAGS ${${PROJECT_NAME}_MEXFLAGS}) # we have nested @-statements, so we have to parse twice: From ee8e7c9f54507dcc801c7c6e1af5c6348894c9ab Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 16:41:48 +0200 Subject: [PATCH 011/786] imported FindDOOCS script --- cmake/Modules/FindDOOCS.cmake | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 cmake/Modules/FindDOOCS.cmake diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake new file mode 100644 index 0000000..f346382 --- /dev/null +++ b/cmake/Modules/FindDOOCS.cmake @@ -0,0 +1,51 @@ +# +# cmake module for finding DOOCS +# +# By default, only the client API is included. If the component "server" is specified, also the +# server library will be used. +# +# returns: +# DOOCS_FOUND : true or false, depending on whether the package was found +# DOOCS_VERSION : the package version +# DOOCS_INCLUDE_DIRS : path to the include directory +# DOOCS_LIBRARY_DIRS : path to the library directory +# DOOCS_LIBRARIES : list of libraries to link against +# DOOCS_CXX_FLAGS : Flags needed to be passed to the c++ compiler +# DOOCS_LINK_FLAGS : Flags needed to be passed to the linker +# +# @author Martin Hierholzer, DESY +# + +SET(DOOCS_FOUND 0) + +if("${DOOCS_FIND_COMPONENTS}" STREQUAL "server") + FIND_PATH(DOOCS_DIR libEqServer.so + ${CMAKE_CURRENT_LIST_DIR} + /export/doocs/lib + ) + set(DOOCS_LIBRARIES EqServer DOOCSapi nsl dl pthread m rt ldap) +else() + FIND_PATH(DOOCS_DIR libDOOCSapi.so + ${CMAKE_CURRENT_LIST_DIR} + /export/doocs/lib + ) + set(DOOCS_LIBRARIES DOOCSapi nsl dl pthread m rt ldap) +endif() + +# now set the required variables based on the determined DOOCS_DIR +set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) +set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) + +set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG") +set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") + +# extract DOOCS version from librar so symlink. Note: This is platform dependent and only works +# if DOOCS was installed from the Debian pagackes. Find a better version detection scheme! +execute_process(COMMAND bash -c "readlink ${DOOCS_DIR}/libDOOCSapi.so | sed -e 's/^.*libDOOCSapi.so.//' -e 's/-.*$//'" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) + +# use a macro provided by CMake to check if all the listed arguments are valid and set DOOCS_FOUND accordingly +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS + REQUIRED_VARS DOOCS_DIR + VERSION_VAR DOOCS_VERSION ) + From 9c2ac57211a5dc5447867355efab0119407797bb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 16:45:47 +0200 Subject: [PATCH 012/786] removed some unneeded lines --- cmake/create_cmake_config_files.cmake | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 45f52be..1ee11a1 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -34,15 +34,6 @@ foreach(LIBRARY ${LIST}) set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -l${LIBRARY}") endforeach() -# force the lists to be space separated (may be sometimes semicolon separated) -string(REPLACE ";" " " ${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_SOVERSION}) -string(REPLACE ";" " " ${PROJECT_NAME}_INCLUDE_DIRS ${${PROJECT_NAME}_INCLUDE_DIRS}) -string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARY_DIRS ${${PROJECT_NAME}_LIBRARY_DIRS}) -string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES}) -string(REPLACE ";" " " ${PROJECT_NAME}_CXX_FLAGS ${${PROJECT_NAME}_CXX_FLAGS}) -string(REPLACE ";" " " ${PROJECT_NAME}_LINKER_FLAGS ${${PROJECT_NAME}_LINKER_FLAGS}) -string(REPLACE ";" " " ${PROJECT_NAME}_MEXFLAGS ${${PROJECT_NAME}_MEXFLAGS}) - # we have nested @-statements, so we have to parse twice: # create the cmake Find package script From e0aed4a81479446b6e44d475f3780d298a53ddbb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 16:50:00 +0200 Subject: [PATCH 013/786] removed unneeded lines --- cmake/create_cmake_config_files.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 26726b6..0d328b7 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -34,10 +34,6 @@ foreach(LIBRARY ${LIST}) set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -l${LIBRARY}") endforeach() -string(REPLACE ";" " " ${PROJECT_NAME}_INCLUDE_DIRS ${${PROJECT_NAME}_INCLUDE_DIRS}) -string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARY_DIRS ${${PROJECT_NAME}_LIBRARY_DIRS}) -string(REPLACE ";" " " ${PROJECT_NAME}_LIBRARIES ${${PROJECT_NAME}_LIBRARIES}) - # force the lists to be space separated (may be sometimes semicolon separated) # we have nested @-statements, so we have to parse twice: From 1c193b600f9f4563402ac686282f220fd29beda3 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 May 2016 17:04:01 +0200 Subject: [PATCH 014/786] removed unused file (was imported in a merge) --- cmake/Modules/FindDOOCS.cmake | 51 ----------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 cmake/Modules/FindDOOCS.cmake diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake deleted file mode 100644 index f346382..0000000 --- a/cmake/Modules/FindDOOCS.cmake +++ /dev/null @@ -1,51 +0,0 @@ -# -# cmake module for finding DOOCS -# -# By default, only the client API is included. If the component "server" is specified, also the -# server library will be used. -# -# returns: -# DOOCS_FOUND : true or false, depending on whether the package was found -# DOOCS_VERSION : the package version -# DOOCS_INCLUDE_DIRS : path to the include directory -# DOOCS_LIBRARY_DIRS : path to the library directory -# DOOCS_LIBRARIES : list of libraries to link against -# DOOCS_CXX_FLAGS : Flags needed to be passed to the c++ compiler -# DOOCS_LINK_FLAGS : Flags needed to be passed to the linker -# -# @author Martin Hierholzer, DESY -# - -SET(DOOCS_FOUND 0) - -if("${DOOCS_FIND_COMPONENTS}" STREQUAL "server") - FIND_PATH(DOOCS_DIR libEqServer.so - ${CMAKE_CURRENT_LIST_DIR} - /export/doocs/lib - ) - set(DOOCS_LIBRARIES EqServer DOOCSapi nsl dl pthread m rt ldap) -else() - FIND_PATH(DOOCS_DIR libDOOCSapi.so - ${CMAKE_CURRENT_LIST_DIR} - /export/doocs/lib - ) - set(DOOCS_LIBRARIES DOOCSapi nsl dl pthread m rt ldap) -endif() - -# now set the required variables based on the determined DOOCS_DIR -set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) -set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) - -set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG") -set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") - -# extract DOOCS version from librar so symlink. Note: This is platform dependent and only works -# if DOOCS was installed from the Debian pagackes. Find a better version detection scheme! -execute_process(COMMAND bash -c "readlink ${DOOCS_DIR}/libDOOCSapi.so | sed -e 's/^.*libDOOCSapi.so.//' -e 's/-.*$//'" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) - -# use a macro provided by CMake to check if all the listed arguments are valid and set DOOCS_FOUND accordingly -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS - REQUIRED_VARS DOOCS_DIR - VERSION_VAR DOOCS_VERSION ) - From 9d3428c8292762647f418edb190391389eed35c6 Mon Sep 17 00:00:00 2001 From: killenb Date: Sat, 28 May 2016 13:16:41 +0200 Subject: [PATCH 015/786] added doc/html to gitignre --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a6a4af1..ac8b046 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build .project +doc/html/ From 2434f0241ef3799eeae6625a6de6fadbc89647e1 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 31 May 2016 19:19:52 +0200 Subject: [PATCH 016/786] Use FindLibrary() cmake command to properly locate the library, even if the directory is already known. This is needed to get the proper full path reference to the library. --- cmake/FindPROJECT_NAME.cmake.in.in | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 570821c..66a8760 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -6,19 +6,27 @@ # @PROJECT_NAME@_VERSION : the package version # @PROJECT_NAME@_INCLUDE_DIRS : path to the include directory # @PROJECT_NAME@_LIBRARY_DIRS : path to the library directory -# @PROJECT_NAME@_LIBRARIES : the provided libraries +# @PROJECT_NAME@_LIBRARIES : additional libraries the depending project needs to link against. The library provided +# by the package @PROJECT_NAME@ will be added automatically # @PROJECT_NAME@_CXX_FLAGS : additional C++ compiler flags # @PROJECT_NAME@_LINKER_FLAGS : additional linker flags # # @author Martin Killenberg, DESY # +# The library itself must be "searched" using the FIND_LIBRARY command in the known install directory, to set +# the variable properly +FIND_LIBRARY(@PROJECT_NAME@_LIBRARY @PROJECT_NAME@ + @CMAKE_INSTALL_PREFIX@/lib + NO_DEFAULT_PATH +) + # Since this file is already part of the installation to be found, the configuration can be hard-coded at # installation time set(@PROJECT_NAME@_VERSION "@@PROJECT_NAME@_SOVERSION@") set(@PROJECT_NAME@_INCLUDE_DIRS @@PROJECT_NAME@_INCLUDE_DIRS@) set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) -set(@PROJECT_NAME@_LIBRARIES @@PROJECT_NAME@_LIBRARIES@) +set(@PROJECT_NAME@_LIBRARIES ${@PROJECT_NAME@_LIBRARY} @@PROJECT_NAME@_LIBRARIES@) set(@PROJECT_NAME@_CXX_FLAGS "@@PROJECT_NAME@_CXX_FLAGS@") set(@PROJECT_NAME@_LINKER_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@") From 558966cbe91fd6cfc464c39499e2314d3fb8b057 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 2 Jun 2016 10:17:15 +0200 Subject: [PATCH 017/786] removed @PROJECT_NAME@_LIBRARIES from the required variables since the package might not provide a library --- cmake/FindPROJECT_NAME.cmake.in.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 66a8760..a16d439 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -34,6 +34,5 @@ set(@PROJECT_NAME@_LINKER_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@") # This is mainly important to check the version. set(@PROJECT_NAME@_FOUND 0) include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_LIBRARIES @PROJECT_NAME@_INCLUDE_DIRS - VERSION_VAR @PROJECT_NAME@_VERSION ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_INCLUDE_DIRS VERSION_VAR @PROJECT_NAME@_VERSION ) From 9b32e88b54987fc15550008962e3fb10ed9174fe Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 2 Jun 2016 10:35:16 +0200 Subject: [PATCH 018/786] updated the usage description --- cmake/FindPROJECT_NAME.cmake.in.in | 3 +-- cmake/create_cmake_config_files.cmake | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index a16d439..0c6dee4 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -6,8 +6,7 @@ # @PROJECT_NAME@_VERSION : the package version # @PROJECT_NAME@_INCLUDE_DIRS : path to the include directory # @PROJECT_NAME@_LIBRARY_DIRS : path to the library directory -# @PROJECT_NAME@_LIBRARIES : additional libraries the depending project needs to link against. The library provided -# by the package @PROJECT_NAME@ will be added automatically +# @PROJECT_NAME@_LIBRARIES : libraries the depending project needs to link against # @PROJECT_NAME@_CXX_FLAGS : additional C++ compiler flags # @PROJECT_NAME@_LINKER_FLAGS : additional linker flags # diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 1ee11a1..ab4176b 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -4,10 +4,11 @@ # Create the Find${PROJECT_NAME}.cmake cmake macro and the ${PROJECT_NAME}-config shell script and installs them. # # Expects the following input variables: -# ${PROJECT_NAME}_SOVERSION - version of the .so library file +# ${PROJECT_NAME}_SOVERSION - version of the .so library file (or just MAJOR.MINOR without the patch level) # ${PROJECT_NAME}_INCLUDE_DIRS - list include directories needed when compiling against this project # ${PROJECT_NAME}_LIBRARY_DIRS - list of library directories needed when linking against this project -# ${PROJECT_NAME}_LIBRARIES - list of libraries needed when linking against this project +# ${PROJECT_NAME}_LIBRARIES - list of additional libraries needed when linking against this project. The library +# provided by the project will be added automatically # ${PROJECT_NAME}_CXX_FLAGS - list of additional C++ compiler flags needed when compiling against this project # ${PROJECT_NAME}_LINKER_FLAGS - list of additional linker flags needed when linking against this project # ${PROJECT_NAME}_MEXFLAGS - (optional) mex compiler flags From 895db14fed69cfb266e63dde592010a5b04f7f2d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 2 Jun 2016 10:38:03 +0200 Subject: [PATCH 019/786] added a variable @PROJECT_NAME@_PREFIX and use it as the required variable to make a nicer output when depending projects find this package --- cmake/FindPROJECT_NAME.cmake.in.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 0c6dee4..0b67598 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -28,10 +28,11 @@ set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) set(@PROJECT_NAME@_LIBRARIES ${@PROJECT_NAME@_LIBRARY} @@PROJECT_NAME@_LIBRARIES@) set(@PROJECT_NAME@_CXX_FLAGS "@@PROJECT_NAME@_CXX_FLAGS@") set(@PROJECT_NAME@_LINKER_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@") +set(@PROJECT_NAME@_PREFIX "@CMAKE_INSTALL_PREFIX@") # Use a macro provided by CMake to check if all the listed arguments are valid and set @PROJECT_NAME@_FOUND accordingly. # This is mainly important to check the version. set(@PROJECT_NAME@_FOUND 0) include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_INCLUDE_DIRS VERSION_VAR @PROJECT_NAME@_VERSION ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION ) From 5b03518c998a044708218e4125a8cd212d5fba22 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 2 Jun 2016 16:28:39 +0200 Subject: [PATCH 020/786] fix for depending Makefile projects: do not add -l option to full-path library names --- cmake/create_cmake_config_files.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index ab4176b..d52ec92 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -32,7 +32,11 @@ endforeach() string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARIES}) foreach(LIBRARY ${LIST}) - set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -l${LIBRARY}") + if(LIBRARY MATCHES "/") # library name contains slashes: link against the a file path name + set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} ${LIBRARY}") + else() # library name does not contain slashes: link against library with -l option + set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -l${LIBRARY}") + endif() endforeach() # we have nested @-statements, so we have to parse twice: From 1f18ae642b8df822388bbe5e60d0ba3ea7cf8796 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 2 Jun 2016 16:36:13 +0200 Subject: [PATCH 021/786] updated documentation, especially added a warning not to modify these files inside a project --- cmake/FindPROJECT_NAME.cmake.in.in | 14 ++++++++++++++ cmake/Makefile.coverage.in | 18 ++++++++++++++++++ cmake/Modules/FindDOOCS.cmake | 14 ++++++++++++++ cmake/PROJECT_NAME-config.in.in | 23 ++++++++++++++++++++--- cmake/create_cmake_config_files.cmake | 12 ++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 0b67598..b614b0f 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -1,3 +1,4 @@ +####################################################################################################################### # # cmake module for finding @PROJECT_NAME@ # @@ -12,6 +13,19 @@ # # @author Martin Killenberg, DESY # +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### # The library itself must be "searched" using the FIND_LIBRARY command in the known install directory, to set # the variable properly diff --git a/cmake/Makefile.coverage.in b/cmake/Makefile.coverage.in index 79b08d3..afda667 100644 --- a/cmake/Makefile.coverage.in +++ b/cmake/Makefile.coverage.in @@ -1,3 +1,21 @@ +####################################################################################################################### +# +# Additional make target for generating a code coverage report. +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + coverage: rm -f `find . -name "*\.gcda"` make test diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index f346382..3694694 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -1,3 +1,4 @@ +####################################################################################################################### # # cmake module for finding DOOCS # @@ -15,6 +16,19 @@ # # @author Martin Hierholzer, DESY # +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### SET(DOOCS_FOUND 0) diff --git a/cmake/PROJECT_NAME-config.in.in b/cmake/PROJECT_NAME-config.in.in index 459561e..e3feb03 100755 --- a/cmake/PROJECT_NAME-config.in.in +++ b/cmake/PROJECT_NAME-config.in.in @@ -1,4 +1,21 @@ #!/bin/sh +####################################################################################################################### +# +# Shell script to output compiler and linker flags for use in plain Makefiles or at command line. +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### print_usage(){ echo -n "usage: $0 [--version] [--cppflags] [--ldflags]" 1>&2 @@ -14,9 +31,9 @@ if [ $# -eq 0 ]; then exit 1 fi -#The variables are all prepared in CMAKE and also provided by -#Find@PROJECT_NAME@.cmake, so dependent applications can create consistent -#Makefiles. +# The variables are all prepared in CMAKE and also provided by +# Find@PROJECT_NAME@.cmake, so dependent applications can create consistent +# Makefiles. while [ $# -gt 0 ]; do case "$1" in --cppflags) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index d52ec92..72135c6 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -15,6 +15,18 @@ # ####################################################################################################################### +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + # create variables for standard makefiles set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS}") From c72792bf52c607bea21e350a9d3d5a9b543a5f8f Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 10 Jun 2016 16:36:08 +0200 Subject: [PATCH 022/786] added ProcessVariableAccessor and ProcessScalarAccessor. Not used anywhere yet, no tests, not compiled. Just a first version. --- include/ProcessScalarAccessor.h | 59 +++++++++++++ include/ProcessVariableAccessor.h | 137 ++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 include/ProcessScalarAccessor.h create mode 100644 include/ProcessVariableAccessor.h diff --git a/include/ProcessScalarAccessor.h b/include/ProcessScalarAccessor.h new file mode 100644 index 0000000..9a64643 --- /dev/null +++ b/include/ProcessScalarAccessor.h @@ -0,0 +1,59 @@ +#ifndef MTCA4U_PROCESS_SCALAR_ACCESSOR_H +#define MTCA4U_PROCESS_SCALAR_ACCESSOR_H + +#include "ProcessScalar.h" + +namespace mtca4u { + + template + class ProcessScalarAccessor: public ProcessVariableAccessor{ + public: + ProcessScalarAccessor(ProcessScalar::SharedPtr & processScalar) + : _processScalar(processScalar){ + } + + /** + * Assign the content of the template type. This operator behaves like + * set(T). + */ + ProcessScalarAccessor & operator=(T const & t) { + processScalar->set(t); + return *this; + } + + /** + * Set the value of this process variable to the one of the other process + * variable. + */ + void set(ProcessScalar const & other){ + processScalar->set(other); + } + + /** + * Set the value of this process variable to the specified one. + */ + void set(T const & t){ + processScalar->set(t); + } + + /** + * Automatic conversion operator which returns a \b copy of this process + * variable's value. As no reference is returned, this cannot be used for + * assignment. + */ + operator T() const{ + return processScalar->get(); + } + + /** + * Returns a \b copy of this process variable's value. As no reference is + * returned, this cannot be used for assignment. + */ + virtual T get() const{ + return processScalar->get(); + } +}; + +} + +#endif // MTCA4U_PROCESS_SCALAR_ACCESSOR_H diff --git a/include/ProcessVariableAccessor.h b/include/ProcessVariableAccessor.h new file mode 100644 index 0000000..f989b19 --- /dev/null +++ b/include/ProcessVariableAccessor.h @@ -0,0 +1,137 @@ +#ifndef MTCA4U_PROCESS_VARIABLE_ACCESSOR_H +#define MTCA4U_PROCESS_VARIABLE_ACCESSOR_H + +namespace mtca4u { + public: + /** + * Returns the name that identifies the process variable. + */ + const std::string& getName() const{ + return _impl->getName(); + } + + /** + * Returns the {@link std::type_info} for the value type of this process + * variable or array. This can be used to determine the type of a process + * variable at runtime. + */ + const std::type_info& getValueType() const{ + return _impl->getValueType(); + } + + /** + * Returns true if the implementation is an instance of + * {@link ProcessArray} and false if this object is an + * instance of {@link ProcessScalar}. + */ + bool isArray() const{ + return _impl->isArray(); + } + + /** + * Returns true if this object represents the receiver in a + * sender / receiver pair, false otherwise. + */ + virtual bool isReceiver() const{ + return _impl->isReceiver(); + } + + /** + * Returns true if this object represents the sender in a + * sender / receiver pair, false otherwise. + */ + bool isSender() const{ + return _impl->isSender(); + } + + /** + * Returns the time stamp associated with the current value of the process + * variable. Typically, this is the time when the value was updated. + */ + TimeStamp getTimeStamp() const{ + return _impl->getTimeStamp(); + } + + /** + * Receives a new value from the sender if this process variable is a + * receiver. Returns true if a new value has been received and + * false if there is no new value available. + * + * Throws an exception if this process variable is not a receiver. + */ + bool receive(){ + return _impl->receive(); + } + + /** + * Sends the current value to the receiver if this process variable is a + * sender. Returns true if an empty buffer was available and + * false if no empty buffer was available and thus a previously + * sent value has been dropped in order to send the current value. + * + * Throws an exception if this process variable is not a sender. + */ + bool send(){ + return _impl->send(); + } + + /** + * Replaces the implementation that is used under the hood. Use this function instead of + * a assignment operator. The assignment operator was turned off to avoid confusion with + * assigning the content of the other process variable, which is accessed by set. + */ + void replace(ProcessScalarAccessor const & other){ + replaceImpl( other.impl ); + } + + private: + + /** The replace impl to avoid code duplication. Requires impl to be valid. + */ + void replaceImpl(ProcessScalar::SharedPtr const & otherImpl){ + // current safety check + // FIXME: Do we want this in future when the accessors childen + // should work with all kinds of impl? Just one impl? + if ( (_impl->isArray() != other.impl->isArray() ) || + (_impl->getValueType() != other.impl->getValueType() ) ){ + throw std::runtime_error("ProcessScalarAccessors can only be replaced by accessors of the +same type"); + } + _impl = otherImpl; + } + + /** + * Replace function which direktly takes a ProcessScalar (sharedPtr). Can be used + * to initalise an accessor which has been created with the default constuctor of the child + * (empty shared ptr). + */ + void replace(ProcessScalar::SharedPrt const & impl){ + if (_impl){ // my current impl exists. replace with safety check + replaceImpl( impl ); + }else{ // I don't have an impl yet, just assign it. + _impl = other.impl; + } + } + + protected: + ProcessVariable::SharedPtr _impl; + + /** + */ + class ProcessVariableAccessor{ + ProcessVariableAccessor(ProcessVariable::SharedPtr & impl) + : _impl(impl){ + } + + private: + /** + * The assignment operator is intentionally turned off. Also derrieved classes cannot + * access it. It intentionally is not implemented. Use replace() instead to replace the impl, + * and set() to replace the content. + */ + ProcessScalarAccessor & operator=(ProcessScalarAccessor const & other); + }; + +} //namespace mtca4u + +#endif // MTCA4U_PROCESS_VARIABLE_ACCESSOR_H From b7483af93e253b28ec7d2dacda24307b7b2b0780 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 11 Jun 2016 12:22:24 +0200 Subject: [PATCH 023/786] first compiling and linking version of ScalarAccessor test. Empty instance, nothing tested yet --- include/ProcessScalarAccessor.h | 26 +++++++++++++++++--------- include/ProcessVariableAccessor.h | 30 ++++++++++++++++-------------- tests/src/testPVAccessors.cpp | 17 +++++++++++++++++ 3 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 tests/src/testPVAccessors.cpp diff --git a/include/ProcessScalarAccessor.h b/include/ProcessScalarAccessor.h index 9a64643..59ab03f 100644 --- a/include/ProcessScalarAccessor.h +++ b/include/ProcessScalarAccessor.h @@ -2,14 +2,21 @@ #define MTCA4U_PROCESS_SCALAR_ACCESSOR_H #include "ProcessScalar.h" +#include "ProcessVariableAccessor.h" namespace mtca4u { template class ProcessScalarAccessor: public ProcessVariableAccessor{ + + // protected: + // typename ProcessScalar::SharedPtr & _processScalar; + public: - ProcessScalarAccessor(ProcessScalar::SharedPtr & processScalar) - : _processScalar(processScalar){ + ProcessScalarAccessor(typename ProcessScalar::SharedPtr const & processScalar = + typename ProcessScalar::SharedPtr()) + : ProcessVariableAccessor(processScalar){ + // _processScalar( boost::static_pointer_cast< ProcessScalar >( _impl ) ){ } /** @@ -17,23 +24,23 @@ namespace mtca4u { * set(T). */ ProcessScalarAccessor & operator=(T const & t) { - processScalar->set(t); + boost::static_pointer_cast< ProcessScalar >(_impl)->set(t); return *this; } - + /** * Set the value of this process variable to the one of the other process * variable. */ void set(ProcessScalar const & other){ - processScalar->set(other); + boost::static_pointer_cast< ProcessScalar >(_impl)->set(other); } /** * Set the value of this process variable to the specified one. */ void set(T const & t){ - processScalar->set(t); + boost::static_pointer_cast< ProcessScalar >(_impl)->set(t); } /** @@ -42,7 +49,7 @@ namespace mtca4u { * assignment. */ operator T() const{ - return processScalar->get(); + return boost::static_pointer_cast< ProcessScalar >(_impl)->get(); } /** @@ -50,9 +57,10 @@ namespace mtca4u { * returned, this cannot be used for assignment. */ virtual T get() const{ - return processScalar->get(); + return boost::static_pointer_cast< ProcessScalar >(_impl)->get(); } -}; + + }; } diff --git a/include/ProcessVariableAccessor.h b/include/ProcessVariableAccessor.h index f989b19..3031b33 100644 --- a/include/ProcessVariableAccessor.h +++ b/include/ProcessVariableAccessor.h @@ -1,8 +1,12 @@ #ifndef MTCA4U_PROCESS_VARIABLE_ACCESSOR_H #define MTCA4U_PROCESS_VARIABLE_ACCESSOR_H +#include "ProcessVariable.h" + namespace mtca4u { - public: + + class ProcessVariableAccessor{ + public: /** * Returns the name that identifies the process variable. */ @@ -80,36 +84,35 @@ namespace mtca4u { * a assignment operator. The assignment operator was turned off to avoid confusion with * assigning the content of the other process variable, which is accessed by set. */ - void replace(ProcessScalarAccessor const & other){ - replaceImpl( other.impl ); + void replace(ProcessVariableAccessor const & other){ + replaceImpl( other._impl ); } private: /** The replace impl to avoid code duplication. Requires impl to be valid. */ - void replaceImpl(ProcessScalar::SharedPtr const & otherImpl){ + void replaceImpl(ProcessVariable::SharedPtr const & otherImpl){ // current safety check // FIXME: Do we want this in future when the accessors childen // should work with all kinds of impl? Just one impl? - if ( (_impl->isArray() != other.impl->isArray() ) || - (_impl->getValueType() != other.impl->getValueType() ) ){ - throw std::runtime_error("ProcessScalarAccessors can only be replaced by accessors of the -same type"); + if ( (_impl->isArray() != otherImpl->isArray() ) || + (_impl->getValueType() != otherImpl->getValueType() ) ){ + throw std::runtime_error("ProcessVariableAccessors can only be replaced by accessors of the same type"); } _impl = otherImpl; } /** - * Replace function which direktly takes a ProcessScalar (sharedPtr). Can be used + * Replace function which direktly takes a ProcessVariable (sharedPtr). Can be used * to initalise an accessor which has been created with the default constuctor of the child * (empty shared ptr). */ - void replace(ProcessScalar::SharedPrt const & impl){ + void replace(ProcessVariable::SharedPtr const & impl){ if (_impl){ // my current impl exists. replace with safety check replaceImpl( impl ); }else{ // I don't have an impl yet, just assign it. - _impl = other.impl; + _impl = impl; } } @@ -118,8 +121,7 @@ same type"); /** */ - class ProcessVariableAccessor{ - ProcessVariableAccessor(ProcessVariable::SharedPtr & impl) + ProcessVariableAccessor(ProcessVariable::SharedPtr const & impl) : _impl(impl){ } @@ -129,7 +131,7 @@ same type"); * access it. It intentionally is not implemented. Use replace() instead to replace the impl, * and set() to replace the content. */ - ProcessScalarAccessor & operator=(ProcessScalarAccessor const & other); + ProcessVariableAccessor & operator=(ProcessVariableAccessor const & other); }; } //namespace mtca4u diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp new file mode 100644 index 0000000..2a116cd --- /dev/null +++ b/tests/src/testPVAccessors.cpp @@ -0,0 +1,17 @@ +#define BOOST_TEST_MODULE PvAcessorsTest +// Only after defining the name include the unit test header. +#include + +#include "ProcessScalarAccessor.h" + +using namespace mtca4u; + +BOOST_AUTO_TEST_SUITE( PvAccessorsTestSuite ) + +BOOST_AUTO_TEST_CASE( testScalarCreation ){ + // checking that the default constructor is working + ProcessScalarAccessor scalarIntAccessor; + +} + +BOOST_AUTO_TEST_SUITE_END() From b46dc5f20e54326ffdeebcdda9f639988d43b895 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 11 Jun 2016 14:02:18 +0200 Subject: [PATCH 024/786] testing all creating and initialisation of the scalar accessor --- include/ProcessVariableAccessor.h | 1 + tests/src/testPVAccessors.cpp | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/ProcessVariableAccessor.h b/include/ProcessVariableAccessor.h index 3031b33..6155257 100644 --- a/include/ProcessVariableAccessor.h +++ b/include/ProcessVariableAccessor.h @@ -103,6 +103,7 @@ namespace mtca4u { _impl = otherImpl; } + public: /** * Replace function which direktly takes a ProcessVariable (sharedPtr). Can be used * to initalise an accessor which has been created with the default constuctor of the child diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 2a116cd..a105ff3 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -9,9 +9,29 @@ using namespace mtca4u; BOOST_AUTO_TEST_SUITE( PvAccessorsTestSuite ) BOOST_AUTO_TEST_CASE( testScalarCreation ){ + // These tests are just to check that they are working + // checking that the default constructor is working ProcessScalarAccessor scalarIntAccessor; + // There are two ways to create. Just run them both: + auto firstInt = createSimpleProcessScalar("first",42); + scalarIntAccessor.replace( firstInt ); + + BOOST_CHECK( scalarIntAccessor.get() == 42 ); + + auto secondInt = createSimpleProcessScalar("second",43); + ProcessScalarAccessor secondIntAccessor(secondInt); + + BOOST_CHECK( secondIntAccessor.get() == 43 ); + + scalarIntAccessor.replace( secondIntAccessor ); + + BOOST_CHECK( scalarIntAccessor.get() == 43 ); + // both must point to the same impl (shared prt pointing to the same instance + + scalarIntAccessor.set(44); + BOOST_CHECK( secondIntAccessor.get() == 44 ); } BOOST_AUTO_TEST_SUITE_END() From ba7013a2485827818447d92942687a26ef17a57a Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 11 Jun 2016 14:09:32 +0200 Subject: [PATCH 025/786] testing throwing if the wrong type is replaced --- tests/src/testPVAccessors.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index a105ff3..fc9a103 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -3,6 +3,7 @@ #include #include "ProcessScalarAccessor.h" +#include "ProcessArray.h" using namespace mtca4u; @@ -32,6 +33,17 @@ BOOST_AUTO_TEST_CASE( testScalarCreation ){ scalarIntAccessor.set(44); BOOST_CHECK( secondIntAccessor.get() == 44 ); + + auto intArray = createSimpleProcessArray(10, "intArray"); + BOOST_CHECK_THROW( secondIntAccessor.replace(intArray), std::runtime_error) ; + + auto scalarFloat = createSimpleProcessScalar("scalarFloat"); + BOOST_CHECK_THROW( secondIntAccessor.replace(scalarFloat), std::runtime_error) ; +} + +BOOST_AUTO_TEST_CASE( testPVAccessor ){ + // testing the base class. + } BOOST_AUTO_TEST_SUITE_END() From 1736ffc985828f8bbeb3f760a40c20c0e41334f3 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 11 Jun 2016 15:25:01 +0200 Subject: [PATCH 026/786] complete test of ProcessVariableAccessor --- include/ManualTimeStampSource.h | 24 +++++++++++++++++++++ tests/src/testPVAccessors.cpp | 37 +++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 include/ManualTimeStampSource.h diff --git a/include/ManualTimeStampSource.h b/include/ManualTimeStampSource.h new file mode 100644 index 0000000..75164c0 --- /dev/null +++ b/include/ManualTimeStampSource.h @@ -0,0 +1,24 @@ +#ifndef MTCA4U_MANUAL_TIME_STAMP_SOURCE_H +#define MTCA4U_MANUAL_TIME_STAMP_SOURCE_H + +namespace mtca4u { + + /** The code manually has to set a time stamp, which is then disctributed + * to the connected PVs. + */ + class ManualTimeStampSource : public TimeStampSource{ + public: + void setTimeStamp( TimeStamp timeStamp ){ + _timeStamp = timeStamp; + } + + TimeStamp getCurrentTimeStamp(){ + return _timeStamp; + } + + protected: + TimeStamp _timeStamp; + }; +} + +#endif // MTCA4U_MANUAL_TIME_STAMP_SOURCE_H diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index fc9a103..222aab0 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -4,6 +4,7 @@ #include "ProcessScalarAccessor.h" #include "ProcessArray.h" +#include "ManualTimeStampSource.h" using namespace mtca4u; @@ -42,8 +43,40 @@ BOOST_AUTO_TEST_CASE( testScalarCreation ){ } BOOST_AUTO_TEST_CASE( testPVAccessor ){ - // testing the base class. - + // testing the base class. We are mainly testing sending and receiving, so + // we need a pair + auto myTimeStampSource = boost::make_shared(); + TimeStamp testTimeStamp; + testTimeStamp.seconds = 123; + myTimeStampSource->setTimeStamp(testTimeStamp); + + auto senderReceiverPair = createSynchronizedProcessScalar("anInt", + 0 /*start value*/, 1 /* nBuffers */, myTimeStampSource ); + + ProcessScalarAccessor scalarSender(senderReceiverPair.first); + ProcessScalarAccessor scalarReceiver(senderReceiverPair.second); + + ProcessVariableAccessor & sender = scalarSender; + ProcessVariableAccessor & receiver = scalarReceiver; + + BOOST_CHECK( sender.getName() == "anInt" ); + BOOST_CHECK( sender.getValueType() == typeid(int) ); + BOOST_CHECK( sender.isArray() == false ); + + BOOST_CHECK( sender.isReceiver() == false ); + BOOST_CHECK( sender.isSender() == true ); + BOOST_CHECK( receiver.isReceiver() == true ); + BOOST_CHECK( receiver.isSender() == false ); + + BOOST_CHECK( !(sender.getTimeStamp() == testTimeStamp) ); + BOOST_CHECK( scalarReceiver != 2 ); + scalarSender.set(2); + sender.send(); + BOOST_CHECK( sender.getTimeStamp() == testTimeStamp ); + receiver.receive(); + BOOST_CHECK( receiver.getTimeStamp() == testTimeStamp ); + BOOST_CHECK( scalarReceiver == 2 ); + } BOOST_AUTO_TEST_SUITE_END() From 76758e9698b68d912e32d21cc15215d53a432e8f Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 12 Jun 2016 01:12:39 +0200 Subject: [PATCH 027/786] full test coverage of the scalar accessor --- include/ProcessScalarAccessor.h | 8 +++++++ tests/src/testPVAccessors.cpp | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/include/ProcessScalarAccessor.h b/include/ProcessScalarAccessor.h index 59ab03f..ca97b37 100644 --- a/include/ProcessScalarAccessor.h +++ b/include/ProcessScalarAccessor.h @@ -36,6 +36,14 @@ namespace mtca4u { boost::static_pointer_cast< ProcessScalar >(_impl)->set(other); } + /** + * Set the value of this process variable to the one of the other process + * variable accessor + */ + void set(ProcessScalarAccessor const & other){ + set(other.get()); + } + /** * Set the value of this process variable to the specified one. */ diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 222aab0..87fa47b 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -40,6 +40,11 @@ BOOST_AUTO_TEST_CASE( testScalarCreation ){ auto scalarFloat = createSimpleProcessScalar("scalarFloat"); BOOST_CHECK_THROW( secondIntAccessor.replace(scalarFloat), std::runtime_error) ; + + // test copy construction (automatically created, but it's a feature we want + ProcessScalarAccessor thirdIntAccessor( secondIntAccessor ); + secondIntAccessor.set( 46 ); + BOOST_CHECK( thirdIntAccessor.get() == 46 ); } BOOST_AUTO_TEST_CASE( testPVAccessor ){ @@ -79,4 +84,36 @@ BOOST_AUTO_TEST_CASE( testPVAccessor ){ } +BOOST_AUTO_TEST_CASE( testScalarOperations ){ + // test getters, setters and operators + auto scalarInt = createSimpleProcessScalar("scalarInt",42); + ProcessScalarAccessor scalarIntAccessor(scalarInt); + + // implicit conversion + int i; + i = scalarIntAccessor; + BOOST_CHECK( i == 42 ); + + // assigment + i = 43; + scalarIntAccessor = i; + // we test with the underlying impl + BOOST_CHECK( scalarInt->get() == 43 ); + // now we can also test the get function + BOOST_CHECK( scalarIntAccessor.get() == 43 ); + + auto anotherScalarInt = createSimpleProcessScalar("anotherScalarInt",44); + scalarIntAccessor.set(*anotherScalarInt); + BOOST_CHECK( scalarIntAccessor.get() == 44 ); + scalarIntAccessor.set( 45 ); + BOOST_CHECK( scalarIntAccessor.get() == 45 ); + + ProcessScalarAccessor anotherScalarIntAccessor(anotherScalarInt); + anotherScalarIntAccessor.set(46); + scalarIntAccessor.set( anotherScalarIntAccessor ); + BOOST_CHECK( scalarIntAccessor.get() == 46 ); + +} + + BOOST_AUTO_TEST_SUITE_END() From 59f30d988fd5d52a3035b441ab67bfd4da402e03 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 12 Jun 2016 17:47:55 +0200 Subject: [PATCH 028/786] first, not really useful version of the arrayAccessor. Only has get, operators still missing --- .gitignore | 1 + include/ProcessArrayAccessor.h | 26 +++++++++++++++++++ include/ProcessScalarAccessor.h | 2 +- tests/src/testPVAccessors.cpp | 46 ++++++++++++++++++++++++++++++--- 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 include/ProcessArrayAccessor.h diff --git a/.gitignore b/.gitignore index ac8b046..ef6f953 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build .project doc/html/ +*~ diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h new file mode 100644 index 0000000..9747a5c --- /dev/null +++ b/include/ProcessArrayAccessor.h @@ -0,0 +1,26 @@ +#ifndef MTCA4U_PROCESS_ARRAY_ACCESSOR_H +#define MTCA4U_PROCESS_ARRAY_ACCESSOR_H + +#include "ProcessArray.h" +#include "ProcessVariableAccessor.h" + +namespace mtca4u{ + template + class ProcessArrayAccessor: public ProcessVariableAccessor{ + + public: + ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = + typename ProcessArray::SharedPtr()) + : ProcessVariableAccessor(processArray){ + // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ + } + + std::vector & get(){ + return boost::static_pointer_cast< ProcessArray >(_impl)->get(); + } + + }; + +} //namespace mtca4u + +#endif // MTCA4U_PROCESS_ARRAY_ACCESSOR_H diff --git a/include/ProcessScalarAccessor.h b/include/ProcessScalarAccessor.h index ca97b37..e1e1c9f 100644 --- a/include/ProcessScalarAccessor.h +++ b/include/ProcessScalarAccessor.h @@ -64,7 +64,7 @@ namespace mtca4u { * Returns a \b copy of this process variable's value. As no reference is * returned, this cannot be used for assignment. */ - virtual T get() const{ + T get() const{ return boost::static_pointer_cast< ProcessScalar >(_impl)->get(); } diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 87fa47b..dd3457e 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -3,7 +3,7 @@ #include #include "ProcessScalarAccessor.h" -#include "ProcessArray.h" +#include "ProcessArrayAccessor.h" #include "ManualTimeStampSource.h" using namespace mtca4u; @@ -11,8 +11,6 @@ using namespace mtca4u; BOOST_AUTO_TEST_SUITE( PvAccessorsTestSuite ) BOOST_AUTO_TEST_CASE( testScalarCreation ){ - // These tests are just to check that they are working - // checking that the default constructor is working ProcessScalarAccessor scalarIntAccessor; @@ -112,8 +110,48 @@ BOOST_AUTO_TEST_CASE( testScalarOperations ){ anotherScalarIntAccessor.set(46); scalarIntAccessor.set( anotherScalarIntAccessor ); BOOST_CHECK( scalarIntAccessor.get() == 46 ); - } +BOOST_AUTO_TEST_CASE( testArrayCreation ){ + // checking that the default constructor is working + ProcessArrayAccessor arrayIntAccessor; + + // There are two ways to create. Just run them both: + auto firstIntArray = createSimpleProcessArray(10,"first", 42); + arrayIntAccessor.replace( firstIntArray ); + + BOOST_CHECK( arrayIntAccessor.get().size() == 10 ); + BOOST_CHECK( arrayIntAccessor.get()[0] == 42 ); + + auto secondIntArray = createSimpleProcessArray(11,"second",43); + ProcessArrayAccessor secondIntArrayAccessor(secondIntArray); + + BOOST_CHECK( secondIntArrayAccessor.get().size() == 11 ); + BOOST_CHECK( secondIntArrayAccessor.get()[0] == 43 ); + + arrayIntAccessor.replace( secondIntArrayAccessor ); + + BOOST_CHECK( arrayIntAccessor.get().size() == 11 ); + BOOST_CHECK( arrayIntAccessor.get()[0] == 43 ); + // both must point to the same impl (shared prt pointing to the same instance + + for (auto & i : arrayIntAccessor.get()){ + i = 44; + } + BOOST_CHECK( secondIntArrayAccessor.get()[0] == 44 ); + + auto scalarInt = createSimpleProcessScalar("scalarInt"); + BOOST_CHECK_THROW( secondIntArrayAccessor.replace(scalarInt), std::runtime_error) ; + + auto arrayFloat = createSimpleProcessArray(10, "arrayFloat"); + BOOST_CHECK_THROW( secondIntArrayAccessor.replace(arrayFloat), std::runtime_error) ; + + // test copy construction (automatically created, but it's a feature we want + ProcessArrayAccessor thirdIntArrayAccessor( secondIntArrayAccessor ); + for (auto & i : secondIntArrayAccessor.get()){ + i = 46; + } + BOOST_CHECK( thirdIntArrayAccessor.get()[0] == 46 ); +} BOOST_AUTO_TEST_SUITE_END() From 3f0861170277a67910e087d0a8523a6eb4a38b6f Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 12 Jun 2016 23:20:14 +0200 Subject: [PATCH 029/786] Introduced intermediate class NonModifiableProcessArrayAccessor. Needed to keep the readback promise for non-swappable ProcessArrays. --- include/ProcessArrayAccessor.h | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 9747a5c..45e3f19 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -6,18 +6,38 @@ namespace mtca4u{ template - class ProcessArrayAccessor: public ProcessVariableAccessor{ + class NonModifiableProcessArrayAccessor: public ProcessVariableAccessor{ public: - ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = + NonModifiableProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = typename ProcessArray::SharedPtr()) : ProcessVariableAccessor(processArray){ // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ } - std::vector & get(){ + std::vector const & get() const{ return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } + + std::vector const & getConst() const{ + return boost::static_pointer_cast< ProcessArray >(_impl)->getConst(); + } + + }; + + template + class ProcessArrayAccessor: public NonModifiableProcessArrayAccessor{ + + public: + ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = + typename ProcessArray::SharedPtr()) + : NonModifiableProcessArrayAccessor(processArray){ + // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ + } + + std::vector & get(){ + return boost::static_pointer_cast< ProcessArray >(ProcessVariableAccessor::_impl)->get(); + } }; From 025b7ccb54d13eb79038d3c75d29755b4f82d161 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 13 Jun 2016 15:42:48 +0200 Subject: [PATCH 030/786] Revert "Introduced intermediate class NonModifiableProcessArrayAccessor. Needed to keep the readback promise for non-swappable ProcessArrays." This reverts commit 3f0861170277a67910e087d0a8523a6eb4a38b6f. The new implementation will not have the 'non-swappable' promise any more, so we don't need it. --- include/ProcessArrayAccessor.h | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 45e3f19..9747a5c 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -6,37 +6,17 @@ namespace mtca4u{ template - class NonModifiableProcessArrayAccessor: public ProcessVariableAccessor{ - - public: - NonModifiableProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = - typename ProcessArray::SharedPtr()) - : ProcessVariableAccessor(processArray){ - // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ - } - - std::vector const & get() const{ - return boost::static_pointer_cast< ProcessArray >(_impl)->get(); - } - - std::vector const & getConst() const{ - return boost::static_pointer_cast< ProcessArray >(_impl)->getConst(); - } - - }; - - template - class ProcessArrayAccessor: public NonModifiableProcessArrayAccessor{ + class ProcessArrayAccessor: public ProcessVariableAccessor{ public: ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = typename ProcessArray::SharedPtr()) - : NonModifiableProcessArrayAccessor(processArray){ + : ProcessVariableAccessor(processArray){ // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ } std::vector & get(){ - return boost::static_pointer_cast< ProcessArray >(ProcessVariableAccessor::_impl)->get(); + return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } }; From c2382edf0670fd747a65714ad72e0144b1facbe6 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Jun 2016 04:01:58 +0200 Subject: [PATCH 031/786] doing baby steps --- include/ProcessScalar.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index e136efe..c30c29b 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -153,14 +153,6 @@ namespace mtca4u{ boost::shared_ptr sendNotificationListener = boost::shared_ptr()); -} // namespace mtca4u - -// ProcessScalarImpl.h must be included after the class definition and the -// template function declaration, because it depends on it. -#include "ProcessScalarImpl.h" - -namespace mtca4u { - template typename ProcessScalar::SharedPtr createSimpleProcessScalar( const std::string & name, T initialValue) { From 5cf15e80d071128b31510a4b2d99165b5bf27e89 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Jun 2016 04:08:29 +0200 Subject: [PATCH 032/786] copied content of ProcessScalarImpl to ProcessScalar file --- include/ProcessScalar.h | 314 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 311 insertions(+), 3 deletions(-) diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index c30c29b..e9874e6 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -5,6 +5,16 @@ #include +#include +#include +#include +#include + +#include +#include +#include +#include + #include "ProcessVariable.h" #include "ProcessVariableListener.h" @@ -90,13 +100,311 @@ namespace mtca4u { }; -} //namespace // FIXME ProcessScalarImpl.h must be included before defining the creator functions, because // these have been hacked to return the impl. -#include "ProcessScalarImpl.h" + namespace impl { + + /** + * Implementation of the ProcessScalar. This implementation is used for all + * three use cases (sender, receiver, and stand-alone). + */ + template + class ProcessScalarImpl: public ProcessVariable { + + // Allow the factory functions to create instances of this class. + // friend typename ProcessScalarImpl::SharedPtr createSimpleProcessScalar( + // const std::string &, T); + + // friend typename std::pair::SharedPtr, + // typename ProcessScalarImpl::SharedPtr> createSynchronizedProcessScalar< + // T>(const std::string &, T, std::size_t, + // boost::shared_ptr, + // boost::shared_ptr); + + // fixme whould be private: + public: + /** + * Type of the instance. This defines the behavior (send or receive + * possible, modifications allowed, etc.). This enum type is private so + * that only friend functions can construct instances of this class. The + * constructors themselves have to be public so that boost::make_shared + * can be used. + */ + enum InstanceType { + /** + * Instance acts on is own. + */ + STAND_ALONE, + + /** + * Instance acts as the sender in a sender / receiver pair. + */ + SENDER, + + /** + * Instance acts as the receiver in a sender / receiver pair. + */ + RECEIVER + }; + + public: + /** + * Type alias for a shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + + /** + * Creates a process scalar that works independently. This means that the + * instance is not synchronized with any other instance and thus the send + * and receive operations are not supported. However, all other operations + * can be used like on any process variable. + */ + ProcessScalarImpl(InstanceType instanceType, const std::string& name, + T initialValue) : + ProcessVariable(name), _instanceType(instanceType), _value( + initialValue) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != STAND_ALONE) { + throw std::invalid_argument( + "This constructor may only be used for a stand-alone process scalar."); + } + } + + /** + * Creates a process scalar that acts as a receiver. A receiver is + * intended to work as a tandem with a sender and receives values that + * have been set to the sender. + * + * This constructor creates the buffers and queues that are needed for the + * send/receive process and are shared with the sender. + */ + ProcessScalarImpl(InstanceType instanceType, const std::string& name, + T initialValue, std::size_t numberOfBuffers) : + ProcessVariable(name), _instanceType(instanceType), _value( + initialValue), _bufferQueue( + boost::make_shared >( + numberOfBuffers)) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != RECEIVER) { + throw std::invalid_argument( + "This constructor may only be used for a receiver process scalar."); + } + if (numberOfBuffers < 1) { + throw std::invalid_argument( + "The number of buffers must be at least one."); + } + } + + /** + * Creates a process scalar that acts as a sender. A sender is intended + * intended to work as a tandem with a receiver and send set values to + * it. It uses the buffers and queues that have been created by the + * receiver. + * + * If the swappable option is true, the swap operation is + * allowed on the receiver. The swappable flag must be the + * same for the sender and receiver. Allowing swapping has the side effect + * that a value cannot be read after having been sent. + * + * The optional send-notification listener is notified every time the + * sender's {@link ProcessScalarImpl::send()} method is called. It can be + * used to queue a request for the receiver's + * {@link ProcessScalarImpl::receive()} method to be called. The process + * variable passed to the listener is the receiver and not the sender. + */ + ProcessScalarImpl(InstanceType instanceType, + boost::shared_ptr timeStampSource, + boost::shared_ptr sendNotificationListener, + boost::shared_ptr receiver) : + ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( + receiver->_timeStamp), _value(receiver->_value), _bufferQueue( + receiver->_bufferQueue), _receiver(receiver), _timeStampSource( + timeStampSource), _sendNotificationListener( + sendNotificationListener) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != SENDER) { + throw std::invalid_argument( + "This constructor may only be used for a sender process scalar."); + } + if (!receiver) { + throw std::invalid_argument( + "The pointer to the receiver must not be null."); + } + if (receiver->_instanceType != RECEIVER) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + } + + /** + * Assignment operator. This behaves excactly like the set(...) method. + */ + ProcessScalarImpl & operator=(ProcessScalarImpl const & other) { + set(other.get()); + return (*this); + } + + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ + // currently commented out while removing the impl-inheritance. will be needed later + // in the pimpl pattern + // ProcessScalarImpl & operator=(ProcessScalar const & other) { + // set(other.get()); + // return (*this); + // } + + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ + ProcessScalarImpl & operator=(T const & t) { + set(t); + return *this; + } + + operator T() const { + return get(); + } + + void set(T const & t) { + _value = t; + } + + // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl + // inheritence for creating the pimpl pattern, where impl and not are typedefed. + const std::type_info& getValueType() const { + return typeid(T); + } + + bool isArray() const { + return false; + } + + void set(ProcessScalarImpl const & other) { + set(other.get()); + } + + T get() const { + return _value; + } + + bool isReceiver() const { + return _instanceType == RECEIVER; + } + + bool isSender() const { + return _instanceType == SENDER; + } + + TimeStamp getTimeStamp() const { + return _timeStamp; + } + + bool receive() { + if (_instanceType != RECEIVER) { + throw std::logic_error( + "Receive operation is only allowed for a receiver process variable."); + } + Buffer nextBuffer; + if (_bufferQueue->pop(nextBuffer)) { + _timeStamp = nextBuffer.timeStamp; + _value = nextBuffer.value; + return true; + } else { + return false; + } + } + + bool send() { + if (_instanceType != SENDER) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable."); + } + // Before sending the value, we have to update our time stamp. + _timeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : + TimeStamp::currentTime(); + Buffer nextBuffer; + nextBuffer.timeStamp = _timeStamp; + nextBuffer.value = _value; + bool foundEmptyBuffer; + if (_bufferQueue->bounded_push(nextBuffer)) { + foundEmptyBuffer = true; + } else { + // We are not interested in the old value, but we have to provided + // a reference. + Buffer oldBuffer; + // If we remove an element, pop returns true, otherwise the receiving + // thread just took the last element and we can continue without + // losing data. + foundEmptyBuffer = !_bufferQueue->pop(oldBuffer); + // Now we can be sure that the push is successful. + _bufferQueue->bounded_push(nextBuffer); + } + if (_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); + } + return foundEmptyBuffer; + } + + private: + /** + * Type for the individual buffers. Each buffer stores a value and a time + * stamp. + */ + struct Buffer { + TimeStamp timeStamp; + T value; + }; + + /** + * Type this instance is representing. + */ + InstanceType _instanceType; + + /** + * Time stamp of the current value. + */ + TimeStamp _timeStamp; + + /** + * Current value. + */ + T _value; + + /** + * Queue holding the values that have been sent but not received yet. + * We do not use an spsc_queue for this queue, because might want to take + * elements from the sending thread, so there are two threads which might + * consume elements and thus an spsc_queue is not safe. + */ + boost::shared_ptr > _bufferQueue; + + /** + * Pointer to the receiver associated with this sender. This field is only + * used if this process variable represents a sender. + */ + boost::shared_ptr > _receiver; + + /** + * Time-stamp source used to update the time-stamp when sending a value. + */ + boost::shared_ptr _timeStampSource; + + /** + * Listener that is notified when the process variable is sent. + */ + boost::shared_ptr _sendNotificationListener; + + }; + + } // namespace impl -namespace mtca4u{ // for the transition phase ProcessScalar and Impl are the same template using ProcessScalar = impl::ProcessScalarImpl; From 593df4100609623bbd917ede472f449a2f4fe051 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Jun 2016 04:32:00 +0200 Subject: [PATCH 033/786] finally compiles without namespace impl --- include/ProcessScalar.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index e9874e6..bc1cfbc 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -103,7 +103,6 @@ namespace mtca4u { // FIXME ProcessScalarImpl.h must be included before defining the creator functions, because // these have been hacked to return the impl. - namespace impl { /** * Implementation of the ProcessScalar. This implementation is used for all @@ -403,11 +402,10 @@ namespace mtca4u { }; - } // namespace impl // for the transition phase ProcessScalar and Impl are the same template - using ProcessScalar = impl::ProcessScalarImpl; + using ProcessScalar = ProcessScalarImpl; /** * Creates a simple process scalar. A simple process scalar just works on its @@ -464,8 +462,8 @@ namespace mtca4u { template typename ProcessScalar::SharedPtr createSimpleProcessScalar( const std::string & name, T initialValue) { - return boost::make_shared >( - impl::ProcessScalarImpl::STAND_ALONE, name, initialValue); + return boost::make_shared >( + ProcessScalarImpl::STAND_ALONE, name, initialValue); } template @@ -474,13 +472,13 @@ namespace mtca4u { const std::string & name, T initialValue, std::size_t numberOfBuffers, boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener) { - boost::shared_ptr > receiver = - boost::make_shared >( - impl::ProcessScalarImpl::RECEIVER, name, initialValue, + boost::shared_ptr > receiver = + boost::make_shared >( + ProcessScalarImpl::RECEIVER, name, initialValue, numberOfBuffers); typename ProcessScalar::SharedPtr sender = boost::make_shared< - typename impl::ProcessScalarImpl >( - impl::ProcessScalarImpl::SENDER, timeStampSource, + ProcessScalarImpl >( + ProcessScalarImpl::SENDER, timeStampSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessScalar::SharedPtr>(sender, receiver); From 510263ebf9c2bce2b052ecec3673bb65e44ef59c Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Jun 2016 04:40:45 +0200 Subject: [PATCH 034/786] Finally completely got rid of ProcessScalarImpl --- include/ProcessScalar.h | 535 ++++++++++++++++++------------------ include/ProcessScalarImpl.h | 321 ---------------------- 2 files changed, 264 insertions(+), 592 deletions(-) delete mode 100644 include/ProcessScalarImpl.h diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index bc1cfbc..ab35d55 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -101,311 +101,304 @@ namespace mtca4u { }; -// FIXME ProcessScalarImpl.h must be included before defining the creator functions, because -// these have been hacked to return the impl. - - /** - * Implementation of the ProcessScalar. This implementation is used for all - * three use cases (sender, receiver, and stand-alone). - */ - template - class ProcessScalarImpl: public ProcessVariable { + /** + * Implementation of the ProcessScalar. This implementation is used for all + * three use cases (sender, receiver, and stand-alone). + */ + template + class ProcessScalar: public ProcessVariable { - // Allow the factory functions to create instances of this class. - // friend typename ProcessScalarImpl::SharedPtr createSimpleProcessScalar( - // const std::string &, T); + // Allow the factory functions to create instances of this class. + // friend typename ProcessScalar::SharedPtr createSimpleProcessScalar( + // const std::string &, T); - // friend typename std::pair::SharedPtr, - // typename ProcessScalarImpl::SharedPtr> createSynchronizedProcessScalar< - // T>(const std::string &, T, std::size_t, - // boost::shared_ptr, - // boost::shared_ptr); + // friend typename std::pair::SharedPtr, + // typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar< + // T>(const std::string &, T, std::size_t, + // boost::shared_ptr, + // boost::shared_ptr); - // fixme whould be private: - public: - /** - * Type of the instance. This defines the behavior (send or receive - * possible, modifications allowed, etc.). This enum type is private so - * that only friend functions can construct instances of this class. The - * constructors themselves have to be public so that boost::make_shared - * can be used. - */ - enum InstanceType { - /** - * Instance acts on is own. - */ - STAND_ALONE, - - /** - * Instance acts as the sender in a sender / receiver pair. - */ - SENDER, - - /** - * Instance acts as the receiver in a sender / receiver pair. - */ - RECEIVER - }; - - public: + // fixme whould be private: + public: + /** + * Type of the instance. This defines the behavior (send or receive + * possible, modifications allowed, etc.). This enum type is private so + * that only friend functions can construct instances of this class. The + * constructors themselves have to be public so that boost::make_shared + * can be used. + */ + enum InstanceType { /** - * Type alias for a shared pointer to this type. + * Instance acts on is own. */ - typedef boost::shared_ptr SharedPtr; + STAND_ALONE, /** - * Creates a process scalar that works independently. This means that the - * instance is not synchronized with any other instance and thus the send - * and receive operations are not supported. However, all other operations - * can be used like on any process variable. + * Instance acts as the sender in a sender / receiver pair. */ - ProcessScalarImpl(InstanceType instanceType, const std::string& name, - T initialValue) : - ProcessVariable(name), _instanceType(instanceType), _value( - initialValue) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != STAND_ALONE) { - throw std::invalid_argument( - "This constructor may only be used for a stand-alone process scalar."); - } - } + SENDER, /** - * Creates a process scalar that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. + * Instance acts as the receiver in a sender / receiver pair. */ - ProcessScalarImpl(InstanceType instanceType, const std::string& name, - T initialValue, std::size_t numberOfBuffers) : - ProcessVariable(name), _instanceType(instanceType), _value( - initialValue), _bufferQueue( - boost::make_shared >( - numberOfBuffers)) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument( - "This constructor may only be used for a receiver process scalar."); - } - if (numberOfBuffers < 1) { - throw std::invalid_argument( - "The number of buffers must be at least one."); - } - } + RECEIVER + }; - /** - * Creates a process scalar that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the swappable option is true, the swap operation is - * allowed on the receiver. The swappable flag must be the - * same for the sender and receiver. Allowing swapping has the side effect - * that a value cannot be read after having been sent. - * - * The optional send-notification listener is notified every time the - * sender's {@link ProcessScalarImpl::send()} method is called. It can be - * used to queue a request for the receiver's - * {@link ProcessScalarImpl::receive()} method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - ProcessScalarImpl(InstanceType instanceType, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener, - boost::shared_ptr receiver) : - ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( - receiver->_timeStamp), _value(receiver->_value), _bufferQueue( - receiver->_bufferQueue), _receiver(receiver), _timeStampSource( - timeStampSource), _sendNotificationListener( - sendNotificationListener) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument( - "This constructor may only be used for a sender process scalar."); - } - if (!receiver) { - throw std::invalid_argument( - "The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - } + public: + /** + * Type alias for a shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; - /** - * Assignment operator. This behaves excactly like the set(...) method. - */ - ProcessScalarImpl & operator=(ProcessScalarImpl const & other) { - set(other.get()); - return (*this); + /** + * Creates a process scalar that works independently. This means that the + * instance is not synchronized with any other instance and thus the send + * and receive operations are not supported. However, all other operations + * can be used like on any process variable. + */ + ProcessScalar(InstanceType instanceType, const std::string& name, + T initialValue) : + ProcessVariable(name), _instanceType(instanceType), _value( + initialValue) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != STAND_ALONE) { + throw std::invalid_argument( + "This constructor may only be used for a stand-alone process scalar."); } + } - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - // currently commented out while removing the impl-inheritance. will be needed later - // in the pimpl pattern - // ProcessScalarImpl & operator=(ProcessScalar const & other) { - // set(other.get()); - // return (*this); - // } - - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - ProcessScalarImpl & operator=(T const & t) { - set(t); - return *this; + /** + * Creates a process scalar that acts as a receiver. A receiver is + * intended to work as a tandem with a sender and receives values that + * have been set to the sender. + * + * This constructor creates the buffers and queues that are needed for the + * send/receive process and are shared with the sender. + */ + ProcessScalar(InstanceType instanceType, const std::string& name, + T initialValue, std::size_t numberOfBuffers) : + ProcessVariable(name), _instanceType(instanceType), _value( + initialValue), _bufferQueue( + boost::make_shared >( + numberOfBuffers)) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != RECEIVER) { + throw std::invalid_argument( + "This constructor may only be used for a receiver process scalar."); } - - operator T() const { - return get(); + if (numberOfBuffers < 1) { + throw std::invalid_argument( + "The number of buffers must be at least one."); } + } - void set(T const & t) { - _value = t; + /** + * Creates a process scalar that acts as a sender. A sender is intended + * intended to work as a tandem with a receiver and send set values to + * it. It uses the buffers and queues that have been created by the + * receiver. + * + * If the swappable option is true, the swap operation is + * allowed on the receiver. The swappable flag must be the + * same for the sender and receiver. Allowing swapping has the side effect + * that a value cannot be read after having been sent. + * + * The optional send-notification listener is notified every time the + * sender's {@link ProcessScalar::send()} method is called. It can be + * used to queue a request for the receiver's + * {@link ProcessScalar::receive()} method to be called. The process + * variable passed to the listener is the receiver and not the sender. + */ + ProcessScalar(InstanceType instanceType, + boost::shared_ptr timeStampSource, + boost::shared_ptr sendNotificationListener, + boost::shared_ptr receiver) : + ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( + receiver->_timeStamp), _value(receiver->_value), _bufferQueue( + receiver->_bufferQueue), _receiver(receiver), _timeStampSource( + timeStampSource), _sendNotificationListener( + sendNotificationListener) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != SENDER) { + throw std::invalid_argument( + "This constructor may only be used for a sender process scalar."); } - - // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl - // inheritence for creating the pimpl pattern, where impl and not are typedefed. - const std::type_info& getValueType() const { - return typeid(T); + if (!receiver) { + throw std::invalid_argument( + "The pointer to the receiver must not be null."); } + if (receiver->_instanceType != RECEIVER) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + } + + /** + * Assignment operator. This behaves excactly like the set(...) method. + */ + ProcessScalar & operator=(ProcessScalar const & other) { + set(other.get()); + return (*this); + } + + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ + // currently commented out while removing the impl-inheritance. will be needed later + // in the pimpl pattern + // ProcessScalar & operator=(ProcessScalar const & other) { + // set(other.get()); + // return (*this); + // } + + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ + ProcessScalar & operator=(T const & t) { + set(t); + return *this; + } + + operator T() const { + return get(); + } + + void set(T const & t) { + _value = t; + } + + // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl + // inheritence for creating the pimpl pattern, where impl and not are typedefed. + const std::type_info& getValueType() const { + return typeid(T); + } - bool isArray() const { + bool isArray() const { return false; - } + } - void set(ProcessScalarImpl const & other) { - set(other.get()); - } + void set(ProcessScalar const & other) { + set(other.get()); + } - T get() const { - return _value; - } + T get() const { + return _value; + } - bool isReceiver() const { - return _instanceType == RECEIVER; - } + bool isReceiver() const { + return _instanceType == RECEIVER; + } - bool isSender() const { - return _instanceType == SENDER; - } + bool isSender() const { + return _instanceType == SENDER; + } - TimeStamp getTimeStamp() const { - return _timeStamp; - } + TimeStamp getTimeStamp() const { + return _timeStamp; + } - bool receive() { - if (_instanceType != RECEIVER) { - throw std::logic_error( - "Receive operation is only allowed for a receiver process variable."); - } - Buffer nextBuffer; - if (_bufferQueue->pop(nextBuffer)) { - _timeStamp = nextBuffer.timeStamp; - _value = nextBuffer.value; - return true; - } else { - return false; - } + bool receive() { + if (_instanceType != RECEIVER) { + throw std::logic_error( + "Receive operation is only allowed for a receiver process variable."); } - - bool send() { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable."); - } - // Before sending the value, we have to update our time stamp. - _timeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - Buffer nextBuffer; - nextBuffer.timeStamp = _timeStamp; - nextBuffer.value = _value; - bool foundEmptyBuffer; - if (_bufferQueue->bounded_push(nextBuffer)) { - foundEmptyBuffer = true; - } else { - // We are not interested in the old value, but we have to provided - // a reference. - Buffer oldBuffer; - // If we remove an element, pop returns true, otherwise the receiving - // thread just took the last element and we can continue without - // losing data. - foundEmptyBuffer = !_bufferQueue->pop(oldBuffer); - // Now we can be sure that the push is successful. - _bufferQueue->bounded_push(nextBuffer); - } - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - return foundEmptyBuffer; + Buffer nextBuffer; + if (_bufferQueue->pop(nextBuffer)) { + _timeStamp = nextBuffer.timeStamp; + _value = nextBuffer.value; + return true; + } else { + return false; } + } - private: - /** - * Type for the individual buffers. Each buffer stores a value and a time - * stamp. - */ - struct Buffer { - TimeStamp timeStamp; - T value; - }; + bool send() { + if (_instanceType != SENDER) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable."); + } + // Before sending the value, we have to update our time stamp. + _timeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : + TimeStamp::currentTime(); + Buffer nextBuffer; + nextBuffer.timeStamp = _timeStamp; + nextBuffer.value = _value; + bool foundEmptyBuffer; + if (_bufferQueue->bounded_push(nextBuffer)) { + foundEmptyBuffer = true; + } else { + // We are not interested in the old value, but we have to provided + // a reference. + Buffer oldBuffer; + // If we remove an element, pop returns true, otherwise the receiving + // thread just took the last element and we can continue without + // losing data. + foundEmptyBuffer = !_bufferQueue->pop(oldBuffer); + // Now we can be sure that the push is successful. + _bufferQueue->bounded_push(nextBuffer); + } + if (_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); + } + return foundEmptyBuffer; + } - /** - * Type this instance is representing. - */ - InstanceType _instanceType; + private: + /** + * Type for the individual buffers. Each buffer stores a value and a time + * stamp. + */ + struct Buffer { + TimeStamp timeStamp; + T value; + }; - /** - * Time stamp of the current value. - */ - TimeStamp _timeStamp; + /** + * Type this instance is representing. + */ + InstanceType _instanceType; - /** - * Current value. - */ - T _value; + /** + * Time stamp of the current value. + */ + TimeStamp _timeStamp; - /** - * Queue holding the values that have been sent but not received yet. - * We do not use an spsc_queue for this queue, because might want to take - * elements from the sending thread, so there are two threads which might - * consume elements and thus an spsc_queue is not safe. - */ - boost::shared_ptr > _bufferQueue; + /** + * Current value. + */ + T _value; - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr > _receiver; + /** + * Queue holding the values that have been sent but not received yet. + * We do not use an spsc_queue for this queue, because might want to take + * elements from the sending thread, so there are two threads which might + * consume elements and thus an spsc_queue is not safe. + */ + boost::shared_ptr > _bufferQueue; - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; + /** + * Pointer to the receiver associated with this sender. This field is only + * used if this process variable represents a sender. + */ + boost::shared_ptr > _receiver; - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; + /** + * Time-stamp source used to update the time-stamp when sending a value. + */ + boost::shared_ptr _timeStampSource; - }; + /** + * Listener that is notified when the process variable is sent. + */ + boost::shared_ptr _sendNotificationListener; + }; - // for the transition phase ProcessScalar and Impl are the same - template - using ProcessScalar = ProcessScalarImpl; /** * Creates a simple process scalar. A simple process scalar just works on its @@ -462,8 +455,8 @@ namespace mtca4u { template typename ProcessScalar::SharedPtr createSimpleProcessScalar( const std::string & name, T initialValue) { - return boost::make_shared >( - ProcessScalarImpl::STAND_ALONE, name, initialValue); + return boost::make_shared >( + ProcessScalar::STAND_ALONE, name, initialValue); } template @@ -472,13 +465,13 @@ namespace mtca4u { const std::string & name, T initialValue, std::size_t numberOfBuffers, boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener) { - boost::shared_ptr > receiver = - boost::make_shared >( - ProcessScalarImpl::RECEIVER, name, initialValue, + boost::shared_ptr > receiver = + boost::make_shared >( + ProcessScalar::RECEIVER, name, initialValue, numberOfBuffers); typename ProcessScalar::SharedPtr sender = boost::make_shared< - ProcessScalarImpl >( - ProcessScalarImpl::SENDER, timeStampSource, + ProcessScalar >( + ProcessScalar::SENDER, timeStampSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessScalar::SharedPtr>(sender, receiver); diff --git a/include/ProcessScalarImpl.h b/include/ProcessScalarImpl.h deleted file mode 100644 index 46e4454..0000000 --- a/include/ProcessScalarImpl.h +++ /dev/null @@ -1,321 +0,0 @@ -#ifndef MTCA4U_PROCESS_SCALAR_IMPL_H -#define MTCA4U_PROCESS_SCALAR_IMPL_H - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ProcessScalar.h" -#include "ProcessVariableListener.h" - -namespace mtca4u { - namespace impl { - - /** - * Implementation of the ProcessScalar. This implementation is used for all - * three use cases (sender, receiver, and stand-alone). - */ - template - class ProcessScalarImpl: public ProcessVariable { - - // Allow the factory functions to create instances of this class. - // friend typename ProcessScalarImpl::SharedPtr createSimpleProcessScalar( - // const std::string &, T); - - // friend typename std::pair::SharedPtr, - // typename ProcessScalarImpl::SharedPtr> createSynchronizedProcessScalar< - // T>(const std::string &, T, std::size_t, - // boost::shared_ptr, - // boost::shared_ptr); - - // fixme whould be private: - public: - /** - * Type of the instance. This defines the behavior (send or receive - * possible, modifications allowed, etc.). This enum type is private so - * that only friend functions can construct instances of this class. The - * constructors themselves have to be public so that boost::make_shared - * can be used. - */ - enum InstanceType { - /** - * Instance acts on is own. - */ - STAND_ALONE, - - /** - * Instance acts as the sender in a sender / receiver pair. - */ - SENDER, - - /** - * Instance acts as the receiver in a sender / receiver pair. - */ - RECEIVER - }; - - public: - /** - * Type alias for a shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Creates a process scalar that works independently. This means that the - * instance is not synchronized with any other instance and thus the send - * and receive operations are not supported. However, all other operations - * can be used like on any process variable. - */ - ProcessScalarImpl(InstanceType instanceType, const std::string& name, - T initialValue) : - ProcessVariable(name), _instanceType(instanceType), _value( - initialValue) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != STAND_ALONE) { - throw std::invalid_argument( - "This constructor may only be used for a stand-alone process scalar."); - } - } - - /** - * Creates a process scalar that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. - */ - ProcessScalarImpl(InstanceType instanceType, const std::string& name, - T initialValue, std::size_t numberOfBuffers) : - ProcessVariable(name), _instanceType(instanceType), _value( - initialValue), _bufferQueue( - boost::make_shared >( - numberOfBuffers)) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument( - "This constructor may only be used for a receiver process scalar."); - } - if (numberOfBuffers < 1) { - throw std::invalid_argument( - "The number of buffers must be at least one."); - } - } - - /** - * Creates a process scalar that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the swappable option is true, the swap operation is - * allowed on the receiver. The swappable flag must be the - * same for the sender and receiver. Allowing swapping has the side effect - * that a value cannot be read after having been sent. - * - * The optional send-notification listener is notified every time the - * sender's {@link ProcessScalarImpl::send()} method is called. It can be - * used to queue a request for the receiver's - * {@link ProcessScalarImpl::receive()} method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - ProcessScalarImpl(InstanceType instanceType, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener, - boost::shared_ptr receiver) : - ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( - receiver->_timeStamp), _value(receiver->_value), _bufferQueue( - receiver->_bufferQueue), _receiver(receiver), _timeStampSource( - timeStampSource), _sendNotificationListener( - sendNotificationListener) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument( - "This constructor may only be used for a sender process scalar."); - } - if (!receiver) { - throw std::invalid_argument( - "The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - } - - /** - * Assignment operator. This behaves excactly like the set(...) method. - */ - ProcessScalarImpl & operator=(ProcessScalarImpl const & other) { - set(other.get()); - return (*this); - } - - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - // currently commented out while removing the impl-inheritance. will be needed later - // in the pimpl pattern - // ProcessScalarImpl & operator=(ProcessScalar const & other) { - // set(other.get()); - // return (*this); - // } - - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - ProcessScalarImpl & operator=(T const & t) { - set(t); - return *this; - } - - operator T() const { - return get(); - } - - void set(T const & t) { - _value = t; - } - - // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl - // inheritence for creating the pimpl pattern, where impl and not are typedefed. - const std::type_info& getValueType() const { - return typeid(T); - } - - bool isArray() const { - return false; - } - - void set(ProcessScalarImpl const & other) { - set(other.get()); - } - - T get() const { - return _value; - } - - bool isReceiver() const { - return _instanceType == RECEIVER; - } - - bool isSender() const { - return _instanceType == SENDER; - } - - TimeStamp getTimeStamp() const { - return _timeStamp; - } - - bool receive() { - if (_instanceType != RECEIVER) { - throw std::logic_error( - "Receive operation is only allowed for a receiver process variable."); - } - Buffer nextBuffer; - if (_bufferQueue->pop(nextBuffer)) { - _timeStamp = nextBuffer.timeStamp; - _value = nextBuffer.value; - return true; - } else { - return false; - } - } - - bool send() { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable."); - } - // Before sending the value, we have to update our time stamp. - _timeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - Buffer nextBuffer; - nextBuffer.timeStamp = _timeStamp; - nextBuffer.value = _value; - bool foundEmptyBuffer; - if (_bufferQueue->bounded_push(nextBuffer)) { - foundEmptyBuffer = true; - } else { - // We are not interested in the old value, but we have to provided - // a reference. - Buffer oldBuffer; - // If we remove an element, pop returns true, otherwise the receiving - // thread just took the last element and we can continue without - // losing data. - foundEmptyBuffer = !_bufferQueue->pop(oldBuffer); - // Now we can be sure that the push is successful. - _bufferQueue->bounded_push(nextBuffer); - } - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - return foundEmptyBuffer; - } - - private: - /** - * Type for the individual buffers. Each buffer stores a value and a time - * stamp. - */ - struct Buffer { - TimeStamp timeStamp; - T value; - }; - - /** - * Type this instance is representing. - */ - InstanceType _instanceType; - - /** - * Time stamp of the current value. - */ - TimeStamp _timeStamp; - - /** - * Current value. - */ - T _value; - - /** - * Queue holding the values that have been sent but not received yet. - * We do not use an spsc_queue for this queue, because might want to take - * elements from the sending thread, so there are two threads which might - * consume elements and thus an spsc_queue is not safe. - */ - boost::shared_ptr > _bufferQueue; - - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr > _receiver; - - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; - - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; - - }; - - } // namespace impl -} //namespace mtca4u - -#endif // MTCA4U_PROCESS_SCALAR_IMPL_H From 663d788a9b97d4782d9fbd3d2f089848c3721fee Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Jun 2016 13:34:57 +0200 Subject: [PATCH 035/786] moved position of the include --- include/ProcessArray.h | 11 +++++++---- include/ProcessArrayImpl.h | 38 +++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index 4d53e89..94e832a 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -164,6 +164,13 @@ namespace mtca4u { } }; +} + +// ProcessArrayImpl.h must be included after the class definition and the +// template function declaration, because it depends on it. +#include "ProcessArrayImpl.h" + +namespace mtca4u { /** * Creates a simple process array. A simple process array just works on its @@ -295,10 +302,6 @@ namespace mtca4u { } // namespace mtca4u -// ProcessArrayImpl.h must be included after the class definition and the -// template function declaration, because it depends on it. -#include "ProcessArrayImpl.h" - namespace mtca4u { template diff --git a/include/ProcessArrayImpl.h b/include/ProcessArrayImpl.h index 5536761..f32e83a 100644 --- a/include/ProcessArrayImpl.h +++ b/include/ProcessArrayImpl.h @@ -24,26 +24,26 @@ namespace mtca4u { template class ProcessArrayImpl: public ProcessArray { - // Allow the factory functions to create instances of this class. - friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< - T>(std::size_t, const std::string &, T); +// // Allow the factory functions to create instances of this class. +// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< +// T>(std::size_t, const std::string &, T); +// +// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< +// T>(const std::vector&, const std::string &); +// +// friend typename std::pair::SharedPtr, +// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< +// T>(std::size_t, const std::string &, T, bool, std::size_t, +// boost::shared_ptr, +// boost::shared_ptr); +// +// friend typename std::pair::SharedPtr, +// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< +// T>(const std::vector&, const std::string &, bool, std::size_t, +// boost::shared_ptr, +// boost::shared_ptr); - friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< - T>(const std::vector&, const std::string &); - - friend typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< - T>(std::size_t, const std::string &, T, bool, std::size_t, - boost::shared_ptr, - boost::shared_ptr); - - friend typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< - T>(const std::vector&, const std::string &, bool, std::size_t, - boost::shared_ptr, - boost::shared_ptr); - - private: + public: /** * Type of the instance. This defines the behavior (send or receive * possible, modifications allowed, etc.). This enum type is private so From ae981c82d716ec1fb3380d10300f80889836f7dc Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Jun 2016 17:40:00 +0200 Subject: [PATCH 036/786] ProcessArray and ProcessArrayImpl are the same with a typedef --- include/ProcessArray.h | 47 +++++++++++++++++++++----------------- include/ProcessArrayImpl.h | 32 ++++++++++++++++++-------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index 94e832a..90dc33f 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -17,32 +17,33 @@ namespace mtca4u { * should only be used from a single thread. */ template - class ProcessArray: public ProcessVariable { + class ProcessArrayHowItLookedLike: public ProcessVariable { public: /** * Type alias for a shared pointer to this type. */ - typedef boost::shared_ptr SharedPtr; + typedef boost::shared_ptr SharedPtr; /** * Assignment operator for another ProcessArray of the same template type. * It can be of a different implementation, though. The size of the assigned * array must be smaller than or equal to the target size. */ - ProcessArray & operator=(ProcessArray const & other) { - this->set(other); - return *this; - } + // we don't want this behavious anyway. + //ProcessArray & operator=(ProcessArray const & other) { + // this->set(other); + // return *this; + //} /** * Assignment operator for a std::vector of the template data type. * The size of the assigned array must be smaller than or equal to the * target size. */ - ProcessArray & operator=(std::vector const & other) { - this->set(other); - return *this; - } +// ProcessArray & operator=(std::vector const & other) { +// this->set(other); +// return *this; +// } /** * Returns a reference to the vector that represents the current value of @@ -105,7 +106,7 @@ namespace mtca4u { * If this instance of the process array must not be modified (because it is * a receiver and does not allow swapping), this method throws an exception. */ - virtual void set(ProcessArray const & other) =0; + // virtual void set(ProcessArray const & other) =0; /** * Updates this process variable's value with the elements from the @@ -152,16 +153,16 @@ namespace mtca4u { /** * Creates a process array with the specified name. */ - ProcessArray(const std::string& name = std::string()) : - ProcessVariable(name) { - } - - /** - * Protected destructor. Instances should not be destroyed through - * pointers to this base type. - */ - virtual ~ProcessArray() { - } +// ProcessArray(const std::string& name = std::string()) : +// ProcessVariable(name) { +// } +// +// /** +// * Protected destructor. Instances should not be destroyed through +// * pointers to this base type. +// */ +// virtual ~ProcessArray() { +// } }; } @@ -172,6 +173,10 @@ namespace mtca4u { namespace mtca4u { + // temporary typedef + template + using ProcessArray = impl::ProcessArrayImpl; + /** * Creates a simple process array. A simple process array just works on its * own and does not implement a synchronization mechanism. Apart from this, diff --git a/include/ProcessArrayImpl.h b/include/ProcessArrayImpl.h index f32e83a..86ac803 100644 --- a/include/ProcessArrayImpl.h +++ b/include/ProcessArrayImpl.h @@ -11,7 +11,7 @@ #include #include -#include "ProcessArray.h" +#include "ProcessVariable.h" #include "ProcessVariableListener.h" namespace mtca4u { @@ -22,7 +22,7 @@ namespace mtca4u { * three use cases (sender, receiver, and stand-alone). */ template - class ProcessArrayImpl: public ProcessArray { + class ProcessArrayImpl: public ProcessVariable { // // Allow the factory functions to create instances of this class. // friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< @@ -44,6 +44,9 @@ namespace mtca4u { // boost::shared_ptr); public: + + typedef boost::shared_ptr SharedPtr; + /** * Type of the instance. This defines the behavior (send or receive * possible, modifications allowed, etc.). This enum type is private so @@ -77,7 +80,7 @@ namespace mtca4u { */ ProcessArrayImpl(InstanceType instanceType, const std::string& name, const std::vector& initialValue) : - ProcessArray(name), _instanceType(instanceType), _vectorSize( + ProcessVariable(name), _instanceType(instanceType), _vectorSize( initialValue.size()), _swappable(true), _buffers( boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( 0) { @@ -110,7 +113,7 @@ namespace mtca4u { ProcessArrayImpl(InstanceType instanceType, const std::string& name, const std::vector& initialValue, std::size_t numberOfBuffers, bool swappable) : - ProcessArray(name), _instanceType(instanceType), _vectorSize( + ProcessVariable(name), _instanceType(instanceType), _vectorSize( initialValue.size()), _swappable(swappable), _buffers( boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( boost::make_shared >( @@ -178,7 +181,7 @@ namespace mtca4u { boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener, boost::shared_ptr receiver) : - ProcessArray(receiver->getName()), _instanceType(instanceType), _vectorSize( + ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( receiver->_vectorSize), _swappable(swappable), _buffers( receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( @@ -211,10 +214,10 @@ namespace mtca4u { /** * Assignment operator. This behaves exactly like the set(...) method. */ - ProcessArrayImpl & operator=(ProcessArray const & other) { - set(other.getConst()); - return (*this); - } +// ProcessArrayImpl & operator=(ProcessArray const & other) { +// set(other.getConst()); +// return (*this); +// } /** * Assignment operator. This behaves exactly like the set(...) method. @@ -232,7 +235,7 @@ namespace mtca4u { get() = v; } - void set(ProcessArray const & other) { + void set(ProcessArrayImpl const & other) { set(other.getConst()); } @@ -365,6 +368,15 @@ namespace mtca4u { } return foundEmptyBuffer; } + + const std::type_info& getValueType() const { + return typeid(T); + } + + bool isArray() const { + return true; + } + private: From 92e7abe61612f22f5bdcb9e193757c0df0577927 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 14 Jun 2016 19:18:21 +0200 Subject: [PATCH 037/786] moved code from ProcessArrayImpl to ProcessArray --- include/ProcessArray.h | 483 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 477 insertions(+), 6 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index 90dc33f..fb24c52 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -5,6 +5,15 @@ #include #include +#include +#include +#include +#include + +#include +#include +#include +#include #include "ProcessVariable.h" #include "ProcessVariableListener.h" @@ -165,15 +174,477 @@ namespace mtca4u { // } }; -} -// ProcessArrayImpl.h must be included after the class definition and the -// template function declaration, because it depends on it. -#include "ProcessArrayImpl.h" + namespace impl { -namespace mtca4u { + /** + * Implementation of the ProcessArray. This implementation is used for all + * three use cases (sender, receiver, and stand-alone). + */ + template + class ProcessArrayImpl: public ProcessVariable { + +// // Allow the factory functions to create instances of this class. +// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< +// T>(std::size_t, const std::string &, T); +// +// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< +// T>(const std::vector&, const std::string &); +// +// friend typename std::pair::SharedPtr, +// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< +// T>(std::size_t, const std::string &, T, bool, std::size_t, +// boost::shared_ptr, +// boost::shared_ptr); +// +// friend typename std::pair::SharedPtr, +// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< +// T>(const std::vector&, const std::string &, bool, std::size_t, +// boost::shared_ptr, +// boost::shared_ptr); + + public: + + typedef boost::shared_ptr SharedPtr; + + /** + * Type of the instance. This defines the behavior (send or receive + * possible, modifications allowed, etc.). This enum type is private so + * that only friend functions can construct instances of this class. The + * constructors themselves have to be public so that boost::make_shared + * can be used. + */ + enum InstanceType { + /** + * Instance acts on is own. + */ + STAND_ALONE, + + /** + * Instance acts as the sender in a sender / receiver pair. + */ + SENDER, + + /** + * Instance acts as the receiver in a sender / receiver pair. + */ + RECEIVER + }; + + public: + /** + * Creates a process array that works independently. This means that the + * instance is not synchronized with any other instance and thus the send + * and receive operations are not supported. However, all other operations + * can be used like on any process variable. + */ + ProcessArrayImpl(InstanceType instanceType, const std::string& name, + const std::vector& initialValue) : + ProcessVariable(name), _instanceType(instanceType), _vectorSize( + initialValue.size()), _swappable(true), _buffers( + boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( + 0) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != STAND_ALONE) { + throw std::invalid_argument( + "This constructor may only be used for a stand-alone process scalar."); + } + // We have to initialize the buffer by creating a vector of the + // appropriate size. We have to use swap because scoped_ptr does not + // allow assignment (it is compatible with C++ 98 which does not have + // move semantics). + boost::scoped_ptr > v(new std::vector(initialValue)); + (*_buffers)[0].value.swap(v); + } + + /** + * Creates a process array that acts as a receiver. A receiver is + * intended to work as a tandem with a sender and receives values that + * have been set to the sender. + * + * This constructor creates the buffers and queues that are needed for the + * send/receive process and are shared with the sender. + * + * If the swappable option is true, the swap operation is + * allowed. The swappable flag must be the same for the + * sender and receiver. + */ + ProcessArrayImpl(InstanceType instanceType, const std::string& name, + const std::vector& initialValue, std::size_t numberOfBuffers, + bool swappable) : + ProcessVariable(name), _instanceType(instanceType), _vectorSize( + initialValue.size()), _swappable(swappable), _buffers( + boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( + boost::make_shared >( + numberOfBuffers)), _emptyBufferQueue( + boost::make_shared >( + numberOfBuffers)), _currentIndex(0), _lastSentIndex(0) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != RECEIVER) { + throw std::invalid_argument( + "This constructor may only be used for a receiver process scalar."); + } + // We need at least two buffers for the queue (so four buffers in total) + // in order to guarantee that we never have to block. + if (numberOfBuffers < 2) { + throw std::invalid_argument( + "The number of buffers must be at least two."); + } + // We have to limit the number of buffers because we cannot allocate + // more buffers than can be represented by size_t and the total number + // of buffers is the specified number plus two. These extra two buffers + // are needed because the sender and the receiver each hold one buffer + // at all times and thus only the rest remains for the queue. We use + // this definition of the number of buffers to keep it consistent with + // what ProcessScalarImpl uses. + if (numberOfBuffers > (std::numeric_limits::max() - 2)) { + throw std::invalid_argument("The number of buffers is too large."); + } + // We have to initialize the buffers by creating vectors of the + // appropriate size. We have to use swap because scoped_ptr does not + // allow assignment (it is compatible with C++ 98 which does not have + // move semantics). + for (typename std::vector::iterator i = _buffers->begin(); + i != _buffers->end(); ++i) { + boost::scoped_ptr > v( + new std::vector(initialValue)); + i->value.swap(v); + } + // The buffer with the index 0 is assigned to the receiver and the + // buffer with the index 1 is assigned to the sender. All buffers have + // to be added to the empty-buffer queue. + for (std::size_t i = 2; i < _buffers->size(); ++i) { + _emptyBufferQueue->push(i); + } + } + + /** + * Creates a process array that acts as a sender. A sender is intended + * intended to work as a tandem with a receiver and send set values to + * it. It uses the buffers and queues that have been created by the + * receiver. + * + * If the swappable option is true, the swap operation is + * allowed on the receiver. The swappable flag must be the + * same for the sender and receiver. Allowing swapping has the side effect + * that a value cannot be read after having been sent. + * + * The optional send-notification listener is notified every time the + * sender's {@link ProcessArrayImpl::send()} method is called. It can be + * used to queue a request for the receiver's + * {@link ProcessArrayImpl::receive()} method to be called. The process + * variable passed to the listener is the receiver and not the sender. + */ + ProcessArrayImpl(InstanceType instanceType, bool swappable, + boost::shared_ptr timeStampSource, + boost::shared_ptr sendNotificationListener, + boost::shared_ptr receiver) : + ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( + receiver->_vectorSize), _swappable(swappable), _buffers( + receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( + receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( + receiver), _timeStampSource(timeStampSource), _sendNotificationListener( + sendNotificationListener) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != SENDER) { + throw std::invalid_argument( + "This constructor may only be used for a sender process scalar."); + } + if (!receiver) { + throw std::invalid_argument( + "The pointer to the receiver must not be null."); + } + if (receiver->_instanceType != RECEIVER) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + } + + /** + * Assignment operator. This behaves excactly like the set(...) method. + */ + ProcessArrayImpl & operator=(ProcessArrayImpl const & other) { + set(other.getConst()); + return (*this); + } + + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ +// ProcessArrayImpl & operator=(ProcessArray const & other) { +// set(other.getConst()); +// return (*this); +// } + + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ + ProcessArrayImpl & operator=(std::vector const & v) { + set(v); + return *this; + } + + operator std::vector() const { + return getConst(); + } + + void set(std::vector const & v) { + get() = v; + } + + void set(ProcessArrayImpl const & other) { + set(other.getConst()); + } + + void swap(boost::scoped_ptr > & otherVector) { + if (!isSwappable()) { + throw std::logic_error( + "Swap is not supported by this process array."); + } + if (otherVector->size() != get().size()) { + throw std::runtime_error("Vector sizes do not match"); + } + (*_buffers)[_currentIndex].value.swap(otherVector); + } + + std::vector & get() { + if (_instanceType == RECEIVER && !_swappable) { + throw std::logic_error( + "Attempt to modify a read-only process variable."); + } + return *(((*_buffers)[_currentIndex]).value); + } + + std::vector const & get() const { + return getConst(); + } + + std::vector const & getConst() const { + return *(((*_buffers)[_currentIndex]).value); + } + + std::vector const & getLastSent() const { + if (_instanceType != SENDER || _swappable) { + throw std::logic_error( + "The last sent value is only available for a sender that is associated with a receiver that does not allow swapping."); + } + return *(((*_buffers)[_lastSentIndex]).value); + } + + bool isReceiver() const { + return _instanceType == RECEIVER; + } + + bool isSender() const { + return _instanceType == SENDER; + } + + TimeStamp getTimeStamp() const { + return ((*_buffers)[_currentIndex]).timeStamp; + } + + bool isSwappable() const { + // Senders and stand-alone instances are always swappable. For + // receivers, it depends on the swappable flag. + if (_instanceType == RECEIVER) { + return _swappable; + } else { + return true; + } + } + + bool receive() { + if (_instanceType != RECEIVER) { + throw std::logic_error( + "Receive operation is only allowed for a receiver process variable."); + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the sender might get into + // trouble when it suddenly experiences a vector of the wrong size. + if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + } + std::size_t nextIndex; + if (_fullBufferQueue->pop(nextIndex)) { + _emptyBufferQueue->push(_currentIndex); + _currentIndex = nextIndex; + return true; + } else { + return false; + } + } + + bool send() { + if (_instanceType != SENDER) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable."); + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the receiver might get into + // trouble when it suddenly experiences a vector of the wrong size. + if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + } + // Before sending the value, we have to update the associated + // time-stamp. + ((*_buffers)[_currentIndex]).timeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : + TimeStamp::currentTime(); + std::size_t nextIndex; + bool foundEmptyBuffer; + if (_emptyBufferQueue->pop(nextIndex)) { + foundEmptyBuffer = true; + _fullBufferQueue->push(_currentIndex); + } else { + // We can still send, but we will lose older data. + if (_fullBufferQueue->pop(nextIndex)) { + foundEmptyBuffer = false; + _fullBufferQueue->push(_currentIndex); + } else { + // It is possible, that we did not find an empty buffer but before + // we could get a full buffer, the receiver processed all full + // buffers. In this case, the queue of empty buffers cannot be empty + // any longer because we have at least four buffers. + if (!_emptyBufferQueue->pop(nextIndex)) { + // This should never happen and is just an assertion for extra + // safety. + throw std::runtime_error( + "Assertion that empty-buffer queue has at least one element failed."); + } + foundEmptyBuffer = true; + _fullBufferQueue->push(_currentIndex); + } + } + _lastSentIndex = _currentIndex; + _currentIndex = nextIndex; + if (_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); + } + return foundEmptyBuffer; + } + + const std::type_info& getValueType() const { + return typeid(T); + } + + bool isArray() const { + return true; + } + + + private: + + /** + * Type for the individual buffers. Each buffer stores a vector (wrapped + * in a Boost scoped pointer) and a time stamp. + */ + struct Buffer { + + TimeStamp timeStamp; + boost::scoped_ptr > value; + + /** + * Default constructor. Has to be defined explicitly because we have an + * non-default copy constructor. + */ + Buffer() { + } + + /** + * Copy constructor. Some STL implementations need the copy constructor + * while initializing the vector that stores the buffers: They do not + * default construct each element but only default construct the first + * element and then copy it. In this case, this copy constructor will + * work perfectly fine. It would even work if the value pointer was + * already initialized with a vector, however we might have a serious + * performance problem if that happened. Therefore, we use an assertion + * to check that the copy constructor is never used once the buffer has + * been initialized with a vector. + */ + Buffer(Buffer const & other) : + timeStamp(other.timeStamp), value( + other.value ? new std::vector(*(other.value)) : 0) { + assert(!(other.value)); + } + + }; + + /** + * Type this instance is representing. + */ + InstanceType _instanceType; + + /** + * Number of elements that each vector (and thus this array) has. + */ + std::size_t _vectorSize; + + /** + * Flag indicating the swapping behavior. For a receiver, it indicates + * whether swapping is allowed. For a sender, it indicates whether + * swapping is allowed on the corresponding receiver. For a stand-alone + * instance, it does not have any meaning. + */ + bool _swappable; + + /** + * Buffers that hold the actual values. + */ + boost::shared_ptr > _buffers; + + /** + * Queue holding the indices of the full buffers. Those are the buffers + * that have been sent but not yet received. We do not use an spsc_queue + * for this queue, because might want to take elements from the sending + * thread, so there are two threads which might consume elements and thus + * an spsc_queue is not safe. + */ + boost::shared_ptr > _fullBufferQueue; + + /** + * Queue holding the empty buffers. Those are the buffers that have been + * returned to the sender by the receiver. + */ + boost::shared_ptr > _emptyBufferQueue; + + /** + * Index into the _buffers array that is currently owned by this instance + * and used for read and (possibly) write operations. + */ + std::size_t _currentIndex; + + /** + * Index of the buffer that has been sent last. This index is only + * meaningful if this is a sender and swapping is not allowed on the + * receiver. + */ + std::size_t _lastSentIndex; + + /** + * Pointer to the receiver associated with this sender. This field is only + * used if this process variable represents a sender. + */ + boost::shared_ptr _receiver; + + /** + * Time-stamp source used to update the time-stamp when sending a value. + */ + boost::shared_ptr _timeStampSource; + + /** + * Listener that is notified when the process variable is sent. + */ + boost::shared_ptr _sendNotificationListener; + + }; + } // namespace impl - // temporary typedef template using ProcessArray = impl::ProcessArrayImpl; From 57d6f6a9e86aad36f2b92402c275489caf619689 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Tue, 28 Jun 2016 14:29:41 +0200 Subject: [PATCH 038/786] Moved the N_ELEMENTS constant outside the class definition so that it can be passed to the std::vector constructor and the assign method. The constructor and the assign method take a reference, an references to constants are not valid if they are only declared and initialized inside the class definition, but never defined. --- tests/src/testProcessArray.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 27afeb2..6b6c1f7 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -30,10 +30,14 @@ namespace mtca4u { private: static size_t const N_ELEMENTS = 12; - static size_t const SOME_NUMBER = 42; // just some number to add so the content if not equal to the index }; + // Just some number to add so the content is not equal to the index. We + // declare and define this constant outside of the ProcessArrayTest class, so + // that it can be used with references. + static size_t const SOME_NUMBER = 42; + /** * The boost test suite which executes the ProcessArrayTest. */ From e8d5db70123563d056ba4d8d4413aaf2e018cff0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 30 Jun 2016 19:18:33 +0200 Subject: [PATCH 039/786] fixed wrong FOUND_VAR (was by default _FOUND) --- cmake/FindPROJECT_NAME.cmake.in.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index b614b0f..7dbabf3 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -48,5 +48,5 @@ set(@PROJECT_NAME@_PREFIX "@CMAKE_INSTALL_PREFIX@") # This is mainly important to check the version. set(@PROJECT_NAME@_FOUND 0) include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION FOUND_VAR @PROJECT_NAME@_FOUND ) From 5d5cc20043aea8c3a64c967c4e3ef7edf162aafa Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jul 2016 13:00:10 +0200 Subject: [PATCH 040/786] added workaround for cmake versions older than 2.8.11 (as used on Ubuntu 12.04) --- cmake/FindPROJECT_NAME.cmake.in.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 7dbabf3..f1a3a4c 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -48,5 +48,13 @@ set(@PROJECT_NAME@_PREFIX "@CMAKE_INSTALL_PREFIX@") # This is mainly important to check the version. set(@PROJECT_NAME@_FOUND 0) include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION FOUND_VAR @PROJECT_NAME@_FOUND ) +# The FOUND_VAR option in FIND_PACKAGE_HANDLE_STANDARD_ARGS was introduced in cmake-2.8.11, but Ubuntu 12.04 has cmake-2.8.7 only. +# Thus we use a work around here for older cmake versions. +if("${CMAKE_VERSION}" VERSION_LESS 2.8.11) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION) + STRING(TOUPPER "@PROJECT_NAME@" PROJECT_NAME_UPPERCASE) + set(@PROJECT_NAME@_VERSION ${PROJECT_NAME_UPPERCASE}) +else() + FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION FOUND_VAR @PROJECT_NAME@_FOUND) +endif() From ce14dcd0f185a67e818242e58f1229aa2c703713 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jul 2016 17:19:26 +0200 Subject: [PATCH 041/786] Capture only coverage data for the current project in the first place, instead of extracting it from all coverage data in a second step. This seems to be needed as extraction spuriously might fail in some cases. --- cmake/Makefile.coverage.in | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmake/Makefile.coverage.in b/cmake/Makefile.coverage.in index afda667..89996e0 100644 --- a/cmake/Makefile.coverage.in +++ b/cmake/Makefile.coverage.in @@ -19,8 +19,5 @@ coverage: rm -f `find . -name "*\.gcda"` make test - lcov --capture --directory . --output-file coverage_all.info - #lcov capture also includes external stuff like glibc, boost etc. - #only extract the reports for this project - lcov --extract coverage_all.info "@CMAKE_SOURCE_DIR@*" -o coverage.info + lcov --capture --directory . --output-file coverage.info --no-external --base-directory "@CMAKE_SOURCE_DIR@" genhtml coverage.info --output-directory coverage_html From 34060d3da24bb78a09a6819867a7b8022b51da6c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 4 Jul 2016 10:24:48 +0200 Subject: [PATCH 042/786] Revert "Capture only coverage data for the current project in the first place, instead of extracting it from all coverage data in a second step. This seems to be needed as extraction spuriously might fail in some cases." This reverts commit ce14dcd0f185a67e818242e58f1229aa2c703713. Reason for the revert: The change did not work on Ubuntu 12.04 due to an old version of lcov. --- cmake/Makefile.coverage.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/Makefile.coverage.in b/cmake/Makefile.coverage.in index 89996e0..afda667 100644 --- a/cmake/Makefile.coverage.in +++ b/cmake/Makefile.coverage.in @@ -19,5 +19,8 @@ coverage: rm -f `find . -name "*\.gcda"` make test - lcov --capture --directory . --output-file coverage.info --no-external --base-directory "@CMAKE_SOURCE_DIR@" + lcov --capture --directory . --output-file coverage_all.info + #lcov capture also includes external stuff like glibc, boost etc. + #only extract the reports for this project + lcov --extract coverage_all.info "@CMAKE_SOURCE_DIR@*" -o coverage.info genhtml coverage.info --output-directory coverage_html From 9d95cb8f532dba71886e56e9ecceb7446ea65d73 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 5 Jul 2016 17:12:58 +0200 Subject: [PATCH 043/786] getting rid of some doxygen warnings (still many remaining) --- include/ProcessVariable.h | 6 +++--- include/ProcessVariableAccessor.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/ProcessVariable.h b/include/ProcessVariable.h index d05a27d..18ab2f0 100644 --- a/include/ProcessVariable.h +++ b/include/ProcessVariable.h @@ -34,7 +34,7 @@ namespace mtca4u { const std::string& getName() const; /** - * Returns the {@link std::type_info} for the value type of this process + * Returns the \c std::type_info for the value type of this process * variable or array. This can be used to determine the type of a process * variable at runtime. */ @@ -42,8 +42,8 @@ namespace mtca4u { /** * Returns true if this object is an instance of - * {@link ProcessArray} and false if this object is an - * instance of {@link ProcessScalar}. + * {@link ProcessArray}\ and false if this object is an + * instance of {@link ProcessScalar}\. */ virtual bool isArray() const =0; diff --git a/include/ProcessVariableAccessor.h b/include/ProcessVariableAccessor.h index 6155257..b568887 100644 --- a/include/ProcessVariableAccessor.h +++ b/include/ProcessVariableAccessor.h @@ -15,7 +15,7 @@ namespace mtca4u { } /** - * Returns the {@link std::type_info} for the value type of this process + * Returns the \c std::type_info for the value type of this process * variable or array. This can be used to determine the type of a process * variable at runtime. */ @@ -25,8 +25,8 @@ namespace mtca4u { /** * Returns true if the implementation is an instance of - * {@link ProcessArray} and false if this object is an - * instance of {@link ProcessScalar}. + * {@link ProcessArray}\ and false if this object is an + * instance of {@link ProcessScalar}\. */ bool isArray() const{ return _impl->isArray(); From 2d7274d0089220925a60d11aa8d7630169e09917 Mon Sep 17 00:00:00 2001 From: killenb Date: Fri, 8 Jul 2016 20:42:37 +0200 Subject: [PATCH 044/786] added exclution of the build directory to the coverage makefile --- cmake/Makefile.coverage.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/Makefile.coverage.in b/cmake/Makefile.coverage.in index afda667..82babda 100644 --- a/cmake/Makefile.coverage.in +++ b/cmake/Makefile.coverage.in @@ -22,5 +22,13 @@ coverage: lcov --capture --directory . --output-file coverage_all.info #lcov capture also includes external stuff like glibc, boost etc. #only extract the reports for this project - lcov --extract coverage_all.info "@CMAKE_SOURCE_DIR@*" -o coverage.info + lcov --extract coverage_all.info "@CMAKE_SOURCE_DIR@*" -o coverage_incl_build.info + #Some projects install the library and its headers for testing in the build directory + #and compile tests and examples against it. This leads to double + #counting and untested lines in the coverage report. That's why we + #exclude the build directory from the coverage report (needed if the + #build directory is in the source tree). + #Attention: If you make an 'in sorce build' the coverage report will + #be empty! + lcov --remove coverage_incl_build.info "@PROJECT_BINARY_DIR@/*" -o coverage.info genhtml coverage.info --output-directory coverage_html From c1e12adde3eb97ec6b11b74f3cd0cc99d4a719ad Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 13 Jul 2016 10:30:29 +0200 Subject: [PATCH 045/786] started extending the array accessor. Impl copied from deviceaccess, tests pending --- include/ProcessArrayAccessor.h | 44 +++++++++++++++++++++++++++++----- tests/src/testPVAccessors.cpp | 5 ++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 9747a5c..1317d19 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -5,20 +5,52 @@ #include "ProcessVariableAccessor.h" namespace mtca4u{ - template + template class ProcessArrayAccessor: public ProcessVariableAccessor{ public: - ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = - typename ProcessArray::SharedPtr()) + ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = + typename ProcessArray::SharedPtr()) : ProcessVariableAccessor(processArray){ - // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ + // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ } - std::vector & get(){ - return boost::static_pointer_cast< ProcessArray >(_impl)->get(); + std::vector & get(){ + return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } + UserType& operator[](unsigned int element) { + return get()[element]; + } + + /** Access data with std::vector-like iterators */ + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::reverse_iterator reverse_iterator; + typedef typename std::vector::const_reverse_iterator const_reverse_iterator; + + iterator begin() { return get().begin(); } + const_iterator begin() const { return get().cbegin(); } + const_iterator cbegin() const { return get().cbegin(); } + iterator end() { return get().end(); } + const_iterator end() const { return get().cend(); } + const_iterator cend() const { return get().cend(); } + reverse_iterator rbegin() { return get().rbegin(); } + const_reverse_iterator rbegin() const { return get().crbegin(); } + const_reverse_iterator crbegin() const { return get().crbegin(); } + reverse_iterator rend() { return get().rend(); } + const_reverse_iterator rend() const { return get().crend(); } + const_reverse_iterator crend() const { return get().crend(); } + + /* Swap content of (cooked) buffer with std::vector */ + void swap(std::vector &x) { + boost::static_pointer_cast< ProcessArray >(_impl)->swap(x); + } + + UserType* data() { + return get().data(); + } + }; } //namespace mtca4u diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index dd3457e..624416d 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -154,4 +154,9 @@ BOOST_AUTO_TEST_CASE( testArrayCreation ){ BOOST_CHECK( thirdIntArrayAccessor.get()[0] == 46 ); } +BOOST_AUTO_TEST_CASE( testArrayOperations ){ + auto firstIntArray = createSimpleProcessArray(10,"first", 42); + ProcessArrayAccessor arrayIntAccessor( firstIntArray ); +} + BOOST_AUTO_TEST_SUITE_END() From 6d033bf901c23b89db338b74296bd4dc69c3bcdf Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 14 Jul 2016 18:14:17 +0200 Subject: [PATCH 046/786] testing getting iterators through the accessor --- include/ProcessArrayAccessor.h | 24 ++++++++---- tests/src/testPVAccessors.cpp | 71 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 1317d19..62fe069 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -18,6 +18,12 @@ namespace mtca4u{ std::vector & get(){ return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } + std::vector const & get() const{ + return boost::static_pointer_cast< ProcessArray >(_impl)->getConst(); + } + std::vector const & getConst() const{ + return boost::static_pointer_cast< ProcessArray >(_impl)->getConst(); + } UserType& operator[](unsigned int element) { return get()[element]; @@ -30,17 +36,19 @@ namespace mtca4u{ typedef typename std::vector::const_reverse_iterator const_reverse_iterator; iterator begin() { return get().begin(); } - const_iterator begin() const { return get().cbegin(); } - const_iterator cbegin() const { return get().cbegin(); } + const_iterator begin() const { return getConst().cbegin(); } + const_iterator cbegin() const { return getConst().cbegin(); } iterator end() { return get().end(); } - const_iterator end() const { return get().cend(); } - const_iterator cend() const { return get().cend(); } + const_iterator end() const { return getConst().cend(); } + const_iterator cend() const { return getConst().cend(); } reverse_iterator rbegin() { return get().rbegin(); } - const_reverse_iterator rbegin() const { return get().crbegin(); } - const_reverse_iterator crbegin() const { return get().crbegin(); } + const_reverse_iterator rbegin() const { return getConst().crbegin(); } + const_reverse_iterator crbegin() const { return getConst().crbegin(); } reverse_iterator rend() { return get().rend(); } - const_reverse_iterator rend() const { return get().crend(); } - const_reverse_iterator crend() const { return get().crend(); } + const_reverse_iterator rend() const { return getConst().crend(); } + const_reverse_iterator crend() const { return getConst().crend(); } + + size_t size(){ return get().size(); } /* Swap content of (cooked) buffer with std::vector */ void swap(std::vector &x) { diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 624416d..b692e03 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -157,6 +157,77 @@ BOOST_AUTO_TEST_CASE( testArrayCreation ){ BOOST_AUTO_TEST_CASE( testArrayOperations ){ auto firstIntArray = createSimpleProcessArray(10,"first", 42); ProcessArrayAccessor arrayIntAccessor( firstIntArray ); + + // get the underlying vector to prepare it + int i = 45; + for ( auto & c : arrayIntAccessor.get() ){ + c = i++; + } + + // test the [] operator + for (int j = 0; static_cast(j) < arrayIntAccessor.size(); ++j){ + BOOST_CHECK( arrayIntAccessor[j] == 45+j ); + } + + // test the accessors. Start with modifiying the array. + // NOTE: We are not really testing the accessors themselves, but that we get the + // right one, so that we don't accidentally return the reverst iterator instead of + // the forward one. + for ( auto & c : arrayIntAccessor ){ + c++; + } + // Check with the [] operator, wich we already verified + for (int j = 0; static_cast(j) < arrayIntAccessor.size(); ++j){ + BOOST_CHECK( arrayIntAccessor[j] == 46+j ); + } + + // now recheck with the const iterator. Use a const object to check that it's really const. + ProcessArrayAccessor const & constArrayIntAccessor = arrayIntAccessor; + i = 46; + for (auto it = constArrayIntAccessor.cbegin(); it < constArrayIntAccessor.cend(); ++it ){ + BOOST_CHECK( (*it) == i++ ); + } + + // also check the const begin and end iterators + i = 46; + for (auto it = constArrayIntAccessor.begin(); it < constArrayIntAccessor.end(); ++it ){ + BOOST_CHECK( (*it) == i++ ); + } + + // also try the const get methods + i = 46; + for ( auto & c : constArrayIntAccessor.get() ){ + BOOST_CHECK( c == i++ ); + } + i = 46; + for ( auto & c : constArrayIntAccessor.getConst() ){ + BOOST_CHECK( c == i++ ); + } + + // Now test the reverse iterators + // Modify with the writeable one + i =78; + for (auto it = arrayIntAccessor.rbegin(); it < arrayIntAccessor.rend(); ++it){ + (*it) = i++; + } + + // check with normal, already testes iterators + i = 87; + for (auto const & c : arrayIntAccessor){ + BOOST_CHECK( c == i-- ); + } + + // check with const reverse iterators + i = 78; + for (auto it = constArrayIntAccessor.rbegin(); it < constArrayIntAccessor.rend(); ++it){ + BOOST_CHECK( (*it) == i++ ); + } + + i = 78; + for (auto it = constArrayIntAccessor.crbegin(); it < constArrayIntAccessor.crend(); ++it){ + BOOST_CHECK( (*it) == i++ ); + } + } BOOST_AUTO_TEST_SUITE_END() From 4799a18ac3ca2547a741fc3bf70972414dac67ff Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 14 Jul 2016 18:53:18 +0200 Subject: [PATCH 047/786] Implemented all tests for the array accessor --- include/ProcessArrayAccessor.h | 8 ++++++-- tests/src/testPVAccessors.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 62fe069..25b4bf7 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -48,17 +48,21 @@ namespace mtca4u{ const_reverse_iterator rend() const { return getConst().crend(); } const_reverse_iterator crend() const { return getConst().crend(); } - size_t size(){ return get().size(); } + size_t size() const{ return get().size(); } /* Swap content of (cooked) buffer with std::vector */ void swap(std::vector &x) { - boost::static_pointer_cast< ProcessArray >(_impl)->swap(x); + get().swap(x); } UserType* data() { return get().data(); } + UserType const * data() const{ + return get().data(); + } + }; } //namespace mtca4u diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index b692e03..7097b7c 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -227,6 +227,35 @@ BOOST_AUTO_TEST_CASE( testArrayOperations ){ for (auto it = constArrayIntAccessor.crbegin(); it < constArrayIntAccessor.crend(); ++it){ BOOST_CHECK( (*it) == i++ ); } + + // test swapping with a vector + std::vector v(10,42); + arrayIntAccessor.swap(v); + for (auto & c : constArrayIntAccessor ){ + BOOST_CHECK( c == 42 ); + } + i= 87; + for (auto const & c : v){ + BOOST_CHECK( c == i-- ); + } + + // set using the direct pointer + int * ptr = arrayIntAccessor.data(); + for (size_t j = 0; j < arrayIntAccessor.size(); ++j){ + ptr[j] = 13 + j; + } + + // check with iterator (already verified) + i = 13; + for (auto & c : constArrayIntAccessor ){ + BOOST_CHECK( c == i++ ); + } + + int const * cptr = constArrayIntAccessor.data(); + for (size_t j = 0; j < constArrayIntAccessor.size(); ++j){ + BOOST_CHECK( cptr[j] == static_cast(13 + j) ); + } + } From faf257b78c9a9400df02a673ac609f74c53b473a Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 14 Jul 2016 21:51:10 +0200 Subject: [PATCH 048/786] code cleanup and moving/update of the documentation for ProcessScalar --- include/ProcessScalar.h | 137 ++++++++++++---------------------------- 1 file changed, 42 insertions(+), 95 deletions(-) diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index ab35d55..799b4fb 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -19,91 +19,12 @@ #include "ProcessVariableListener.h" namespace mtca4u { - /** - * Interface implemented by all process scalars. - * - * Instances implementing this interface are typically not thread-safe and - * should only be used from a single thread. - */ - template - class ProcessScalarHowItLookedLike: public ProcessVariable { - public: - /** - * Assign the content of another process variable of type T to this one. - * It only assigns the variable content, but not the callback functions. - * This operator behaves like set(). - */ - // ProcessScalar & operator=(ProcessScalar const & other) { - // this->set(other); - // return *this; - // } - - /** - * Assign the content of the template type. This operator behaves like - * set(). - */ - // ProcessScalar & operator=(T const & t) { - // this->set(t); - // return *this; - // } - - /** - * Set the value of this process variable to the one of the other process - * variable. This does not trigger the on-set callback function, however it - * notifies the control system that this process variable's value has - * changed. - */ - // virtual void set(ProcessScalar const & other)=0; - - /** - * Set the value of this process variable to the specified one. This does - * not trigger the on-set callback function, however it notifies the control - * system that this process variable's value has changed. - */ - //virtual void set(T const & t)=0; - - /** - * Automatic conversion operator which returns a \b copy of this process - * variable's value. As no reference is returned, this cannot be used for - * assignment. - */ - //virtual operator T() const =0; - - /** - * Returns a \b copy of this process variable's value. As no reference is - * returned, this cannot be used for assignment. - */ - //virtual T get() const =0; - - // const std::type_info& getValueType() const { - // return typeid(T); - // } - - // bool isArray() const { - // return false; - // } - - protected: - /** - * Creates a process scalar with the specified name. - */ - //ProcessScalar(const std::string& name = std::string()) : - // ProcessVariable(name) { - // } - - /** - * Protected destructor. Instances should not be destroyed through - * pointers to this base type. - */ - // virtual ~ProcessScalar() { - // } - - }; - /** - * Implementation of the ProcessScalar. This implementation is used for all + * The scalar implementation of a ProcessVariable. This implementation is used for all * three use cases (sender, receiver, and stand-alone). + * + * This class is not thread-safe and should only be used from a single thread. */ template class ProcessScalar: public ProcessVariable { @@ -237,7 +158,12 @@ namespace mtca4u { } /** - * Assignment operator. This behaves excactly like the set(...) method. + * Assign the content of another process variable of type T to this one. + * It only assigns the variable content, but not the callback functions. + * This operator behaves like set(). + * + * FIXME: Should we remove this? It might not behave as + * expected, and the base class is intentionally non-copyable. */ ProcessScalar & operator=(ProcessScalar const & other) { set(other.get()); @@ -245,27 +171,29 @@ namespace mtca4u { } /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - // currently commented out while removing the impl-inheritance. will be needed later - // in the pimpl pattern - // ProcessScalar & operator=(ProcessScalar const & other) { - // set(other.get()); - // return (*this); - // } - - /** - * Assignment operator. This behaves exactly like the set(...) method. + * Assign the content of the template type. This operator behaves like + * set(). */ ProcessScalar & operator=(T const & t) { set(t); return *this; } - operator T() const { + + /** + * Automatic conversion operator which returns a \b copy of this process + * variable's value. As no reference is returned, this cannot be used for + * assignment. + */ + operator T() const { return get(); } + /** + * Set the value of this process variable to the specified one. This does + * not trigger the on-set callback function, however it notifies the control + * system that this process variable's value has changed. + */ void set(T const & t) { _value = t; } @@ -280,14 +208,27 @@ namespace mtca4u { return false; } + /** + * Set the value of this process variable to the one of the other process + * variable. This does not trigger the on-set callback function, however it + * notifies the control system that this process variable's value has + * changed. + */ void set(ProcessScalar const & other) { set(other.get()); } + /** + * Returns a \b copy of this process variable's value. As no reference is + * returned, this cannot be used for assignment. + */ T get() const { return _value; } + // ----------------------------------------------- + // Implementations of the derrived functions. They are documented in ProcessVariable + bool isReceiver() const { return _instanceType == RECEIVER; } @@ -296,6 +237,7 @@ namespace mtca4u { return _instanceType == SENDER; } + // deprecated TimeStamp getTimeStamp() const { return _timeStamp; } @@ -452,6 +394,11 @@ namespace mtca4u { boost::shared_ptr sendNotificationListener = boost::shared_ptr()); + /** + * A process variable which is not a sender/receiver pair but a single instance. + * You can neither send nor receive it. + * FIXME: This sounds pretty useless. Should we remove it? + */ template typename ProcessScalar::SharedPtr createSimpleProcessScalar( const std::string & name, T initialValue) { From 731cf114a81075dc6cb1968b90e475132e8c9b6a Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 14 Jul 2016 22:04:41 +0200 Subject: [PATCH 049/786] renamed ProcessArrayImpl to ProcessArray and moved it out of the impl namespace --- include/ProcessArray.h | 800 ++++++++++++++++++++--------------------- 1 file changed, 393 insertions(+), 407 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index fb24c52..bdcdf66 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -175,15 +175,13 @@ namespace mtca4u { }; - namespace impl { - - /** - * Implementation of the ProcessArray. This implementation is used for all - * three use cases (sender, receiver, and stand-alone). - */ - template - class ProcessArrayImpl: public ProcessVariable { - + /** + * Implementation of the ProcessArray. This implementation is used for all + * three use cases (sender, receiver, and stand-alone). + */ + template + class ProcessArray: public ProcessVariable { + // // Allow the factory functions to create instances of this class. // friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< // T>(std::size_t, const std::string &, T); @@ -202,451 +200,439 @@ namespace mtca4u { // T>(const std::vector&, const std::string &, bool, std::size_t, // boost::shared_ptr, // boost::shared_ptr); - - public: - - typedef boost::shared_ptr SharedPtr; - + + public: + + typedef boost::shared_ptr SharedPtr; + + /** + * Type of the instance. This defines the behavior (send or receive + * possible, modifications allowed, etc.). This enum type is private so + * that only friend functions can construct instances of this class. The + * constructors themselves have to be public so that boost::make_shared + * can be used. + */ + enum InstanceType { /** - * Type of the instance. This defines the behavior (send or receive - * possible, modifications allowed, etc.). This enum type is private so - * that only friend functions can construct instances of this class. The - * constructors themselves have to be public so that boost::make_shared - * can be used. + * Instance acts on is own. */ - enum InstanceType { - /** - * Instance acts on is own. - */ - STAND_ALONE, - - /** - * Instance acts as the sender in a sender / receiver pair. - */ - SENDER, - - /** - * Instance acts as the receiver in a sender / receiver pair. - */ - RECEIVER - }; - - public: + STAND_ALONE, + /** - * Creates a process array that works independently. This means that the - * instance is not synchronized with any other instance and thus the send - * and receive operations are not supported. However, all other operations - * can be used like on any process variable. + * Instance acts as the sender in a sender / receiver pair. */ - ProcessArrayImpl(InstanceType instanceType, const std::string& name, - const std::vector& initialValue) : - ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(true), _buffers( - boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( - 0) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != STAND_ALONE) { - throw std::invalid_argument( - "This constructor may only be used for a stand-alone process scalar."); - } - // We have to initialize the buffer by creating a vector of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). - boost::scoped_ptr > v(new std::vector(initialValue)); - (*_buffers)[0].value.swap(v); - } - + SENDER, + /** - * Creates a process array that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. - * - * If the swappable option is true, the swap operation is - * allowed. The swappable flag must be the same for the - * sender and receiver. + * Instance acts as the receiver in a sender / receiver pair. */ - ProcessArrayImpl(InstanceType instanceType, const std::string& name, - const std::vector& initialValue, std::size_t numberOfBuffers, - bool swappable) : - ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(swappable), _buffers( - boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( - boost::make_shared >( - numberOfBuffers)), _emptyBufferQueue( - boost::make_shared >( - numberOfBuffers)), _currentIndex(0), _lastSentIndex(0) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument( - "This constructor may only be used for a receiver process scalar."); - } - // We need at least two buffers for the queue (so four buffers in total) - // in order to guarantee that we never have to block. - if (numberOfBuffers < 2) { - throw std::invalid_argument( - "The number of buffers must be at least two."); - } - // We have to limit the number of buffers because we cannot allocate - // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - 2)) { - throw std::invalid_argument("The number of buffers is too large."); - } - // We have to initialize the buffers by creating vectors of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). - for (typename std::vector::iterator i = _buffers->begin(); - i != _buffers->end(); ++i) { - boost::scoped_ptr > v( - new std::vector(initialValue)); - i->value.swap(v); - } - // The buffer with the index 0 is assigned to the receiver and the - // buffer with the index 1 is assigned to the sender. All buffers have - // to be added to the empty-buffer queue. - for (std::size_t i = 2; i < _buffers->size(); ++i) { - _emptyBufferQueue->push(i); - } + RECEIVER + }; + + public: + /** + * Creates a process array that works independently. This means that the + * instance is not synchronized with any other instance and thus the send + * and receive operations are not supported. However, all other operations + * can be used like on any process variable. + */ + ProcessArray(InstanceType instanceType, const std::string& name, + const std::vector& initialValue) : + ProcessVariable(name), _instanceType(instanceType), _vectorSize( + initialValue.size()), _swappable(true), _buffers( + boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( + 0) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != STAND_ALONE) { + throw std::invalid_argument( + "This constructor may only be used for a stand-alone process scalar."); } + // We have to initialize the buffer by creating a vector of the + // appropriate size. We have to use swap because scoped_ptr does not + // allow assignment (it is compatible with C++ 98 which does not have + // move semantics). + boost::scoped_ptr > v(new std::vector(initialValue)); + (*_buffers)[0].value.swap(v); + } - /** - * Creates a process array that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the swappable option is true, the swap operation is - * allowed on the receiver. The swappable flag must be the - * same for the sender and receiver. Allowing swapping has the side effect - * that a value cannot be read after having been sent. - * - * The optional send-notification listener is notified every time the - * sender's {@link ProcessArrayImpl::send()} method is called. It can be - * used to queue a request for the receiver's - * {@link ProcessArrayImpl::receive()} method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - ProcessArrayImpl(InstanceType instanceType, bool swappable, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener, - boost::shared_ptr receiver) : - ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( - receiver->_vectorSize), _swappable(swappable), _buffers( - receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( - receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( - receiver), _timeStampSource(timeStampSource), _sendNotificationListener( - sendNotificationListener) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument( - "This constructor may only be used for a sender process scalar."); - } - if (!receiver) { - throw std::invalid_argument( - "The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } + /** + * Creates a process array that acts as a receiver. A receiver is + * intended to work as a tandem with a sender and receives values that + * have been set to the sender. + * + * This constructor creates the buffers and queues that are needed for the + * send/receive process and are shared with the sender. + * + * If the swappable option is true, the swap operation is + * allowed. The swappable flag must be the same for the + * sender and receiver. + */ + ProcessArray(InstanceType instanceType, const std::string& name, + const std::vector& initialValue, std::size_t numberOfBuffers, + bool swappable) : + ProcessVariable(name), _instanceType(instanceType), _vectorSize( + initialValue.size()), _swappable(swappable), _buffers( + boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( + boost::make_shared >( + numberOfBuffers)), _emptyBufferQueue( + boost::make_shared >( + numberOfBuffers)), _currentIndex(0), _lastSentIndex(0) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != RECEIVER) { + throw std::invalid_argument( + "This constructor may only be used for a receiver process scalar."); + } + // We need at least two buffers for the queue (so four buffers in total) + // in order to guarantee that we never have to block. + if (numberOfBuffers < 2) { + throw std::invalid_argument( + "The number of buffers must be at least two."); + } + // We have to limit the number of buffers because we cannot allocate + // more buffers than can be represented by size_t and the total number + // of buffers is the specified number plus two. These extra two buffers + // are needed because the sender and the receiver each hold one buffer + // at all times and thus only the rest remains for the queue. We use + // this definition of the number of buffers to keep it consistent with + // what ProcessScalarImpl uses. + if (numberOfBuffers > (std::numeric_limits::max() - 2)) { + throw std::invalid_argument("The number of buffers is too large."); } + // We have to initialize the buffers by creating vectors of the + // appropriate size. We have to use swap because scoped_ptr does not + // allow assignment (it is compatible with C++ 98 which does not have + // move semantics). + for (typename std::vector::iterator i = _buffers->begin(); + i != _buffers->end(); ++i) { + boost::scoped_ptr > v( + new std::vector(initialValue)); + i->value.swap(v); + } + // The buffer with the index 0 is assigned to the receiver and the + // buffer with the index 1 is assigned to the sender. All buffers have + // to be added to the empty-buffer queue. + for (std::size_t i = 2; i < _buffers->size(); ++i) { + _emptyBufferQueue->push(i); + } + } - /** - * Assignment operator. This behaves excactly like the set(...) method. - */ - ProcessArrayImpl & operator=(ProcessArrayImpl const & other) { - set(other.getConst()); - return (*this); + /** + * Creates a process array that acts as a sender. A sender is intended + * intended to work as a tandem with a receiver and send set values to + * it. It uses the buffers and queues that have been created by the + * receiver. + * + * If the swappable option is true, the swap operation is + * allowed on the receiver. The swappable flag must be the + * same for the sender and receiver. Allowing swapping has the side effect + * that a value cannot be read after having been sent. + * + * The optional send-notification listener is notified every time the + * sender's {@link ProcessArray::send()} method is called. It can be + * used to queue a request for the receiver's + * {@link ProcessArray::receive()} method to be called. The process + * variable passed to the listener is the receiver and not the sender. + */ + ProcessArray(InstanceType instanceType, bool swappable, + boost::shared_ptr timeStampSource, + boost::shared_ptr sendNotificationListener, + boost::shared_ptr receiver) : + ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( + receiver->_vectorSize), _swappable(swappable), _buffers( + receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( + receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( + receiver), _timeStampSource(timeStampSource), _sendNotificationListener( + sendNotificationListener) { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != SENDER) { + throw std::invalid_argument( + "This constructor may only be used for a sender process scalar."); + } + if (!receiver) { + throw std::invalid_argument( + "The pointer to the receiver must not be null."); + } + if (receiver->_instanceType != RECEIVER) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); } + } - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ -// ProcessArrayImpl & operator=(ProcessArray const & other) { -// set(other.getConst()); -// return (*this); -// } + /** + * Assignment operator. This behaves excactly like the set(...) method. + */ + ProcessArray & operator=(ProcessArray const & other) { + set(other.getConst()); + return (*this); + } - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - ProcessArrayImpl & operator=(std::vector const & v) { - set(v); - return *this; - } + /** + * Assignment operator. This behaves exactly like the set(...) method. + */ + ProcessArray & operator=(std::vector const & v) { + set(v); + return *this; + } - operator std::vector() const { - return getConst(); - } + operator std::vector() const { + return getConst(); + } - void set(std::vector const & v) { - get() = v; - } + void set(std::vector const & v) { + get() = v; + } - void set(ProcessArrayImpl const & other) { - set(other.getConst()); - } + void set(ProcessArray const & other) { + set(other.getConst()); + } - void swap(boost::scoped_ptr > & otherVector) { - if (!isSwappable()) { - throw std::logic_error( - "Swap is not supported by this process array."); - } - if (otherVector->size() != get().size()) { - throw std::runtime_error("Vector sizes do not match"); - } - (*_buffers)[_currentIndex].value.swap(otherVector); + void swap(boost::scoped_ptr > & otherVector) { + if (!isSwappable()) { + throw std::logic_error( + "Swap is not supported by this process array."); } - - std::vector & get() { - if (_instanceType == RECEIVER && !_swappable) { - throw std::logic_error( - "Attempt to modify a read-only process variable."); - } - return *(((*_buffers)[_currentIndex]).value); + if (otherVector->size() != get().size()) { + throw std::runtime_error("Vector sizes do not match"); } + (*_buffers)[_currentIndex].value.swap(otherVector); + } - std::vector const & get() const { - return getConst(); + std::vector & get() { + if (_instanceType == RECEIVER && !_swappable) { + throw std::logic_error( + "Attempt to modify a read-only process variable."); } + return *(((*_buffers)[_currentIndex]).value); + } - std::vector const & getConst() const { - return *(((*_buffers)[_currentIndex]).value); - } + std::vector const & get() const { + return getConst(); + } - std::vector const & getLastSent() const { - if (_instanceType != SENDER || _swappable) { - throw std::logic_error( - "The last sent value is only available for a sender that is associated with a receiver that does not allow swapping."); - } - return *(((*_buffers)[_lastSentIndex]).value); - } + std::vector const & getConst() const { + return *(((*_buffers)[_currentIndex]).value); + } - bool isReceiver() const { - return _instanceType == RECEIVER; + std::vector const & getLastSent() const { + if (_instanceType != SENDER || _swappable) { + throw std::logic_error( + "The last sent value is only available for a sender that is associated with a receiver that does not allow swapping."); } + return *(((*_buffers)[_lastSentIndex]).value); + } - bool isSender() const { - return _instanceType == SENDER; - } + bool isReceiver() const { + return _instanceType == RECEIVER; + } - TimeStamp getTimeStamp() const { - return ((*_buffers)[_currentIndex]).timeStamp; - } + bool isSender() const { + return _instanceType == SENDER; + } - bool isSwappable() const { - // Senders and stand-alone instances are always swappable. For - // receivers, it depends on the swappable flag. - if (_instanceType == RECEIVER) { - return _swappable; - } else { - return true; - } + TimeStamp getTimeStamp() const { + return ((*_buffers)[_currentIndex]).timeStamp; + } + + bool isSwappable() const { + // Senders and stand-alone instances are always swappable. For + // receivers, it depends on the swappable flag. + if (_instanceType == RECEIVER) { + return _swappable; + } else { + return true; } + } - bool receive() { - if (_instanceType != RECEIVER) { - throw std::logic_error( - "Receive operation is only allowed for a receiver process variable."); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the sender might get into - // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); - } - std::size_t nextIndex; - if (_fullBufferQueue->pop(nextIndex)) { - _emptyBufferQueue->push(_currentIndex); - _currentIndex = nextIndex; - return true; - } else { - return false; - } + bool receive() { + if (_instanceType != RECEIVER) { + throw std::logic_error( + "Receive operation is only allowed for a receiver process variable."); } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the sender might get into + // trouble when it suddenly experiences a vector of the wrong size. + if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + } + std::size_t nextIndex; + if (_fullBufferQueue->pop(nextIndex)) { + _emptyBufferQueue->push(_currentIndex); + _currentIndex = nextIndex; + return true; + } else { + return false; + } + } - bool send() { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable."); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the receiver might get into - // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); - } - // Before sending the value, we have to update the associated - // time-stamp. - ((*_buffers)[_currentIndex]).timeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - std::size_t nextIndex; - bool foundEmptyBuffer; - if (_emptyBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = true; + bool send() { + if (_instanceType != SENDER) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable."); + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the receiver might get into + // trouble when it suddenly experiences a vector of the wrong size. + if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + } + // Before sending the value, we have to update the associated + // time-stamp. + ((*_buffers)[_currentIndex]).timeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : + TimeStamp::currentTime(); + std::size_t nextIndex; + bool foundEmptyBuffer; + if (_emptyBufferQueue->pop(nextIndex)) { + foundEmptyBuffer = true; + _fullBufferQueue->push(_currentIndex); + } else { + // We can still send, but we will lose older data. + if (_fullBufferQueue->pop(nextIndex)) { + foundEmptyBuffer = false; _fullBufferQueue->push(_currentIndex); } else { - // We can still send, but we will lose older data. - if (_fullBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = false; - _fullBufferQueue->push(_currentIndex); - } else { - // It is possible, that we did not find an empty buffer but before - // we could get a full buffer, the receiver processed all full - // buffers. In this case, the queue of empty buffers cannot be empty - // any longer because we have at least four buffers. - if (!_emptyBufferQueue->pop(nextIndex)) { - // This should never happen and is just an assertion for extra - // safety. - throw std::runtime_error( - "Assertion that empty-buffer queue has at least one element failed."); - } - foundEmptyBuffer = true; - _fullBufferQueue->push(_currentIndex); + // It is possible, that we did not find an empty buffer but before + // we could get a full buffer, the receiver processed all full + // buffers. In this case, the queue of empty buffers cannot be empty + // any longer because we have at least four buffers. + if (!_emptyBufferQueue->pop(nextIndex)) { + // This should never happen and is just an assertion for extra + // safety. + throw std::runtime_error( + "Assertion that empty-buffer queue has at least one element failed."); } + foundEmptyBuffer = true; + _fullBufferQueue->push(_currentIndex); } - _lastSentIndex = _currentIndex; - _currentIndex = nextIndex; - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - return foundEmptyBuffer; } - - const std::type_info& getValueType() const { - return typeid(T); + _lastSentIndex = _currentIndex; + _currentIndex = nextIndex; + if (_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); } - - bool isArray() const { + return foundEmptyBuffer; + } + + const std::type_info& getValueType() const { + return typeid(T); + } + + bool isArray() const { return true; - } - - - private: - - /** - * Type for the individual buffers. Each buffer stores a vector (wrapped - * in a Boost scoped pointer) and a time stamp. - */ - struct Buffer { - - TimeStamp timeStamp; - boost::scoped_ptr > value; + } + - /** - * Default constructor. Has to be defined explicitly because we have an - * non-default copy constructor. - */ - Buffer() { - } + private: - /** - * Copy constructor. Some STL implementations need the copy constructor - * while initializing the vector that stores the buffers: They do not - * default construct each element but only default construct the first - * element and then copy it. In this case, this copy constructor will - * work perfectly fine. It would even work if the value pointer was - * already initialized with a vector, however we might have a serious - * performance problem if that happened. Therefore, we use an assertion - * to check that the copy constructor is never used once the buffer has - * been initialized with a vector. - */ - Buffer(Buffer const & other) : - timeStamp(other.timeStamp), value( - other.value ? new std::vector(*(other.value)) : 0) { - assert(!(other.value)); - } + /** + * Type for the individual buffers. Each buffer stores a vector (wrapped + * in a Boost scoped pointer) and a time stamp. + */ + struct Buffer { - }; + TimeStamp timeStamp; + boost::scoped_ptr > value; /** - * Type this instance is representing. + * Default constructor. Has to be defined explicitly because we have an + * non-default copy constructor. */ - InstanceType _instanceType; + Buffer() { + } /** - * Number of elements that each vector (and thus this array) has. + * Copy constructor. Some STL implementations need the copy constructor + * while initializing the vector that stores the buffers: They do not + * default construct each element but only default construct the first + * element and then copy it. In this case, this copy constructor will + * work perfectly fine. It would even work if the value pointer was + * already initialized with a vector, however we might have a serious + * performance problem if that happened. Therefore, we use an assertion + * to check that the copy constructor is never used once the buffer has + * been initialized with a vector. */ - std::size_t _vectorSize; + Buffer(Buffer const & other) : + timeStamp(other.timeStamp), value( + other.value ? new std::vector(*(other.value)) : 0) { + assert(!(other.value)); + } - /** - * Flag indicating the swapping behavior. For a receiver, it indicates - * whether swapping is allowed. For a sender, it indicates whether - * swapping is allowed on the corresponding receiver. For a stand-alone - * instance, it does not have any meaning. - */ - bool _swappable; + }; - /** - * Buffers that hold the actual values. - */ - boost::shared_ptr > _buffers; + /** + * Type this instance is representing. + */ + InstanceType _instanceType; - /** - * Queue holding the indices of the full buffers. Those are the buffers - * that have been sent but not yet received. We do not use an spsc_queue - * for this queue, because might want to take elements from the sending - * thread, so there are two threads which might consume elements and thus - * an spsc_queue is not safe. - */ - boost::shared_ptr > _fullBufferQueue; + /** + * Number of elements that each vector (and thus this array) has. + */ + std::size_t _vectorSize; - /** - * Queue holding the empty buffers. Those are the buffers that have been - * returned to the sender by the receiver. - */ - boost::shared_ptr > _emptyBufferQueue; + /** + * Flag indicating the swapping behavior. For a receiver, it indicates + * whether swapping is allowed. For a sender, it indicates whether + * swapping is allowed on the corresponding receiver. For a stand-alone + * instance, it does not have any meaning. + */ + bool _swappable; - /** - * Index into the _buffers array that is currently owned by this instance - * and used for read and (possibly) write operations. - */ - std::size_t _currentIndex; + /** + * Buffers that hold the actual values. + */ + boost::shared_ptr > _buffers; - /** - * Index of the buffer that has been sent last. This index is only - * meaningful if this is a sender and swapping is not allowed on the - * receiver. - */ - std::size_t _lastSentIndex; + /** + * Queue holding the indices of the full buffers. Those are the buffers + * that have been sent but not yet received. We do not use an spsc_queue + * for this queue, because might want to take elements from the sending + * thread, so there are two threads which might consume elements and thus + * an spsc_queue is not safe. + */ + boost::shared_ptr > _fullBufferQueue; - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr _receiver; + /** + * Queue holding the empty buffers. Those are the buffers that have been + * returned to the sender by the receiver. + */ + boost::shared_ptr > _emptyBufferQueue; - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; + /** + * Index into the _buffers array that is currently owned by this instance + * and used for read and (possibly) write operations. + */ + std::size_t _currentIndex; - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; + /** + * Index of the buffer that has been sent last. This index is only + * meaningful if this is a sender and swapping is not allowed on the + * receiver. + */ + std::size_t _lastSentIndex; - }; - } // namespace impl + /** + * Pointer to the receiver associated with this sender. This field is only + * used if this process variable represents a sender. + */ + boost::shared_ptr _receiver; - template - using ProcessArray = impl::ProcessArrayImpl; + /** + * Time-stamp source used to update the time-stamp when sending a value. + */ + boost::shared_ptr _timeStampSource; + + /** + * Listener that is notified when the process variable is sent. + */ + boost::shared_ptr _sendNotificationListener; + + }; /** * Creates a simple process array. A simple process array just works on its @@ -783,16 +769,16 @@ namespace mtca4u { template typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string & name, T initialValue) { - return boost::make_shared >( - impl::ProcessArrayImpl::STAND_ALONE, name, + return boost::make_shared< ProcessArray >( + ProcessArray::STAND_ALONE, name, std::vector(size, initialValue)); } template typename ProcessArray::SharedPtr createSimpleProcessArray( const std::vector& initialValue, const std::string & name) { - return boost::make_shared >( - impl::ProcessArrayImpl::STAND_ALONE, name, initialValue); + return boost::make_shared< ProcessArray >( + ProcessArray::STAND_ALONE, name, initialValue); } template @@ -802,12 +788,12 @@ namespace mtca4u { bool swappable, std::size_t numberOfBuffers, boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener) { - typename boost::shared_ptr > receiver = - boost::make_shared >( - impl::ProcessArrayImpl::RECEIVER, name, + typename boost::shared_ptr< ProcessArray > receiver = + boost::make_shared< ProcessArray >( + ProcessArray::RECEIVER, name, std::vector(size, initialValue), numberOfBuffers, swappable); typename ProcessArray::SharedPtr sender = boost::make_shared< - typename impl::ProcessArrayImpl >(impl::ProcessArrayImpl::SENDER, + ProcessArray >(ProcessArray::SENDER, swappable, timeStampSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); @@ -820,12 +806,12 @@ namespace mtca4u { bool swappable, std::size_t numberOfBuffers, boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener) { - typename boost::shared_ptr > receiver = - boost::make_shared >( - impl::ProcessArrayImpl::RECEIVER, name, initialValue, + typename boost::shared_ptr< ProcessArray > receiver = + boost::make_shared< ProcessArray >( + ProcessArray::RECEIVER, name, initialValue, numberOfBuffers, swappable); typename ProcessArray::SharedPtr sender = boost::make_shared< - typename impl::ProcessArrayImpl >(impl::ProcessArrayImpl::SENDER, + ProcessArray >(ProcessArray::SENDER, swappable, timeStampSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); From 6c223cd1ecb3f64dbcef243eeb37dce03718a469 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 14 Jul 2016 22:20:54 +0200 Subject: [PATCH 050/786] working on docu --- include/ProcessArray.h | 70 +++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index bdcdf66..1c60374 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -19,41 +19,9 @@ #include "ProcessVariableListener.h" namespace mtca4u { - /** - * Interface implemented by all process arrays. - * - * Instances implementing this interface are typically not thread-safe and - * should only be used from a single thread. - */ template class ProcessArrayHowItLookedLike: public ProcessVariable { public: - /** - * Type alias for a shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Assignment operator for another ProcessArray of the same template type. - * It can be of a different implementation, though. The size of the assigned - * array must be smaller than or equal to the target size. - */ - // we don't want this behavious anyway. - //ProcessArray & operator=(ProcessArray const & other) { - // this->set(other); - // return *this; - //} - - /** - * Assignment operator for a std::vector of the template data type. - * The size of the assigned array must be smaller than or equal to the - * target size. - */ -// ProcessArray & operator=(std::vector const & other) { -// this->set(other); -// return *this; -// } - /** * Returns a reference to the vector that represents the current value of * this process array. @@ -176,8 +144,11 @@ namespace mtca4u { }; /** - * Implementation of the ProcessArray. This implementation is used for all + * Array implementation of the ProcessVariable. This implementation is used for all * three use cases (sender, receiver, and stand-alone). + * + * This class is not thread-safe and + * should only be used from a single thread. */ template class ProcessArray: public ProcessVariable { @@ -203,8 +174,13 @@ namespace mtca4u { public: + /** + * Type alias for a shared pointer to this type. + */ typedef boost::shared_ptr SharedPtr; + //FIXME: This was private, but what for? + /** * Type of the instance. This defines the behavior (send or receive * possible, modifications allowed, etc.). This enum type is private so @@ -362,7 +338,12 @@ namespace mtca4u { } /** - * Assignment operator. This behaves excactly like the set(...) method. + * FIXME: I think we don't want this. The base class is no copy/assignable anyway. + * + * Assignment operator for another ProcessArray of the same template type. + * It can be of a different implementation, though. The size of the assigned + * array must be smaller than or equal to the target size. + * This behaves excactly like the set(...) method. */ ProcessArray & operator=(ProcessArray const & other) { set(other.getConst()); @@ -370,7 +351,10 @@ namespace mtca4u { } /** - * Assignment operator. This behaves exactly like the set(...) method. + * Assignment operator for a std::vector of the template data type. + * The size of the assigned array must be smaller than or equal to the + * target size. + * This behaves exactly like the set(...) method. */ ProcessArray & operator=(std::vector const & v) { set(v); @@ -381,10 +365,26 @@ namespace mtca4u { return getConst(); } + /** + * Updates this process variable's value with the elements from the + * specified vector. The vector's number of elements must match this process + * variable's number of elements. + * + * If this instance of the process array must not be modified (because it is + * a receiver and does not allow swapping), this method throws an exception. + */ void set(std::vector const & v) { get() = v; } + /** + * Updates this process variable's value with the other process variable's + * value. The other process variable's number of elements must match this + * process variable's number of elements. + * + * If this instance of the process array must not be modified (because it is + * a receiver and does not allow swapping), this method throws an exception. + */ void set(ProcessArray const & other) { set(other.getConst()); } From e7022b7a1703b5a6a1014eaf5ccfac9543c73d8f Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 15 Jul 2016 14:22:04 +0200 Subject: [PATCH 051/786] code cleanup and fixing documentation --- include/ProcessArray.h | 204 +++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 128 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index 1c60374..a781caf 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -19,130 +19,6 @@ #include "ProcessVariableListener.h" namespace mtca4u { - template - class ProcessArrayHowItLookedLike: public ProcessVariable { - public: - /** - * Returns a reference to the vector that represents the current value of - * this process array. - * - * If this instance of the process array must not be modified (because it is a receiver and does not allow swapping), this - * method throws an exception. In this case, you should use the - * {@link #getConst()} method instead. - * - * The reference returned by this method becomes invalid when a receive, - * send, or swap operation is performed on this process variable. Use of an - * invalid reference results in undefined behavior. - */ - virtual std::vector & get() =0; - - /** - * Returns a constant reference to the vector that represents the current - * value of this process array. This version of the get method is used if - * this process array is const. - * - * The reference returned by this method becomes invalid when a receive, - * send, or swap operation is performed on this process variable. Use of an - * invalid reference results in undefined behavior. - */ - virtual std::vector const & get() const =0; - - /** - * Returns a constant reference to the the vector that represents the - * current value of this process array. This method should be preferred for - * receiver process variables because it works even if the process array - * does not allow swapping and thus the {@link #get()} method cannot be - * used. - * - * The reference returned by this method becomes invalid when a receive, - * send, or swap operation is performed on this process variable. Use of an - * invalid reference results in undefined behavior. - */ - virtual std::vector const & getConst() const =0; - - /** - * Returns a constant reference to the vector that represents the value that - * has been sent with the last sent operation. - * - * This method may only be used on a process variable that is a sender and - * if swapping is not allowed for the respective receiver. In all other - * cases, calling this method results in an exception. - * - * While the reference returned by this method stays valid even after a - * receive, send, or swap operation is performed on this process variable, - * it is recommended to not retain this reference because it might point - * to a different vector than expected. - */ - virtual std::vector const & getLastSent() const =0; - - /** - * Updates this process variable's value with the other process variable's - * value. The other process variable's number of elements must match this - * process variable's number of elements. - * - * If this instance of the process array must not be modified (because it is - * a receiver and does not allow swapping), this method throws an exception. - */ - // virtual void set(ProcessArray const & other) =0; - - /** - * Updates this process variable's value with the elements from the - * specified vector. The vector's number of elements must match this process - * variable's number of elements. - * - * If this instance of the process array must not be modified (because it is - * a receiver and does not allow swapping), this method throws an exception. - */ - virtual void set(std::vector const & other) =0; - - const std::type_info& getValueType() const { - return typeid(T); - } - - bool isArray() const { - return true; - } - - /** - * Tells whether this array supports swapping. If true, the - * swap method can be used to swap the vector backing this - * array with a different one, thus avoiding copying during synchronization. - * If false, calling swap results in an exception. - * Swapping (and other modifications) are always supported on the sender - * side but might not be supported on the receiver side. - */ - virtual bool isSwappable() const =0; - - /** - * Swaps the vector backing this process array with a different vector. - * This method may only be called by the peer control-system process array - * when synchronizing with this process array and only if - * isSwappable() returns true. Otherwise, this - * method throws an std::logic_error. - * - * The boost::scoped_ptr passed must not be null - * and must point to a vector that has the same size as the vector it is - * swapped with. - */ - virtual void swap(boost::scoped_ptr >& otherVector) =0; - - protected: - /** - * Creates a process array with the specified name. - */ -// ProcessArray(const std::string& name = std::string()) : -// ProcessVariable(name) { -// } -// -// /** -// * Protected destructor. Instances should not be destroyed through -// * pointers to this base type. -// */ -// virtual ~ProcessArray() { -// } - - }; - /** * Array implementation of the ProcessVariable. This implementation is used for all * three use cases (sender, receiver, and stand-alone). @@ -372,6 +248,10 @@ namespace mtca4u { * * If this instance of the process array must not be modified (because it is * a receiver and does not allow swapping), this method throws an exception. + * + * FIXME: The description says it is throwing, but the implementation does not + * do it. Throwing will be obsolete in future because it is planned to have + * receivers always swappable. */ void set(std::vector const & v) { get() = v; @@ -384,11 +264,26 @@ namespace mtca4u { * * If this instance of the process array must not be modified (because it is * a receiver and does not allow swapping), this method throws an exception. + * + * FIXME: The description says it is throwing, but the implementation does not + * do it. Throwing will be obsolete in future because it is planned to have + * receivers always swappable. */ void set(ProcessArray const & other) { set(other.getConst()); } + /** + * Swaps the vector backing this process array with a different vector. + * This method may only be called by the peer control-system process array + * when synchronizing with this process array and only if + * isSwappable() returns true. Otherwise, this + * method throws an std::logic_error. + * + * The boost::scoped_ptr passed must not be null + * and must point to a vector that has the same size as the vector it is + * swapped with. + */ void swap(boost::scoped_ptr > & otherVector) { if (!isSwappable()) { throw std::logic_error( @@ -400,6 +295,19 @@ namespace mtca4u { (*_buffers)[_currentIndex].value.swap(otherVector); } + /** + * Returns a reference to the vector that represents the current value of + * this process array. + * + * If this instance of the process array must not be modified + * (because it is a receiver and does not allow swapping), this + * method throws an exception. In this case, you should use the + * {@link #getConst()} method instead. + * + * The reference returned by this method becomes invalid when a receive, + * send, or swap operation is performed on this process variable. Use of an + * invalid reference results in undefined behavior. + */ std::vector & get() { if (_instanceType == RECEIVER && !_swappable) { throw std::logic_error( @@ -408,14 +316,50 @@ namespace mtca4u { return *(((*_buffers)[_currentIndex]).value); } + /** + * Returns a constant reference to the vector that represents the current + * value of this process array. This version of the get method is used if + * this process array is const. + * + * The reference returned by this method becomes invalid when a receive, + * send, or swap operation is performed on this process variable. Use of an + * invalid reference results in undefined behavior. + */ std::vector const & get() const { return getConst(); } + /** + * Returns a constant reference to the the vector that represents the + * current value of this process array. This method should be preferred for + * receiver process variables because it works even if the process array + * does not allow swapping and thus the {@link #get()} method cannot be + * used. + * + * The reference returned by this method becomes invalid when a receive, + * send, or swap operation is performed on this process variable. Use of an + * invalid reference results in undefined behavior. + */ std::vector const & getConst() const { return *(((*_buffers)[_currentIndex]).value); } + /** + * @deprecated + * It is planned that receivers are always swappable. + * + * Returns a constant reference to the vector that represents the value that + * has been sent with the last sent operation. + * + * This method may only be used on a process variable that is a sender and + * if swapping is not allowed for the respective receiver. In all other + * cases, calling this method results in an exception. + * + * While the reference returned by this method stays valid even after a + * receive, send, or swap operation is performed on this process variable, + * it is recommended to not retain this reference because it might point + * to a different vector than expected. + */ std::vector const & getLastSent() const { if (_instanceType != SENDER || _swappable) { throw std::logic_error( @@ -436,6 +380,14 @@ namespace mtca4u { return ((*_buffers)[_currentIndex]).timeStamp; } + /** + * Tells whether this array supports swapping. If true, the + * swap method can be used to swap the vector backing this + * array with a different one, thus avoiding copying during synchronization. + * If false, calling swap results in an exception. + * Swapping (and other modifications) are always supported on the sender + * side but might not be supported on the receiver side. + */ bool isSwappable() const { // Senders and stand-alone instances are always swappable. For // receivers, it depends on the swappable flag. @@ -762,10 +714,6 @@ namespace mtca4u { boost::shared_ptr sendNotificationListener = boost::shared_ptr()); -} // namespace mtca4u - -namespace mtca4u { - template typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string & name, T initialValue) { From 0d66bbee971ed57b2a3db61e7e8afd6feba9ca61 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 15 Jul 2016 14:59:50 +0200 Subject: [PATCH 052/786] more code cleanup --- include/ProcessArray.h | 22 ---------------------- include/ProcessScalar.h | 12 ------------ 2 files changed, 34 deletions(-) diff --git a/include/ProcessArray.h b/include/ProcessArray.h index a781caf..f68f843 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -29,25 +29,6 @@ namespace mtca4u { template class ProcessArray: public ProcessVariable { -// // Allow the factory functions to create instances of this class. -// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< -// T>(std::size_t, const std::string &, T); -// -// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< -// T>(const std::vector&, const std::string &); -// -// friend typename std::pair::SharedPtr, -// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< -// T>(std::size_t, const std::string &, T, bool, std::size_t, -// boost::shared_ptr, -// boost::shared_ptr); -// -// friend typename std::pair::SharedPtr, -// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< -// T>(const std::vector&, const std::string &, bool, std::size_t, -// boost::shared_ptr, -// boost::shared_ptr); - public: /** @@ -55,8 +36,6 @@ namespace mtca4u { */ typedef boost::shared_ptr SharedPtr; - //FIXME: This was private, but what for? - /** * Type of the instance. This defines the behavior (send or receive * possible, modifications allowed, etc.). This enum type is private so @@ -81,7 +60,6 @@ namespace mtca4u { RECEIVER }; - public: /** * Creates a process array that works independently. This means that the * instance is not synchronized with any other instance and thus the send diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index 799b4fb..f6beb0e 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -29,17 +29,6 @@ namespace mtca4u { template class ProcessScalar: public ProcessVariable { - // Allow the factory functions to create instances of this class. - // friend typename ProcessScalar::SharedPtr createSimpleProcessScalar( - // const std::string &, T); - - // friend typename std::pair::SharedPtr, - // typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar< - // T>(const std::string &, T, std::size_t, - // boost::shared_ptr, - // boost::shared_ptr); - - // fixme whould be private: public: /** * Type of the instance. This defines the behavior (send or receive @@ -65,7 +54,6 @@ namespace mtca4u { RECEIVER }; - public: /** * Type alias for a shared pointer to this type. */ From ed532ebd349ce8dca963db7d6a962dfd0a92a642 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 15 Jul 2016 16:07:20 +0200 Subject: [PATCH 053/786] removed no longer needed file ProcessArrayImpl.h --- include/ProcessArrayImpl.h | 490 ------------------------------------- 1 file changed, 490 deletions(-) delete mode 100644 include/ProcessArrayImpl.h diff --git a/include/ProcessArrayImpl.h b/include/ProcessArrayImpl.h deleted file mode 100644 index 86ac803..0000000 --- a/include/ProcessArrayImpl.h +++ /dev/null @@ -1,490 +0,0 @@ -#ifndef MTCA4U_PROCESS_ARRAY_IMPL_H -#define MTCA4U_PROCESS_ARRAY_IMPL_H - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ProcessVariable.h" -#include "ProcessVariableListener.h" - -namespace mtca4u { - namespace impl { - - /** - * Implementation of the ProcessArray. This implementation is used for all - * three use cases (sender, receiver, and stand-alone). - */ - template - class ProcessArrayImpl: public ProcessVariable { - -// // Allow the factory functions to create instances of this class. -// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< -// T>(std::size_t, const std::string &, T); -// -// friend typename ProcessArray::SharedPtr mtca4u::createSimpleProcessArray< -// T>(const std::vector&, const std::string &); -// -// friend typename std::pair::SharedPtr, -// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< -// T>(std::size_t, const std::string &, T, bool, std::size_t, -// boost::shared_ptr, -// boost::shared_ptr); -// -// friend typename std::pair::SharedPtr, -// typename ProcessArray::SharedPtr> mtca4u::createSynchronizedProcessArray< -// T>(const std::vector&, const std::string &, bool, std::size_t, -// boost::shared_ptr, -// boost::shared_ptr); - - public: - - typedef boost::shared_ptr SharedPtr; - - /** - * Type of the instance. This defines the behavior (send or receive - * possible, modifications allowed, etc.). This enum type is private so - * that only friend functions can construct instances of this class. The - * constructors themselves have to be public so that boost::make_shared - * can be used. - */ - enum InstanceType { - /** - * Instance acts on is own. - */ - STAND_ALONE, - - /** - * Instance acts as the sender in a sender / receiver pair. - */ - SENDER, - - /** - * Instance acts as the receiver in a sender / receiver pair. - */ - RECEIVER - }; - - public: - /** - * Creates a process array that works independently. This means that the - * instance is not synchronized with any other instance and thus the send - * and receive operations are not supported. However, all other operations - * can be used like on any process variable. - */ - ProcessArrayImpl(InstanceType instanceType, const std::string& name, - const std::vector& initialValue) : - ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(true), _buffers( - boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( - 0) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != STAND_ALONE) { - throw std::invalid_argument( - "This constructor may only be used for a stand-alone process scalar."); - } - // We have to initialize the buffer by creating a vector of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). - boost::scoped_ptr > v(new std::vector(initialValue)); - (*_buffers)[0].value.swap(v); - } - - /** - * Creates a process array that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. - * - * If the swappable option is true, the swap operation is - * allowed. The swappable flag must be the same for the - * sender and receiver. - */ - ProcessArrayImpl(InstanceType instanceType, const std::string& name, - const std::vector& initialValue, std::size_t numberOfBuffers, - bool swappable) : - ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(swappable), _buffers( - boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( - boost::make_shared >( - numberOfBuffers)), _emptyBufferQueue( - boost::make_shared >( - numberOfBuffers)), _currentIndex(0), _lastSentIndex(0) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument( - "This constructor may only be used for a receiver process scalar."); - } - // We need at least two buffers for the queue (so four buffers in total) - // in order to guarantee that we never have to block. - if (numberOfBuffers < 2) { - throw std::invalid_argument( - "The number of buffers must be at least two."); - } - // We have to limit the number of buffers because we cannot allocate - // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - 2)) { - throw std::invalid_argument("The number of buffers is too large."); - } - // We have to initialize the buffers by creating vectors of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). - for (typename std::vector::iterator i = _buffers->begin(); - i != _buffers->end(); ++i) { - boost::scoped_ptr > v( - new std::vector(initialValue)); - i->value.swap(v); - } - // The buffer with the index 0 is assigned to the receiver and the - // buffer with the index 1 is assigned to the sender. All buffers have - // to be added to the empty-buffer queue. - for (std::size_t i = 2; i < _buffers->size(); ++i) { - _emptyBufferQueue->push(i); - } - } - - /** - * Creates a process array that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the swappable option is true, the swap operation is - * allowed on the receiver. The swappable flag must be the - * same for the sender and receiver. Allowing swapping has the side effect - * that a value cannot be read after having been sent. - * - * The optional send-notification listener is notified every time the - * sender's {@link ProcessArrayImpl::send()} method is called. It can be - * used to queue a request for the receiver's - * {@link ProcessArrayImpl::receive()} method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - ProcessArrayImpl(InstanceType instanceType, bool swappable, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener, - boost::shared_ptr receiver) : - ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( - receiver->_vectorSize), _swappable(swappable), _buffers( - receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( - receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( - receiver), _timeStampSource(timeStampSource), _sendNotificationListener( - sendNotificationListener) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument( - "This constructor may only be used for a sender process scalar."); - } - if (!receiver) { - throw std::invalid_argument( - "The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - } - - /** - * Assignment operator. This behaves excactly like the set(...) method. - */ - ProcessArrayImpl & operator=(ProcessArrayImpl const & other) { - set(other.getConst()); - return (*this); - } - - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ -// ProcessArrayImpl & operator=(ProcessArray const & other) { -// set(other.getConst()); -// return (*this); -// } - - /** - * Assignment operator. This behaves exactly like the set(...) method. - */ - ProcessArrayImpl & operator=(std::vector const & v) { - set(v); - return *this; - } - - operator std::vector() const { - return getConst(); - } - - void set(std::vector const & v) { - get() = v; - } - - void set(ProcessArrayImpl const & other) { - set(other.getConst()); - } - - void swap(boost::scoped_ptr > & otherVector) { - if (!isSwappable()) { - throw std::logic_error( - "Swap is not supported by this process array."); - } - if (otherVector->size() != get().size()) { - throw std::runtime_error("Vector sizes do not match"); - } - (*_buffers)[_currentIndex].value.swap(otherVector); - } - - std::vector & get() { - if (_instanceType == RECEIVER && !_swappable) { - throw std::logic_error( - "Attempt to modify a read-only process variable."); - } - return *(((*_buffers)[_currentIndex]).value); - } - - std::vector const & get() const { - return getConst(); - } - - std::vector const & getConst() const { - return *(((*_buffers)[_currentIndex]).value); - } - - std::vector const & getLastSent() const { - if (_instanceType != SENDER || _swappable) { - throw std::logic_error( - "The last sent value is only available for a sender that is associated with a receiver that does not allow swapping."); - } - return *(((*_buffers)[_lastSentIndex]).value); - } - - bool isReceiver() const { - return _instanceType == RECEIVER; - } - - bool isSender() const { - return _instanceType == SENDER; - } - - TimeStamp getTimeStamp() const { - return ((*_buffers)[_currentIndex]).timeStamp; - } - - bool isSwappable() const { - // Senders and stand-alone instances are always swappable. For - // receivers, it depends on the swappable flag. - if (_instanceType == RECEIVER) { - return _swappable; - } else { - return true; - } - } - - bool receive() { - if (_instanceType != RECEIVER) { - throw std::logic_error( - "Receive operation is only allowed for a receiver process variable."); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the sender might get into - // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); - } - std::size_t nextIndex; - if (_fullBufferQueue->pop(nextIndex)) { - _emptyBufferQueue->push(_currentIndex); - _currentIndex = nextIndex; - return true; - } else { - return false; - } - } - - bool send() { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable."); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the receiver might get into - // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); - } - // Before sending the value, we have to update the associated - // time-stamp. - ((*_buffers)[_currentIndex]).timeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - std::size_t nextIndex; - bool foundEmptyBuffer; - if (_emptyBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = true; - _fullBufferQueue->push(_currentIndex); - } else { - // We can still send, but we will lose older data. - if (_fullBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = false; - _fullBufferQueue->push(_currentIndex); - } else { - // It is possible, that we did not find an empty buffer but before - // we could get a full buffer, the receiver processed all full - // buffers. In this case, the queue of empty buffers cannot be empty - // any longer because we have at least four buffers. - if (!_emptyBufferQueue->pop(nextIndex)) { - // This should never happen and is just an assertion for extra - // safety. - throw std::runtime_error( - "Assertion that empty-buffer queue has at least one element failed."); - } - foundEmptyBuffer = true; - _fullBufferQueue->push(_currentIndex); - } - } - _lastSentIndex = _currentIndex; - _currentIndex = nextIndex; - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - return foundEmptyBuffer; - } - - const std::type_info& getValueType() const { - return typeid(T); - } - - bool isArray() const { - return true; - } - - - private: - - /** - * Type for the individual buffers. Each buffer stores a vector (wrapped - * in a Boost scoped pointer) and a time stamp. - */ - struct Buffer { - - TimeStamp timeStamp; - boost::scoped_ptr > value; - - /** - * Default constructor. Has to be defined explicitly because we have an - * non-default copy constructor. - */ - Buffer() { - } - - /** - * Copy constructor. Some STL implementations need the copy constructor - * while initializing the vector that stores the buffers: They do not - * default construct each element but only default construct the first - * element and then copy it. In this case, this copy constructor will - * work perfectly fine. It would even work if the value pointer was - * already initialized with a vector, however we might have a serious - * performance problem if that happened. Therefore, we use an assertion - * to check that the copy constructor is never used once the buffer has - * been initialized with a vector. - */ - Buffer(Buffer const & other) : - timeStamp(other.timeStamp), value( - other.value ? new std::vector(*(other.value)) : 0) { - assert(!(other.value)); - } - - }; - - /** - * Type this instance is representing. - */ - InstanceType _instanceType; - - /** - * Number of elements that each vector (and thus this array) has. - */ - std::size_t _vectorSize; - - /** - * Flag indicating the swapping behavior. For a receiver, it indicates - * whether swapping is allowed. For a sender, it indicates whether - * swapping is allowed on the corresponding receiver. For a stand-alone - * instance, it does not have any meaning. - */ - bool _swappable; - - /** - * Buffers that hold the actual values. - */ - boost::shared_ptr > _buffers; - - /** - * Queue holding the indices of the full buffers. Those are the buffers - * that have been sent but not yet received. We do not use an spsc_queue - * for this queue, because might want to take elements from the sending - * thread, so there are two threads which might consume elements and thus - * an spsc_queue is not safe. - */ - boost::shared_ptr > _fullBufferQueue; - - /** - * Queue holding the empty buffers. Those are the buffers that have been - * returned to the sender by the receiver. - */ - boost::shared_ptr > _emptyBufferQueue; - - /** - * Index into the _buffers array that is currently owned by this instance - * and used for read and (possibly) write operations. - */ - std::size_t _currentIndex; - - /** - * Index of the buffer that has been sent last. This index is only - * meaningful if this is a sender and swapping is not allowed on the - * receiver. - */ - std::size_t _lastSentIndex; - - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr _receiver; - - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; - - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; - - }; - - } // namespace impl -} // namespace mtca4u - -#endif // MTCA4U_PROCESS_ARRAY_IMPL_H From 005fc7d25d639181977a350ea07ab2a6adfb3374 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 15 Jul 2016 16:43:01 +0200 Subject: [PATCH 054/786] removed all doxygen warnings for broken links (which would result in empty links in the output) --- include/ControlSystemPVManager.h | 13 ++++++++++--- include/DevicePVManager.h | 17 ++++++++++++----- include/PVManager.h | 8 ++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/include/ControlSystemPVManager.h b/include/ControlSystemPVManager.h index 3a7a6e3..165d9dd 100644 --- a/include/ControlSystemPVManager.h +++ b/include/ControlSystemPVManager.h @@ -71,7 +71,7 @@ namespace mtca4u { * using the * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)} * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, T, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type @@ -87,12 +87,19 @@ namespace mtca4u { * {@link DevicePVManager::createProcessScalar(SynchronizationDirection, const std::string&, T, std::size_t)}, * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)}, * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, T, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. + */ + /* (intentionally not doxygen) + * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid + * non-working links: * * The {@link getProcessScalar(const std::string&)} and - * {@link getProcessArray(const std::string&)} methods should be preferred + * {@link getProcessArray(const std:.string&)} methods should be preferred + */ + /** The \c getProcessScalar(const std::string&) and + * \c getProcessArray(const std:.string&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( diff --git a/include/DevicePVManager.h b/include/DevicePVManager.h index 8c28e79..9d9e4a1 100644 --- a/include/DevicePVManager.h +++ b/include/DevicePVManager.h @@ -61,7 +61,7 @@ namespace mtca4u { * Creates a new process scalar and registers it with the PV manager. * Creating a process scalar with a name that is already used for a * different process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * The number of buffers (the minimum and default value is one) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -82,7 +82,7 @@ namespace mtca4u { * Creates a new process array and registers it with the PV manager. * Creating a process array with a name that is already used for a different * process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * If the swappable flag is true (the default), * the control-system PV is marked as swappable and thus the control system @@ -110,7 +110,7 @@ namespace mtca4u { * Creates a new process array and registers it with the PV manager. * Creating a process array with a name that is already used for a different * process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * The array's size is set to the number of elements stored in the vector * provided for initialization and all elements are initialized with the @@ -155,7 +155,7 @@ namespace mtca4u { * using the * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)} * or - * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, T, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type @@ -170,12 +170,19 @@ namespace mtca4u { * {@link createProcessScalar(SynchronizationDirection, const std::string&, T, std::size_t)}, * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)}, * or - * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, T, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. + */ + /* (intentionally not doxygen) + * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid + * non-working links: * * The {@link getProcessScalar(const std::string&)} and * {@link getProcessArray(const std:.string&)} methods should be preferred + */ + /** The \c getProcessScalar(const std::string&) and + * \c getProcessArray(const std:.string&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( diff --git a/include/PVManager.h b/include/PVManager.h index 59d1bb4..10f325f 100644 --- a/include/PVManager.h +++ b/include/PVManager.h @@ -65,7 +65,7 @@ namespace mtca4u { * library to the control system and registers it with the PV manager. * Creating a process scalar with a name that is already used for a * different process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * The number of buffers (the minimum and default value is one) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -88,7 +88,7 @@ namespace mtca4u { * system to the device library and registers it with the PV manager. * Creating a process scalar with a name that is already used for a * different process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * The number of buffers (the minimum and default value is one) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -111,7 +111,7 @@ namespace mtca4u { * to the control system and registers it with the PV manager. * Creating a process array with a name that is already used for a different * process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * The array's size is set to the number of elements stored in the vector * provided for initialization and all elements are initialized with the @@ -145,7 +145,7 @@ namespace mtca4u { * to the device library and registers it with the PV manager. * Creating a process array with a name that is already used for a different * process scalar or array is an error and causes an - * {@link std::invalid_argument} exception to be thrown. + * \c std::invalid_argument exception to be thrown. * * The array's size is set to the number of elements stored in the vector * provided for initialization and all elements are initialized with the From 81f4fcc3e8463a81a212f9ecd06b7ba6394dbcf5 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 21 Jul 2016 09:06:02 +0200 Subject: [PATCH 055/786] Added make_debian_package script to the project template --- cmake/make_debian_package.sh.in | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 cmake/make_debian_package.sh.in diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in new file mode 100755 index 0000000..39228be --- /dev/null +++ b/cmake/make_debian_package.sh.in @@ -0,0 +1,56 @@ +#!/bin/bash + +# This cmake.in file requires the following variables to be set: +# PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME Environment variable which contains the build version. +# Must not contain any hyphens. +# PACKAGE_BASE_NAME This is the debian package name, not the project name (e.g. libraries must start with lib, +# all lover case, no underscore) +# PACKAGE_FULL_LIBRARY_VERSION The full version, incl. build version and patch version +# PACKAGE_GIT_URI The URI where to check out the source code +# PACKAGE_TAG_VERSION The tag for which the package is build +# PACKAGE_MESSAGE The message for the debian package log + +#Check that the @PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@ variable is set, otherwise reject creating a package +if [ -z "$@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@" ]; then + echo Environment variable '@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@' not set or empty. You need it to make a debian package! + exit 1 +fi + +#drop out of the script if anything goes wrong (e.g. non-existen git tag) +set -e + +#Create a working directory in order not to merge with the rest in the build directory +rm -rf debian_package +mkdir debian_package +cd debian_package + +#Check out the correct tag from the master git repository. +#The local directory name has to follow the debian convention +# lowecasepackagenname_package.ver.sion +#Note, we first clone and then checkout the tag because the old git version on +#Ubuntu12.4 does not support to do it in one go. +BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_@PACKAGE_FULL_LIBRARY_VERSION@ +git clone @PACKAGE_GIT_URI@ ${BUILD_DIR_NAME} +( cd ${BUILD_DIR_NAME} &&\ + git checkout /@PACKAGE_TAG_VERSION@ &&\ + rm -rf .git ) +#Debian convention: file has to end on .orig.tar.gz +tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} + +#Copy the prepared debian packaging config files to the source code +#directroy +cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian +cd ${BUILD_DIR_NAME} + +#The package versions for doocs / Ubuntu contain the codename of the distribution. Get it from the system. +CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` + +#Before building the package we will update the changelog. This is easier from a shell script +#because debchange does the right format and the current date, user name and email automatically for us. +#Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is +# user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). +debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_FULL_LIBRARY_VERSION@-${@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@} --distribution ${CODENAME} @PACKAGE_MESSAGE@ + +#Now everything is prepared and we can actually build the package. +#If you have a gpg signature you can remove the -us and -uc flags and sign the package. +dpkg-buildpackage -rfakeroot -us -uc From 7f45aa2c1ba50d6b9d6be36db561c1cd03f3a050 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 21 Jul 2016 11:16:51 +0200 Subject: [PATCH 056/786] added non-modification warning to the new script --- cmake/make_debian_package.sh.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 39228be..7f66184 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -1,5 +1,19 @@ #!/bin/bash +# Shell script to generate a debian package from a particular tag (configured during the cmake configuration). + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + # This cmake.in file requires the following variables to be set: # PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME Environment variable which contains the build version. # Must not contain any hyphens. From 04bcdeebccb92742810dee1899b598d6a1b9cb90 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 21 Jul 2016 14:28:11 +0200 Subject: [PATCH 057/786] fixed typo in debian packaging script --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 7f66184..e1c02cb 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -46,7 +46,7 @@ cd debian_package BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_@PACKAGE_FULL_LIBRARY_VERSION@ git clone @PACKAGE_GIT_URI@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ - git checkout /@PACKAGE_TAG_VERSION@ &&\ + git checkout @PACKAGE_TAG_VERSION@ &&\ rm -rf .git ) #Debian convention: file has to end on .orig.tar.gz tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} From 2b2d934c1a31a496d9146c806d1ae8261a82c4b4 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 21 Jul 2016 17:41:43 +0200 Subject: [PATCH 058/786] fixed generation of @PROJECT_NAME@_found for CMake < 2.8.11 --- cmake/FindPROJECT_NAME.cmake.in.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index f1a3a4c..2190bb2 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -52,9 +52,10 @@ include(FindPackageHandleStandardArgs) # The FOUND_VAR option in FIND_PACKAGE_HANDLE_STANDARD_ARGS was introduced in cmake-2.8.11, but Ubuntu 12.04 has cmake-2.8.7 only. # Thus we use a work around here for older cmake versions. if("${CMAKE_VERSION}" VERSION_LESS 2.8.11) + # The old version always provides a variable with upper case project name, so we just copy that FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION) STRING(TOUPPER "@PROJECT_NAME@" PROJECT_NAME_UPPERCASE) - set(@PROJECT_NAME@_VERSION ${PROJECT_NAME_UPPERCASE}) + set(@PROJECT_NAME@_FOUND ${${PROJECT_NAME_UPPERCASE}_FOUND}) else() FIND_PACKAGE_HANDLE_STANDARD_ARGS(@PROJECT_NAME@ REQUIRED_VARS @PROJECT_NAME@_PREFIX VERSION_VAR @PROJECT_NAME@_VERSION FOUND_VAR @PROJECT_NAME@_FOUND) endif() From 55552b4485b15a8be2e1b4e41490c33e1e9c782d Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 26 Jul 2016 16:40:35 +0200 Subject: [PATCH 059/786] fixed spaces in the PROJECT_NAME-config template --- cmake/PROJECT_NAME-config.in.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/PROJECT_NAME-config.in.in b/cmake/PROJECT_NAME-config.in.in index e3feb03..b2d3e71 100755 --- a/cmake/PROJECT_NAME-config.in.in +++ b/cmake/PROJECT_NAME-config.in.in @@ -37,16 +37,16 @@ fi while [ $# -gt 0 ]; do case "$1" in --cppflags) - OUTPUT="${OUTPUT}@@PROJECT_NAME@_CXX_FLAGS_MAKEFILE@" + OUTPUT="${OUTPUT} @@PROJECT_NAME@_CXX_FLAGS_MAKEFILE@" shift;; --ldflags) - OUTPUT="${OUTPUT}@@PROJECT_NAME@_LINKER_FLAGS_MAKEFILE@" + OUTPUT="${OUTPUT} @@PROJECT_NAME@_LINKER_FLAGS_MAKEFILE@" shift;; --version) - OUTPUT="${OUTPUT}@@PROJECT_NAME@_SOVERSION@" + OUTPUT="${OUTPUT} @@PROJECT_NAME@_SOVERSION@" shift;; --mexflags) - OUTPUT="${OUTPUT}@@PROJECT_NAME@_MEXFLAGS@" + OUTPUT="${OUTPUT} @@PROJECT_NAME@_MEXFLAGS@" shift;; *) echo "invalid option: $1" 1>&2 From a422f243f85dcce881e51dc2c41cdf8fd51ed75c Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 27 Jul 2016 11:40:41 +0200 Subject: [PATCH 060/786] added file to indicate that a project is using the project-template --- This_project_uses_the_project-template.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 This_project_uses_the_project-template.txt diff --git a/This_project_uses_the_project-template.txt b/This_project_uses_the_project-template.txt new file mode 100644 index 0000000..ec7651f --- /dev/null +++ b/This_project_uses_the_project-template.txt @@ -0,0 +1,7 @@ +This project uses the ChimeraTK project-template. + +It contains some files (like this one) which should not be changed +inside the project. The files should be improved in a generic way in the +project-template and are afterwards merged into all projects which use the +project-template. Like this it is easier to improve the build scripts and +keep them consistent across projects. From 6da7e545bf8273be172117b1a503bf754b3d1551 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 27 Jul 2016 12:53:50 +0200 Subject: [PATCH 061/786] Added sections to the .gitignore file. --- .gitignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index a6a4af1..56ac3a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,16 @@ +###################### +# Section for 'ignore patterns' from the project-template +# +# !! Don't edit anything in this section to avoid merge conflics !! +###################### build .project +*~ + +###################### +# Project specific 'ignore patterns' +# +# Put you project specific ignores below. +# If they are generally useful consider adding them to the +# project-template and the merge them in. +###################### From 35d0e255235cf018085960c5f0f0edb604817c07 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 27 Jul 2016 12:55:39 +0200 Subject: [PATCH 062/786] Fixes in create_cmake_config_files script: - Quotes around input variables when doing string replace avoid existing semicolons from being deleted. - The project target library itself was missing in the linker flags. --- cmake/create_cmake_config_files.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 72135c6..86b6377 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -30,19 +30,19 @@ # create variables for standard makefiles set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS}") -string(REPLACE " " ";" LIST ${${PROJECT_NAME}_INCLUDE_DIRS}) +string(REPLACE " " ";" LIST "${${PROJECT_NAME}_INCLUDE_DIRS}") foreach(INCLUDE_DIR ${LIST}) set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS_MAKEFILE} -I${INCLUDE_DIR}") endforeach() set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS}") -string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARY_DIRS}) +string(REPLACE " " ";" LIST "${${PROJECT_NAME}_LIBRARY_DIRS}") foreach(LIBRARY_DIR ${LIST}) set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} -L${LIBRARY_DIR}") endforeach() -string(REPLACE " " ";" LIST ${${PROJECT_NAME}_LIBRARIES}) +string(REPLACE " " ";" LIST "${PROJECT_NAME} ${${PROJECT_NAME}_LIBRARIES}") foreach(LIBRARY ${LIST}) if(LIBRARY MATCHES "/") # library name contains slashes: link against the a file path name set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE} ${LIBRARY}") From 98dee98ba37b52ea2a3d88cba041ebbbad6a8b5d Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 27 Jul 2016 13:40:13 +0200 Subject: [PATCH 063/786] added an ignore pattern for vi --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 56ac3a4..a997f81 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,10 @@ ###################### build .project +#files ending with ~ are emacs backups *~ +#files ending with .swp might show up when using vi +*.swp ###################### # Project specific 'ignore patterns' From a9c83edb61f7f3b804e15066b647bde5fcf8f008 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 27 Jul 2016 14:41:43 +0200 Subject: [PATCH 064/786] added make_coverage.sh.in and deprecated Makefile.coverage.in --- cmake/Makefile.coverage.in | 6 +++++ cmake/make_coverage.sh.in | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100755 cmake/make_coverage.sh.in diff --git a/cmake/Makefile.coverage.in b/cmake/Makefile.coverage.in index 82babda..d4c148c 100644 --- a/cmake/Makefile.coverage.in +++ b/cmake/Makefile.coverage.in @@ -2,6 +2,10 @@ # # Additional make target for generating a code coverage report. # +# ATTENTION: This file has bee deprecated in project-template and will no longer be maintained. +# DO NOT MODIFY THIS FILE IN THE PROJECT TEMPLATE! +# Use make_coverage.sh.in instead! +# ####################################################################################################################### ####################################################################################################################### @@ -17,6 +21,7 @@ ####################################################################################################################### coverage: + echo ATTENTION: Using Makefile.coverage.in in your CMake project is deprecated. Please change to make_coverage.sh.in rm -f `find . -name "*\.gcda"` make test lcov --capture --directory . --output-file coverage_all.info @@ -32,3 +37,4 @@ coverage: #be empty! lcov --remove coverage_incl_build.info "@PROJECT_BINARY_DIR@/*" -o coverage.info genhtml coverage.info --output-directory coverage_html + echo ATTENTION: Using Makefile.coverage.in in your CMake project is deprecated. Please change to make_coverage.sh.in diff --git a/cmake/make_coverage.sh.in b/cmake/make_coverage.sh.in new file mode 100755 index 0000000..f2e13f5 --- /dev/null +++ b/cmake/make_coverage.sh.in @@ -0,0 +1,53 @@ +#!/bin/bash -e +####################################################################################################################### +# +# Shell script for generating a code coverage report. +# +# Parameter to be set inside the CMake project: +# EXCLUDE_FROM_COVERAGE (optional) Space separated list of directories in you source code which are excluded from the +# coverage report. +# +# CMAKE_SOURCE_DIR and PROJECT_BINARY_DIR are always automatically there. +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +rm -f `find . -name "*\.gcda"` +make test +lcov --capture --directory . --output-file coverage_all.info + +#lcov capture also includes external stuff like glibc, boost etc. +#only extract the reports for this project +lcov --extract coverage_all.info "@CMAKE_SOURCE_DIR@*" -o coverage_only_source_tree.info + +#Some parts of the source code might be excluded. Set EXCLUDE_FROM_COVERAGE to contain +# a list of directories to be excluded from the coverage report. +# Don't worry if this is empty. It just means there is nothing to exclude (the normal case). +for exclude_dir in @EXCLUDE_FROM_COVERAGE@; do + echo lcov --remove coverage_only_source_tree.info "@CMAKE_SOURCE_DIR@/${exclude_dir}*" -o tmp.info + lcov --remove coverage_only_source_tree.info "@CMAKE_SOURCE_DIR@/${exclude_dir}*" -o tmp.info + mv tmp.info coverage_only_source_tree.info +done + +#Some projects install the library and its headers for testing in the build directory +#and compile tests and examples against it. This leads to double +#counting and untested lines in the coverage report. That's why we +#exclude the build directory from the coverage report (needed if the +#build directory is in the source tree). +#Attention: If you make an 'in sorce build' the coverage report will +#be empty! +lcov --remove coverage_only_source_tree.info "@PROJECT_BINARY_DIR@/*" -o coverage.info + +#finally generate the html page +genhtml coverage.info --output-directory coverage_html From 323e80b3ead3404ef8bd5affa1fbdb314f173266 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 12 Aug 2016 13:59:30 +0200 Subject: [PATCH 065/786] Changed the namespace from mtca4u to ChimeraTK (issue #3). --- examples/cosade/IndependentControlCore.h | 14 ++++---- .../cosade/testIndependentControlCore.cpp | 2 +- include/ControlSystemPVManager.h | 2 +- include/ControlSystemSynchronizationUtility.h | 4 +-- include/DevicePVManager.h | 4 +-- include/DeviceSynchronizationUtility.h | 4 +-- include/ManualTimeStampSource.h | 2 +- include/PVManager.h | 2 +- include/PVManagerDecl.h | 2 +- include/ProcessArray.h | 4 +-- include/ProcessArrayAccessor.h | 4 +-- include/ProcessScalar.h | 4 +-- include/ProcessScalarAccessor.h | 2 +- include/ProcessVariable.h | 4 +-- include/ProcessVariableAccessor.h | 4 +-- include/ProcessVariableDecl.h | 2 +- include/ProcessVariableListener.h | 2 +- include/SynchronizationDirection.h | 2 +- include/TimeStamp.h | 4 +-- include/TimeStampSource.h | 2 +- src/ControlSystemPVManager.cc | 2 +- src/ControlSystemSynchronizationUtility.cc | 4 +-- src/DevicePVManager.cc | 2 +- src/DeviceSynchronizationUtility.cc | 4 +-- src/PVManager.cc | 2 +- src/ProcessVariable.cc | 2 +- .../include/CountingProcessVariableListener.h | 4 +-- tests/include/CountingTimeStampSource.h | 4 +-- tests/include/IndependentTestCore.h | 32 +++++++++---------- tests/src/fullStubTest.cpp | 2 +- ...estControlSystemSynchronizationUtility.cpp | 2 +- .../src/testDeviceSynchronizationUtility.cpp | 2 +- tests/src/testPVAccessors.cpp | 2 +- tests/src/testPVManager.cpp | 2 +- tests/src/testProcessArray.cpp | 20 ++++++------ tests/src/testProcessScalar.cpp | 20 ++++++------ tests/src/testReferenceTestCoreWithTread.cpp | 2 +- tests/src/testTimeStamp.cpp | 2 +- 38 files changed, 90 insertions(+), 90 deletions(-) diff --git a/examples/cosade/IndependentControlCore.h b/examples/cosade/IndependentControlCore.h index c189e9b..415457b 100644 --- a/examples/cosade/IndependentControlCore.h +++ b/examples/cosade/IndependentControlCore.h @@ -31,13 +31,13 @@ class Hardware{ */ class IndependentControlCore{ private: - mtca4u::DevicePVManager::SharedPtr _processVariableManager; + ChimeraTK::DevicePVManager::SharedPtr _processVariableManager; /** The target voltage to be transmitted to the hardware */ - mtca4u::ProcessScalar::SharedPtr _targetVoltage; + ChimeraTK::ProcessScalar::SharedPtr _targetVoltage; /** The monitor voltage which is read back from the hardware */ - mtca4u::ProcessScalar::SharedPtr _monitorVoltage; + ChimeraTK::ProcessScalar::SharedPtr _monitorVoltage; Hardware _hardware; ///< Some hardware @@ -49,11 +49,11 @@ class IndependentControlCore{ /** The constructor gets an instance of the variable factory to use. * The variables in the factory should already be initialised because the hardware is initialised here. */ - IndependentControlCore(boost::shared_ptr const & processVariableManager) + IndependentControlCore(boost::shared_ptr const & processVariableManager) //initialise all process variables, using the factory : _processVariableManager( processVariableManager ), - _targetVoltage( processVariableManager->createProcessScalar(mtca4u::controlSystemToDevice,"TARGET_VOLTAGE") ), - _monitorVoltage( processVariableManager->createProcessScalar(mtca4u::deviceToControlSystem,"MONITOR_VOLTAGE") ){ + _targetVoltage( processVariableManager->createProcessScalar(ChimeraTK::controlSystemToDevice,"TARGET_VOLTAGE") ), + _monitorVoltage( processVariableManager->createProcessScalar(ChimeraTK::deviceToControlSystem,"MONITOR_VOLTAGE") ){ // initialise the hardware here *_targetVoltage = 0; @@ -73,7 +73,7 @@ class IndependentControlCore{ }; inline void IndependentControlCore::mainLoop(){ - mtca4u::DeviceSynchronizationUtility syncUtil(_processVariableManager); + ChimeraTK::DeviceSynchronizationUtility syncUtil(_processVariableManager); while (!boost::this_thread::interruption_requested()) { syncUtil.receiveAll(); diff --git a/examples/cosade/testIndependentControlCore.cpp b/examples/cosade/testIndependentControlCore.cpp index e07bf76..ea2c3fb 100644 --- a/examples/cosade/testIndependentControlCore.cpp +++ b/examples/cosade/testIndependentControlCore.cpp @@ -12,7 +12,7 @@ #include "IndependentControlCore.h" using namespace boost::unit_test_framework; -using namespace mtca4u; +using namespace ChimeraTK; BOOST_AUTO_TEST_SUITE( IndependentControlCoreTestSuite ) diff --git a/include/ControlSystemPVManager.h b/include/ControlSystemPVManager.h index 165d9dd..bae1507 100644 --- a/include/ControlSystemPVManager.h +++ b/include/ControlSystemPVManager.h @@ -9,7 +9,7 @@ #include "PVManager.h" -namespace mtca4u { +namespace ChimeraTK { /** * Manager for process variables on the control-system side. Provides methods diff --git a/include/ControlSystemSynchronizationUtility.h b/include/ControlSystemSynchronizationUtility.h index f50276c..620e7e2 100644 --- a/include/ControlSystemSynchronizationUtility.h +++ b/include/ControlSystemSynchronizationUtility.h @@ -5,7 +5,7 @@ #include "ControlSystemPVManager.h" -namespace mtca4u { +namespace ChimeraTK { /** * Utility class for synchronizing process variables on the control-system @@ -174,6 +174,6 @@ namespace mtca4u { } } -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY diff --git a/include/DevicePVManager.h b/include/DevicePVManager.h index 9d9e4a1..b9da570 100644 --- a/include/DevicePVManager.h +++ b/include/DevicePVManager.h @@ -1,7 +1,7 @@ #ifndef MTCA4U_DEVICE_PV_MANAGER_H #define MTCA4U_DEVICE_PV_MANAGER_H -namespace mtca4u { +namespace ChimeraTK { class DevicePVManager; } @@ -13,7 +13,7 @@ namespace mtca4u { #include "PVManager.h" #include "SynchronizationDirection.h" -namespace mtca4u { +namespace ChimeraTK { /** * Manager for process variables on the device side. Provides methods for diff --git a/include/DeviceSynchronizationUtility.h b/include/DeviceSynchronizationUtility.h index 4021efd..fca5f72 100644 --- a/include/DeviceSynchronizationUtility.h +++ b/include/DeviceSynchronizationUtility.h @@ -5,7 +5,7 @@ #include "DevicePVManager.h" -namespace mtca4u { +namespace ChimeraTK { /** * Utility class for synchronizing process variables on the device side. This @@ -173,6 +173,6 @@ namespace mtca4u { } } -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_DEVICE_SYNCHRONIZATION_UTILITY diff --git a/include/ManualTimeStampSource.h b/include/ManualTimeStampSource.h index 75164c0..bca171f 100644 --- a/include/ManualTimeStampSource.h +++ b/include/ManualTimeStampSource.h @@ -1,7 +1,7 @@ #ifndef MTCA4U_MANUAL_TIME_STAMP_SOURCE_H #define MTCA4U_MANUAL_TIME_STAMP_SOURCE_H -namespace mtca4u { +namespace ChimeraTK { /** The code manually has to set a time stamp, which is then disctributed * to the connected PVs. diff --git a/include/PVManager.h b/include/PVManager.h index 10f325f..9849a26 100644 --- a/include/PVManager.h +++ b/include/PVManager.h @@ -15,7 +15,7 @@ #include "PVManagerDecl.h" -namespace mtca4u { +namespace ChimeraTK { // These declarations should actually be in the respective header files, // however this would cause problems with incomplete definitions due to diff --git a/include/PVManagerDecl.h b/include/PVManagerDecl.h index 946ca96..0453cbc 100644 --- a/include/PVManagerDecl.h +++ b/include/PVManagerDecl.h @@ -1,7 +1,7 @@ #ifndef MTCA4U_PV_MANAGER_DECL_H #define MTCA4U_PV_MANAGER_DECL_H -namespace mtca4u { +namespace ChimeraTK { class PVManager; } diff --git a/include/ProcessArray.h b/include/ProcessArray.h index f68f843..325cb49 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -18,7 +18,7 @@ #include "ProcessVariable.h" #include "ProcessVariableListener.h" -namespace mtca4u { +namespace ChimeraTK { /** * Array implementation of the ProcessVariable. This implementation is used for all * three use cases (sender, receiver, and stand-alone). @@ -743,6 +743,6 @@ namespace mtca4u { typename ProcessArray::SharedPtr>(sender, receiver); } -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_PROCESS_ARRAY_H diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 25b4bf7..5f41a3a 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -4,7 +4,7 @@ #include "ProcessArray.h" #include "ProcessVariableAccessor.h" -namespace mtca4u{ +namespace ChimeraTK{ template class ProcessArrayAccessor: public ProcessVariableAccessor{ @@ -65,6 +65,6 @@ namespace mtca4u{ }; -} //namespace mtca4u +} //namespace ChimeraTK #endif // MTCA4U_PROCESS_ARRAY_ACCESSOR_H diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index f6beb0e..f4ba279 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -18,7 +18,7 @@ #include "ProcessVariable.h" #include "ProcessVariableListener.h" -namespace mtca4u { +namespace ChimeraTK { /** * The scalar implementation of a ProcessVariable. This implementation is used for all @@ -412,6 +412,6 @@ namespace mtca4u { typename ProcessScalar::SharedPtr>(sender, receiver); } -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_PROCESS_SCALAR_H diff --git a/include/ProcessScalarAccessor.h b/include/ProcessScalarAccessor.h index e1e1c9f..8fdbdc6 100644 --- a/include/ProcessScalarAccessor.h +++ b/include/ProcessScalarAccessor.h @@ -4,7 +4,7 @@ #include "ProcessScalar.h" #include "ProcessVariableAccessor.h" -namespace mtca4u { +namespace ChimeraTK { template class ProcessScalarAccessor: public ProcessVariableAccessor{ diff --git a/include/ProcessVariable.h b/include/ProcessVariable.h index 18ab2f0..0bb5bba 100644 --- a/include/ProcessVariable.h +++ b/include/ProcessVariable.h @@ -1,7 +1,7 @@ #ifndef MTCA4U_PROCESS_VARIABLE_H #define MTCA4U_PROCESS_VARIABLE_H -namespace mtca4u { +namespace ChimeraTK { class ProcessVariable; } @@ -14,7 +14,7 @@ namespace mtca4u { #include "ProcessVariableDecl.h" -namespace mtca4u { +namespace ChimeraTK { /** * Base class for all {@link ProcessScalar}s and {@link ProcessArray}s. diff --git a/include/ProcessVariableAccessor.h b/include/ProcessVariableAccessor.h index b568887..24eb59d 100644 --- a/include/ProcessVariableAccessor.h +++ b/include/ProcessVariableAccessor.h @@ -3,7 +3,7 @@ #include "ProcessVariable.h" -namespace mtca4u { +namespace ChimeraTK { class ProcessVariableAccessor{ public: @@ -135,6 +135,6 @@ namespace mtca4u { ProcessVariableAccessor & operator=(ProcessVariableAccessor const & other); }; -} //namespace mtca4u +} //namespace ChimeraTK #endif // MTCA4U_PROCESS_VARIABLE_ACCESSOR_H diff --git a/include/ProcessVariableDecl.h b/include/ProcessVariableDecl.h index fb4debf..dc2452d 100644 --- a/include/ProcessVariableDecl.h +++ b/include/ProcessVariableDecl.h @@ -1,7 +1,7 @@ #ifndef MTCA4U_PROCESS_VARIABLE_DECL_H #define MTCA4U_PROCESS_VARIABLE_DECL_H -namespace mtca4u { +namespace ChimeraTK { class ProcessVariable; } diff --git a/include/ProcessVariableListener.h b/include/ProcessVariableListener.h index 44e62e2..945abde 100644 --- a/include/ProcessVariableListener.h +++ b/include/ProcessVariableListener.h @@ -5,7 +5,7 @@ #include "ProcessVariableDecl.h" -namespace mtca4u { +namespace ChimeraTK { /** * Listener receiving event notifications for a process variable. The exact diff --git a/include/SynchronizationDirection.h b/include/SynchronizationDirection.h index 1d80e4e..da1b88f 100644 --- a/include/SynchronizationDirection.h +++ b/include/SynchronizationDirection.h @@ -1,7 +1,7 @@ #ifndef MTCA4U_SYNCHRONIZATION_DIRECTION_H #define MTCA4U_SYNCHRONIZATION_DIRECTION_H -namespace mtca4u { +namespace ChimeraTK { /** * Direction when synchronizing process variables. A process variable may be diff --git a/include/TimeStamp.h b/include/TimeStamp.h index e2406d5..7536aa5 100644 --- a/include/TimeStamp.h +++ b/include/TimeStamp.h @@ -3,7 +3,7 @@ #include -namespace mtca4u { +namespace ChimeraTK { /** * The MTCA4U time stamp consists of the unix time stamp (number of seconds @@ -73,6 +73,6 @@ namespace mtca4u { }; -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_TIME_STAMP_H diff --git a/include/TimeStampSource.h b/include/TimeStampSource.h index f18ca1f..1e97ff6 100644 --- a/include/TimeStampSource.h +++ b/include/TimeStampSource.h @@ -5,7 +5,7 @@ #include "TimeStamp.h" -namespace mtca4u { +namespace ChimeraTK { /** * Interface for a time-stamp source. A time-stamp source is used by a process diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index be3da78..2e60569 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -1,6 +1,6 @@ #include "ControlSystemPVManager.h" -namespace mtca4u { +namespace ChimeraTK { ControlSystemPVManager::ControlSystemPVManager( boost::shared_ptr pvManager) : diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index b444121..3683afc 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -2,7 +2,7 @@ #include -namespace mtca4u { +namespace ChimeraTK { ControlSystemSynchronizationUtility::ControlSystemSynchronizationUtility( ControlSystemPVManager::SharedPtr pvManager) : @@ -76,4 +76,4 @@ namespace mtca4u { } while (boost::chrono::high_resolution_clock::now() < limit); } -} // namespace mtca4u +} // namespace ChimeraTK diff --git a/src/DevicePVManager.cc b/src/DevicePVManager.cc index 7b4dcdb..e4bd211 100644 --- a/src/DevicePVManager.cc +++ b/src/DevicePVManager.cc @@ -2,7 +2,7 @@ #include "DevicePVManager.h" -namespace mtca4u { +namespace ChimeraTK { DevicePVManager::DevicePVManager(boost::shared_ptr pvManager) : _pvManager(pvManager) { diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 0f22b50..252d705 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -1,6 +1,6 @@ #include -namespace mtca4u { +namespace ChimeraTK { DeviceSynchronizationUtility::DeviceSynchronizationUtility( DevicePVManager::SharedPtr pvManager) : @@ -74,4 +74,4 @@ namespace mtca4u { } while (boost::chrono::high_resolution_clock::now() < limit); } -} // namespace mtca4u +} // namespace ChimeraTK diff --git a/src/PVManager.cc b/src/PVManager.cc index cfffd03..6023abc 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -4,7 +4,7 @@ #include "ControlSystemPVManager.h" #include "DevicePVManager.h" -namespace mtca4u { +namespace ChimeraTK { using boost::atomic; using boost::shared_ptr; diff --git a/src/ProcessVariable.cc b/src/ProcessVariable.cc index 6290152..107f956 100644 --- a/src/ProcessVariable.cc +++ b/src/ProcessVariable.cc @@ -1,6 +1,6 @@ #include "ProcessVariable.h" -namespace mtca4u { +namespace ChimeraTK { ProcessVariable::ProcessVariable(const std::string& name) : _name(name) { diff --git a/tests/include/CountingProcessVariableListener.h b/tests/include/CountingProcessVariableListener.h index bbbca53..edfd12d 100644 --- a/tests/include/CountingProcessVariableListener.h +++ b/tests/include/CountingProcessVariableListener.h @@ -3,7 +3,7 @@ #include -namespace mtca4u { +namespace ChimeraTK { /** * Simple implementation of a process-variable listener that simply counts @@ -26,6 +26,6 @@ namespace mtca4u { }; -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_COUNTING_PROCESS_VARIABLE_LISTENER_H diff --git a/tests/include/CountingTimeStampSource.h b/tests/include/CountingTimeStampSource.h index 1d0e5f7..3926054 100644 --- a/tests/include/CountingTimeStampSource.h +++ b/tests/include/CountingTimeStampSource.h @@ -3,7 +3,7 @@ #include -namespace mtca4u { +namespace ChimeraTK { /** * Simple time-stamp source that returns a time stamp that has all fields @@ -24,6 +24,6 @@ namespace mtca4u { }; -} // namespace mtca4u +} // namespace ChimeraTK #endif // MTCA4U_COUNTING_TIME_STAMP_SOURCE_H diff --git a/tests/include/IndependentTestCore.h b/tests/include/IndependentTestCore.h index ab7ed6c..0ad73df 100644 --- a/tests/include/IndependentTestCore.h +++ b/tests/include/IndependentTestCore.h @@ -18,10 +18,10 @@ template struct TypedPVHolder{ - typename mtca4u::ProcessScalar::SharedPtr toDeviceScalar; - typename mtca4u::ProcessScalar::SharedPtr fromDeviceScalar; - typename mtca4u::ProcessArray::SharedPtr toDeviceArray; - typename mtca4u::ProcessArray::SharedPtr fromDeviceArray; + typename ChimeraTK::ProcessScalar::SharedPtr toDeviceScalar; + typename ChimeraTK::ProcessScalar::SharedPtr fromDeviceScalar; + typename ChimeraTK::ProcessArray::SharedPtr toDeviceArray; + typename ChimeraTK::ProcessArray::SharedPtr fromDeviceArray; /** The "data type constant" is a value that depends on the data type. It is intended as * 'magic' constant which can be read out to test reading because the value it knows. * @@ -30,16 +30,16 @@ struct TypedPVHolder{ * \li -sizeof(type) for signed integer types * \li 1./sizeof(type) for floating point types */ - typename mtca4u::ProcessScalar::SharedPtr dataTypeConstant; - typename mtca4u::ProcessArray::SharedPtr constantArray; + typename ChimeraTK::ProcessScalar::SharedPtr dataTypeConstant; + typename ChimeraTK::ProcessArray::SharedPtr constantArray; - TypedPVHolder(boost::shared_ptr const & processVariableManager, std::string typeNamePrefix): - toDeviceScalar( processVariableManager->createProcessScalar(mtca4u::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_SCALAR") ), - fromDeviceScalar( processVariableManager->createProcessScalar(mtca4u::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_SCALAR") ), - toDeviceArray( processVariableManager->createProcessArray(mtca4u::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_ARRAY", 10) ), - fromDeviceArray( processVariableManager->createProcessArray(mtca4u::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_ARRAY", 10) ), - dataTypeConstant( processVariableManager->createProcessScalar(mtca4u::deviceToControlSystem, typeNamePrefix + "/DATA_TYPE_CONSTANT") ), - constantArray( processVariableManager->createProcessArray(mtca4u::deviceToControlSystem, typeNamePrefix + "/CONSTANT_ARRAY",10) ){ + TypedPVHolder(boost::shared_ptr const & processVariableManager, std::string typeNamePrefix): + toDeviceScalar( processVariableManager->createProcessScalar(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_SCALAR") ), + fromDeviceScalar( processVariableManager->createProcessScalar(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_SCALAR") ), + toDeviceArray( processVariableManager->createProcessArray(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_ARRAY", 10) ), + fromDeviceArray( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_ARRAY", 10) ), + dataTypeConstant( processVariableManager->createProcessScalar(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/DATA_TYPE_CONSTANT") ), + constantArray( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/CONSTANT_ARRAY",10) ){ if (std::numeric_limits::is_integer){ if (std::numeric_limits::is_signed){ // signed int @@ -82,13 +82,13 @@ typedef boost::fusion::map< class IndependentTestCore{ public: - mtca4u::DevicePVManager::SharedPtr processVariableManager; + ChimeraTK::DevicePVManager::SharedPtr processVariableManager; boost::scoped_ptr< boost::thread > _deviceThread; HolderMap holderMap; // the syncUtil needs to be initalised after the PVs are added to the manager - mtca4u::DeviceSynchronizationUtility syncUtil; + ChimeraTK::DeviceSynchronizationUtility syncUtil; static std::mutex & mainLoopMutex(){ static std::mutex _mainLoopMutex; @@ -127,7 +127,7 @@ class IndependentTestCore{ * If needed for the test, a thread can be started which automatically executes the 'mainBody()' function in * an endless loop. */ - IndependentTestCore(boost::shared_ptr const & processVariableManager_) + IndependentTestCore(boost::shared_ptr const & processVariableManager_) //initialise all process variables, using the factory : processVariableManager( processVariableManager_ ), holderMap( diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index a04aada..cef92ff 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -6,7 +6,7 @@ #include "IndependentTestCore.h" using namespace boost::unit_test_framework; -using namespace mtca4u; +using namespace ChimeraTK; struct TestCoreFixture{ std::pair, diff --git a/tests/src/testControlSystemSynchronizationUtility.cpp b/tests/src/testControlSystemSynchronizationUtility.cpp index afa5f28..b850541 100644 --- a/tests/src/testControlSystemSynchronizationUtility.cpp +++ b/tests/src/testControlSystemSynchronizationUtility.cpp @@ -16,7 +16,7 @@ #include using namespace boost::unit_test_framework; -using namespace mtca4u; +using namespace ChimeraTK; // Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index b053583..bc8eb73 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -16,7 +16,7 @@ #include using namespace boost::unit_test_framework; -using namespace mtca4u; +using namespace ChimeraTK; // Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 7097b7c..63e0aed 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -6,7 +6,7 @@ #include "ProcessArrayAccessor.h" #include "ManualTimeStampSource.h" -using namespace mtca4u; +using namespace ChimeraTK; BOOST_AUTO_TEST_SUITE( PvAccessorsTestSuite ) diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 4502167..46bbc14 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -13,7 +13,7 @@ #include using namespace boost::unit_test_framework; -using namespace mtca4u; +using namespace ChimeraTK; using boost::shared_ptr; using std::list; diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 6b6c1f7..27d5123 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -10,7 +10,7 @@ using namespace boost::unit_test_framework; -namespace mtca4u { +namespace ChimeraTK { /** * The test class for the ProcessArray. @@ -429,25 +429,25 @@ namespace mtca4u { BOOST_CHECK(!receiver->receive()); } -} //namespace mtca4u +} //namespace ChimeraTK test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { framework::master_test_suite().p_name.value = "ProcessArray test suite"; framework::master_test_suite().add( - new mtca4u::ProcessArrayTestSuite); + new ChimeraTK::ProcessArrayTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessArrayTestSuite); + new ChimeraTK::ProcessArrayTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessArrayTestSuite); + new ChimeraTK::ProcessArrayTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessArrayTestSuite); - framework::master_test_suite().add(new mtca4u::ProcessArrayTestSuite); + new ChimeraTK::ProcessArrayTestSuite); + framework::master_test_suite().add(new ChimeraTK::ProcessArrayTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessArrayTestSuite); - framework::master_test_suite().add(new mtca4u::ProcessArrayTestSuite); - framework::master_test_suite().add(new mtca4u::ProcessArrayTestSuite); + new ChimeraTK::ProcessArrayTestSuite); + framework::master_test_suite().add(new ChimeraTK::ProcessArrayTestSuite); + framework::master_test_suite().add(new ChimeraTK::ProcessArrayTestSuite); return NULL; } diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp index 9d26030..dab3bf3 100644 --- a/tests/src/testProcessScalar.cpp +++ b/tests/src/testProcessScalar.cpp @@ -8,7 +8,7 @@ using namespace boost::unit_test_framework; #include #include -namespace mtca4u { +namespace ChimeraTK { /** * The test class for the ProcessScalar. * It is templated to be tested with all data types. @@ -221,27 +221,27 @@ namespace mtca4u { BOOST_CHECK(*receiver == 46); } -} //namespace mtca4u +} //namespace ChimeraTK test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { framework::master_test_suite().p_name.value = "ProcessScalar test suite"; framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( - new mtca4u::ProcessScalarTestSuite); - framework::master_test_suite().add(new mtca4u::ProcessScalarTestSuite); + new ChimeraTK::ProcessScalarTestSuite); + framework::master_test_suite().add(new ChimeraTK::ProcessScalarTestSuite); return NULL; } diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index 5602f6f..abedf52 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -6,7 +6,7 @@ #include "IndependentTestCore.h" using namespace boost::unit_test_framework; -using namespace mtca4u; +using namespace ChimeraTK; // We are testing the reference core when executed with it's own thread running. // Synchronisation is done through the atomic mainBodyCompletelyExecuted variable. diff --git a/tests/src/testTimeStamp.cpp b/tests/src/testTimeStamp.cpp index b1d8f4b..4a74cd4 100644 --- a/tests/src/testTimeStamp.cpp +++ b/tests/src/testTimeStamp.cpp @@ -7,7 +7,7 @@ using namespace boost::unit_test_framework; #include #include "TimeStamp.h" -using namespace mtca4u; +using namespace ChimeraTK; // Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( TimeStampTestSuite ) From ec600e4c02b2b27953edf49b7780670bc8253ae5 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 12 Aug 2016 14:06:47 +0200 Subject: [PATCH 066/786] Changed MTCA4U prefix in include guards (issue #3). The prefix used for the include guards has been changed from MTCA4U_ to CHIMERA_TK_CONTORL_SYSTEM_ADAPTER. This change is consistent with the new directory layout that is going to be used. --- include/ControlSystemPVManager.h | 6 +++--- include/ControlSystemSynchronizationUtility.h | 6 +++--- include/DevicePVManager.h | 6 +++--- include/DeviceSynchronizationUtility.h | 6 +++--- include/ManualTimeStampSource.h | 6 +++--- include/PVManager.h | 6 +++--- include/PVManagerDecl.h | 6 +++--- include/ProcessArray.h | 6 +++--- include/ProcessArrayAccessor.h | 6 +++--- include/ProcessScalar.h | 6 +++--- include/ProcessScalarAccessor.h | 6 +++--- include/ProcessVariable.h | 6 +++--- include/ProcessVariableAccessor.h | 6 +++--- include/ProcessVariableDecl.h | 6 +++--- include/ProcessVariableListener.h | 6 +++--- include/SynchronizationDirection.h | 6 +++--- include/TimeStamp.h | 6 +++--- include/TimeStampSource.h | 6 +++--- tests/include/CountingProcessVariableListener.h | 6 +++--- tests/include/CountingTimeStampSource.h | 6 +++--- 20 files changed, 60 insertions(+), 60 deletions(-) diff --git a/include/ControlSystemPVManager.h b/include/ControlSystemPVManager.h index bae1507..8c95389 100644 --- a/include/ControlSystemPVManager.h +++ b/include/ControlSystemPVManager.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_CONTROL_SYSTEM_PV_MANAGER_H -#define MTCA4U_CONTROL_SYSTEM_PV_MANAGER_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_PV_MANAGER_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_PV_MANAGER_H #include #include @@ -151,4 +151,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_CONTROL_SYSTEM_PV_MANAGER_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_PV_MANAGER_H diff --git a/include/ControlSystemSynchronizationUtility.h b/include/ControlSystemSynchronizationUtility.h index 620e7e2..6220112 100644 --- a/include/ControlSystemSynchronizationUtility.h +++ b/include/ControlSystemSynchronizationUtility.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY -#define MTCA4U_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY #include @@ -176,4 +176,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY diff --git a/include/DevicePVManager.h b/include/DevicePVManager.h index b9da570..e4a6dae 100644 --- a/include/DevicePVManager.h +++ b/include/DevicePVManager.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_DEVICE_PV_MANAGER_H -#define MTCA4U_DEVICE_PV_MANAGER_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_PV_MANAGER_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_PV_MANAGER_H namespace ChimeraTK { class DevicePVManager; @@ -325,4 +325,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_DEVICE_PV_MANAGER_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_PV_MANAGER_H diff --git a/include/DeviceSynchronizationUtility.h b/include/DeviceSynchronizationUtility.h index fca5f72..9547c4b 100644 --- a/include/DeviceSynchronizationUtility.h +++ b/include/DeviceSynchronizationUtility.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_DEVICE_SYNCHRONIZATION_UTILITY -#define MTCA4U_DEVICE_SYNCHRONIZATION_UTILITY +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_SYNCHRONIZATION_UTILITY +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_SYNCHRONIZATION_UTILITY #include @@ -175,4 +175,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_DEVICE_SYNCHRONIZATION_UTILITY +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_SYNCHRONIZATION_UTILITY diff --git a/include/ManualTimeStampSource.h b/include/ManualTimeStampSource.h index bca171f..2021dc2 100644 --- a/include/ManualTimeStampSource.h +++ b/include/ManualTimeStampSource.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_MANUAL_TIME_STAMP_SOURCE_H -#define MTCA4U_MANUAL_TIME_STAMP_SOURCE_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_MANUAL_TIME_STAMP_SOURCE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_MANUAL_TIME_STAMP_SOURCE_H namespace ChimeraTK { @@ -21,4 +21,4 @@ namespace ChimeraTK { }; } -#endif // MTCA4U_MANUAL_TIME_STAMP_SOURCE_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_MANUAL_TIME_STAMP_SOURCE_H diff --git a/include/PVManager.h b/include/PVManager.h index 9849a26..1256225 100644 --- a/include/PVManager.h +++ b/include/PVManager.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PV_MANAGER_H -#define MTCA4U_PV_MANAGER_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PV_MANAGER_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PV_MANAGER_H #include #include @@ -678,4 +678,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_PV_MANAGER_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PV_MANAGER_H diff --git a/include/PVManagerDecl.h b/include/PVManagerDecl.h index 0453cbc..eafddb3 100644 --- a/include/PVManagerDecl.h +++ b/include/PVManagerDecl.h @@ -1,8 +1,8 @@ -#ifndef MTCA4U_PV_MANAGER_DECL_H -#define MTCA4U_PV_MANAGER_DECL_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PV_MANAGER_DECL_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PV_MANAGER_DECL_H namespace ChimeraTK { class PVManager; } -#endif // MTCA4U_PV_MANAGER_DECL_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PV_MANAGER_DECL_H diff --git a/include/ProcessArray.h b/include/ProcessArray.h index 325cb49..602ee44 100644 --- a/include/ProcessArray.h +++ b/include/ProcessArray.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_ARRAY_H -#define MTCA4U_PROCESS_ARRAY_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_H #include #include @@ -745,4 +745,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_PROCESS_ARRAY_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_H diff --git a/include/ProcessArrayAccessor.h b/include/ProcessArrayAccessor.h index 5f41a3a..d066f18 100644 --- a/include/ProcessArrayAccessor.h +++ b/include/ProcessArrayAccessor.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_ARRAY_ACCESSOR_H -#define MTCA4U_PROCESS_ARRAY_ACCESSOR_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_ACCESSOR_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_ACCESSOR_H #include "ProcessArray.h" #include "ProcessVariableAccessor.h" @@ -67,4 +67,4 @@ namespace ChimeraTK{ } //namespace ChimeraTK -#endif // MTCA4U_PROCESS_ARRAY_ACCESSOR_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_ACCESSOR_H diff --git a/include/ProcessScalar.h b/include/ProcessScalar.h index f4ba279..18f5caa 100644 --- a/include/ProcessScalar.h +++ b/include/ProcessScalar.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_SCALAR_H -#define MTCA4U_PROCESS_SCALAR_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_H #include @@ -414,4 +414,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_PROCESS_SCALAR_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_H diff --git a/include/ProcessScalarAccessor.h b/include/ProcessScalarAccessor.h index 8fdbdc6..0b4ab78 100644 --- a/include/ProcessScalarAccessor.h +++ b/include/ProcessScalarAccessor.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_SCALAR_ACCESSOR_H -#define MTCA4U_PROCESS_SCALAR_ACCESSOR_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_ACCESSOR_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_ACCESSOR_H #include "ProcessScalar.h" #include "ProcessVariableAccessor.h" @@ -72,4 +72,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_PROCESS_SCALAR_ACCESSOR_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_ACCESSOR_H diff --git a/include/ProcessVariable.h b/include/ProcessVariable.h index 0bb5bba..82a0a5f 100644 --- a/include/ProcessVariable.h +++ b/include/ProcessVariable.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_VARIABLE_H -#define MTCA4U_PROCESS_VARIABLE_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H namespace ChimeraTK { class ProcessVariable; @@ -116,4 +116,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_PROCESS_VARIABLE_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H diff --git a/include/ProcessVariableAccessor.h b/include/ProcessVariableAccessor.h index 24eb59d..e5fac23 100644 --- a/include/ProcessVariableAccessor.h +++ b/include/ProcessVariableAccessor.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_VARIABLE_ACCESSOR_H -#define MTCA4U_PROCESS_VARIABLE_ACCESSOR_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_ACCESSOR_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_ACCESSOR_H #include "ProcessVariable.h" @@ -137,4 +137,4 @@ namespace ChimeraTK { } //namespace ChimeraTK -#endif // MTCA4U_PROCESS_VARIABLE_ACCESSOR_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_ACCESSOR_H diff --git a/include/ProcessVariableDecl.h b/include/ProcessVariableDecl.h index dc2452d..a45d2b4 100644 --- a/include/ProcessVariableDecl.h +++ b/include/ProcessVariableDecl.h @@ -1,8 +1,8 @@ -#ifndef MTCA4U_PROCESS_VARIABLE_DECL_H -#define MTCA4U_PROCESS_VARIABLE_DECL_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_DECL_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_DECL_H namespace ChimeraTK { class ProcessVariable; } -#endif // MTCA4U_PROCESS_VARIABLE_DECL_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_DECL_H diff --git a/include/ProcessVariableListener.h b/include/ProcessVariableListener.h index 945abde..5714c70 100644 --- a/include/ProcessVariableListener.h +++ b/include/ProcessVariableListener.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_PROCESS_VARIABLE_LISTENER_H -#define MTCA4U_PROCESS_VARIABLE_LISTENER_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_LISTENER_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_LISTENER_H #include @@ -38,4 +38,4 @@ class ProcessVariableListener { } -#endif // MTCA4U_PROCESS_VARIABLE_LISTENER_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_LISTENER_H diff --git a/include/SynchronizationDirection.h b/include/SynchronizationDirection.h index da1b88f..0cbc24f 100644 --- a/include/SynchronizationDirection.h +++ b/include/SynchronizationDirection.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_SYNCHRONIZATION_DIRECTION_H -#define MTCA4U_SYNCHRONIZATION_DIRECTION_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_SYNCHRONIZATION_DIRECTION_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_SYNCHRONIZATION_DIRECTION_H namespace ChimeraTK { @@ -24,4 +24,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_SYNCHRONIZATION_DIRECTION_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_SYNCHRONIZATION_DIRECTION_H diff --git a/include/TimeStamp.h b/include/TimeStamp.h index 7536aa5..ee30cd2 100644 --- a/include/TimeStamp.h +++ b/include/TimeStamp.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_TIME_STAMP_H -#define MTCA4U_TIME_STAMP_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H #include @@ -75,4 +75,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_TIME_STAMP_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H diff --git a/include/TimeStampSource.h b/include/TimeStampSource.h index 1e97ff6..593eb9b 100644 --- a/include/TimeStampSource.h +++ b/include/TimeStampSource.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_TIME_STAMP_SOURCE_H -#define MTCA4U_TIME_STAMP_SOURCE_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_SOURCE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_SOURCE_H #include @@ -39,4 +39,4 @@ namespace ChimeraTK { } -#endif // MTCA4U_TIME_STAMP_SOURCE_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_SOURCE_H diff --git a/tests/include/CountingProcessVariableListener.h b/tests/include/CountingProcessVariableListener.h index edfd12d..525146d 100644 --- a/tests/include/CountingProcessVariableListener.h +++ b/tests/include/CountingProcessVariableListener.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_COUNTING_PROCESS_VARIABLE_LISTENER_H -#define MTCA4U_COUNTING_PROCESS_VARIABLE_LISTENER_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H #include @@ -28,4 +28,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_COUNTING_PROCESS_VARIABLE_LISTENER_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H diff --git a/tests/include/CountingTimeStampSource.h b/tests/include/CountingTimeStampSource.h index 3926054..855229f 100644 --- a/tests/include/CountingTimeStampSource.h +++ b/tests/include/CountingTimeStampSource.h @@ -1,5 +1,5 @@ -#ifndef MTCA4U_COUNTING_TIME_STAMP_SOURCE_H -#define MTCA4U_COUNTING_TIME_STAMP_SOURCE_H +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_TIME_STAMP_SOURCE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_TIME_STAMP_SOURCE_H #include @@ -26,4 +26,4 @@ namespace ChimeraTK { } // namespace ChimeraTK -#endif // MTCA4U_COUNTING_TIME_STAMP_SOURCE_H +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_TIME_STAMP_SOURCE_H From 46453f02cfe10ec7f56958581151413ca2e7a013 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 12 Aug 2016 14:11:06 +0200 Subject: [PATCH 067/786] Replaced "MTCA4U" with "ChimeraTK" in the documentation (issue #3). --- doc/Doxyfile | 2 +- .../ControlSystemAdapterRequirements.tex | 12 ++++++------ include/TimeStamp.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/Doxyfile b/doc/Doxyfile index 4cd3115..afb9f4f 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "MTCA4U Control System Tools" +PROJECT_NAME = "ChimeraTK Control System Adapter" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version diff --git a/doc/RequirementsDocument/ControlSystemAdapterRequirements.tex b/doc/RequirementsDocument/ControlSystemAdapterRequirements.tex index fddfb5c..f904c7c 100644 --- a/doc/RequirementsDocument/ControlSystemAdapterRequirements.tex +++ b/doc/RequirementsDocument/ControlSystemAdapterRequirements.tex @@ -44,7 +44,7 @@ \SetWatermarkScale{3} \SetWatermarkText{DRAFT \svnshortrev} -\title{Requirement for the\\ MTCA4U Control System Adapter} +\title{Requirement for the\\ ChimeraTK Control System Adapter} \date{\svnrevision\\ PDF build \today} \author[1]{Martin Killenberg} \author[2]{Sebastian Marsching} \author[3]{Adam Poitrokwsi} @@ -62,7 +62,7 @@ \maketitle \section{Objective and Scope} -MTCA4U's control system interface adapter is intended to decouple the business +ChimeraTK's control system interface adapter is intended to decouple the business logic of a server from the actual control system in use. For this it provides two functionalities: \begin{enumerate} @@ -140,7 +140,7 @@ \subsection{Strings} \subsection{Arrays} -MTCA4U supports arrays of all supported simple data types. +ChimeraTK supports arrays of all supported simple data types. \section{Requirements for the ProcessVariable Adapter}\label{section_process_variable_adapter} @@ -171,7 +171,7 @@ \section{Requirements for the ProcessVariable Adapter}\label{section_process_var system variables are thread safe, then the adapter is also thread safe as it does no further caching but directly accesses the underlying variable. But this is not guaranteed by the interface and the user has to implement a thread safe -mechanism himself. MTCA4U will provide thread support as a separate tool of +mechanism himself. ChimeraTK will provide thread support as a separate tool of the Control System Tools package, probably implementing an actor pattern. \subsection{The ProcessVariable adapter interface features} @@ -442,7 +442,7 @@ \subsection{Functions Triggered by the Business Logic} \fixme{How does this look like if the control system class is a/ requires a state machine? Is business logic which is not a state machine compatible? How do both machines talk to each other? -Do we need a state machine adapter? Should all business logic in MTCA4U also be a state machine?} +Do we need a state machine adapter? Should all business logic in ChimeraTK also be a state machine?} \section{Further, General Requirements} As we have to support Ubuntu 12.4, C++11 is not used because the C++11 support @@ -511,7 +511,7 @@ \section{DOOCS Variable Types} \end{itemize} Although D\_\textit{xxx}array seems like the obvious choice, it is almost never used in practice. -Usually a D\_spectrum is used because it can directly be plotted in a panel. MTCA4U +Usually a D\_spectrum is used because it can directly be plotted in a panel. ChimeraTK will follow this approach and implement arrays as D\_spectrum in the DOOCS adapter. \fixme{The D\_spectrum only exists for floats, which means it can only hold integers up to 24 bits. What to do with real 32 bit arrays?} diff --git a/include/TimeStamp.h b/include/TimeStamp.h index ee30cd2..00b57e8 100644 --- a/include/TimeStamp.h +++ b/include/TimeStamp.h @@ -6,7 +6,7 @@ namespace ChimeraTK { /** - * The MTCA4U time stamp consists of the unix time stamp (number of seconds + * The ChimeraTK time stamp consists of the unix time stamp (number of seconds * since 01.01.1970 00:00:00 UTC). * The unsigned 32 bit value will have an overflow on Sun, 7 February 2106. * If you need higher resolution than once per second, the nanoseconds field From faa0d1f200c9b2f1d154e8c2d601e865ec8944b0 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 12 Aug 2016 14:52:12 +0200 Subject: [PATCH 068/786] Reorganized the header files (issue #3). The header files are all put in the ChimeraTK/ControlSystemAdapter directory, so that they use the same path in the source and the install directory. Three new header files are added: - ChimeraTK/ControlSystemAdapter.h - ChimeraTK/ControlSystemAdapterControlSystem.h - ChimeraTK/ControlSystemAdapterDevice.h They can be used to include all headers or all headers, the headers needed for the control-system side, or the headers needed for the device side. --- CMakeLists.txt | 14 ++++++++------ examples/cosade/IndependentControlCore.h | 8 ++++---- examples/cosade/testIndependentControlCore.cpp | 5 +---- include/ChimeraTK/ControlSystemAdapter.h | 2 ++ .../ControlSystemAdapter}/ControlSystemPVManager.h | 0 .../ControlSystemSynchronizationUtility.h | 0 .../ControlSystemAdapter}/DevicePVManager.h | 0 .../DeviceSynchronizationUtility.h | 0 .../ControlSystemAdapter}/ManualTimeStampSource.h | 0 .../ControlSystemAdapter}/PVManager.h | 0 .../ControlSystemAdapter}/PVManagerDecl.h | 0 .../ControlSystemAdapter}/ProcessArray.h | 0 .../ControlSystemAdapter}/ProcessArrayAccessor.h | 0 .../ControlSystemAdapter}/ProcessScalar.h | 0 .../ControlSystemAdapter}/ProcessScalarAccessor.h | 0 .../ControlSystemAdapter}/ProcessVariable.h | 0 .../ProcessVariableAccessor.h | 0 .../ControlSystemAdapter}/ProcessVariableDecl.h | 0 .../ProcessVariableListener.h | 0 .../SynchronizationDirection.h | 0 .../ControlSystemAdapter}/TimeStamp.h | 0 .../ControlSystemAdapter}/TimeStampSource.h | 0 .../ChimeraTK/ControlSystemAdapterControlSystem.h | 13 +++++++++++++ include/ChimeraTK/ControlSystemAdapterDevice.h | 13 +++++++++++++ include/ControlSystemAdapter | 1 - src/ControlSystemPVManager.cc | 2 +- src/ControlSystemSynchronizationUtility.cc | 2 +- src/DevicePVManager.cc | 2 +- src/DeviceSynchronizationUtility.cc | 2 +- src/PVManager.cc | 6 +++--- src/ProcessVariable.cc | 2 +- .../Testing}/CountingProcessVariableListener.h | 2 +- .../Testing}/CountingTimeStampSource.h | 2 +- .../Testing}/IndependentTestCore.h | 8 ++++---- tests/src/fullStubTest.cpp | 4 ++-- .../testControlSystemSynchronizationUtility.cpp | 8 ++++---- tests/src/testDeviceSynchronizationUtility.cpp | 8 ++++---- tests/src/testPVAccessors.cpp | 6 +++--- tests/src/testPVManager.cpp | 6 +++--- tests/src/testProcessArray.cpp | 6 +++--- tests/src/testProcessScalar.cpp | 6 +++--- tests/src/testReferenceTestCoreWithTread.cpp | 4 ++-- tests/src/testTimeStamp.cpp | 3 ++- 43 files changed, 81 insertions(+), 54 deletions(-) create mode 100644 include/ChimeraTK/ControlSystemAdapter.h rename include/{ => ChimeraTK/ControlSystemAdapter}/ControlSystemPVManager.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ControlSystemSynchronizationUtility.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/DevicePVManager.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/DeviceSynchronizationUtility.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ManualTimeStampSource.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/PVManager.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/PVManagerDecl.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessArray.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessArrayAccessor.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessScalar.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessScalarAccessor.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessVariable.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessVariableAccessor.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessVariableDecl.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/ProcessVariableListener.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/SynchronizationDirection.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/TimeStamp.h (100%) rename include/{ => ChimeraTK/ControlSystemAdapter}/TimeStampSource.h (100%) create mode 100644 include/ChimeraTK/ControlSystemAdapterControlSystem.h create mode 100644 include/ChimeraTK/ControlSystemAdapterDevice.h delete mode 120000 include/ControlSystemAdapter rename tests/include/{ => ChimeraTK/ControlSystemAdapter/Testing}/CountingProcessVariableListener.h (92%) rename tests/include/{ => ChimeraTK/ControlSystemAdapter/Testing}/CountingTimeStampSource.h (92%) rename tests/include/{ => ChimeraTK/ControlSystemAdapter/Testing}/IndependentTestCore.h (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index aca8df1..cb16dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,14 +92,16 @@ install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION lib ) -#all include files go into include/PROJECT_NAME -install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/${PROJECT_NAME} - FILES_MATCHING PATTERN "*.h" - PATTERN ".svn" EXCLUDE - PATTERN "${PROJECT_NAME}" EXCLUDE) +#all include files go into include +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include + FILES_MATCHING + PATTERN "ChimeraTK/*.h" + PATTERN "ChimeraTK/ControlSystemAdapter/*.h") #We additionally install the independent reference test application, it's header only -install(FILES ${CMAKE_SOURCE_DIR}/tests/include/IndependentTestCore.h DESTINATION include/${PROJECT_NAME}/testing) +install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include + FILES_MATCHING + PATTERN "ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h") #Create the config files by filling the correct variables into the template (*.cmake.in). #All variables @VARIABLE@ are replaced with the current values, but they have to be set first.... diff --git a/examples/cosade/IndependentControlCore.h b/examples/cosade/IndependentControlCore.h index 415457b..c4c333c 100644 --- a/examples/cosade/IndependentControlCore.h +++ b/examples/cosade/IndependentControlCore.h @@ -9,10 +9,10 @@ //#include //#include //#include -#include -#include -#include -#include +#include +#include +#include +#include /** Some dummy "hardware". You can read/write a voltage (int). */ class Hardware{ diff --git a/examples/cosade/testIndependentControlCore.cpp b/examples/cosade/testIndependentControlCore.cpp index ea2c3fb..29a3905 100644 --- a/examples/cosade/testIndependentControlCore.cpp +++ b/examples/cosade/testIndependentControlCore.cpp @@ -4,10 +4,7 @@ // Only after defining the name include the unit test header. #include -//FIXME: in a real example an installed version of the adapter has to be used, -//which has the headers installed under the namespace folder -//#include -#include +#include #include "IndependentControlCore.h" diff --git a/include/ChimeraTK/ControlSystemAdapter.h b/include/ChimeraTK/ControlSystemAdapter.h new file mode 100644 index 0000000..2ad6130 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter.h @@ -0,0 +1,2 @@ +#include "ControlSystemAdapterControlSystem.h" +#include "ControlSystemAdapterDevice.h" diff --git a/include/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h similarity index 100% rename from include/ControlSystemPVManager.h rename to include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h diff --git a/include/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h similarity index 100% rename from include/ControlSystemSynchronizationUtility.h rename to include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h diff --git a/include/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h similarity index 100% rename from include/DevicePVManager.h rename to include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h diff --git a/include/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h similarity index 100% rename from include/DeviceSynchronizationUtility.h rename to include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h diff --git a/include/ManualTimeStampSource.h b/include/ChimeraTK/ControlSystemAdapter/ManualTimeStampSource.h similarity index 100% rename from include/ManualTimeStampSource.h rename to include/ChimeraTK/ControlSystemAdapter/ManualTimeStampSource.h diff --git a/include/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h similarity index 100% rename from include/PVManager.h rename to include/ChimeraTK/ControlSystemAdapter/PVManager.h diff --git a/include/PVManagerDecl.h b/include/ChimeraTK/ControlSystemAdapter/PVManagerDecl.h similarity index 100% rename from include/PVManagerDecl.h rename to include/ChimeraTK/ControlSystemAdapter/PVManagerDecl.h diff --git a/include/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h similarity index 100% rename from include/ProcessArray.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessArray.h diff --git a/include/ProcessArrayAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h similarity index 100% rename from include/ProcessArrayAccessor.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h diff --git a/include/ProcessScalar.h b/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h similarity index 100% rename from include/ProcessScalar.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h diff --git a/include/ProcessScalarAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessScalarAccessor.h similarity index 100% rename from include/ProcessScalarAccessor.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessScalarAccessor.h diff --git a/include/ProcessVariable.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h similarity index 100% rename from include/ProcessVariable.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h diff --git a/include/ProcessVariableAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h similarity index 100% rename from include/ProcessVariableAccessor.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h diff --git a/include/ProcessVariableDecl.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableDecl.h similarity index 100% rename from include/ProcessVariableDecl.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessVariableDecl.h diff --git a/include/ProcessVariableListener.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h similarity index 100% rename from include/ProcessVariableListener.h rename to include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h diff --git a/include/SynchronizationDirection.h b/include/ChimeraTK/ControlSystemAdapter/SynchronizationDirection.h similarity index 100% rename from include/SynchronizationDirection.h rename to include/ChimeraTK/ControlSystemAdapter/SynchronizationDirection.h diff --git a/include/TimeStamp.h b/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h similarity index 100% rename from include/TimeStamp.h rename to include/ChimeraTK/ControlSystemAdapter/TimeStamp.h diff --git a/include/TimeStampSource.h b/include/ChimeraTK/ControlSystemAdapter/TimeStampSource.h similarity index 100% rename from include/TimeStampSource.h rename to include/ChimeraTK/ControlSystemAdapter/TimeStampSource.h diff --git a/include/ChimeraTK/ControlSystemAdapterControlSystem.h b/include/ChimeraTK/ControlSystemAdapterControlSystem.h new file mode 100644 index 0000000..d83becf --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapterControlSystem.h @@ -0,0 +1,13 @@ +#include "ControlSystemAdapter/ControlSystemPVManager.h" +#include "ControlSystemAdapter/ControlSystemSynchronizationUtility.h" +#include "ControlSystemAdapter/ManualTimeStampSource.h" +#include "ControlSystemAdapter/ProcessArray.h" +#include "ControlSystemAdapter/ProcessArrayAccessor.h" +#include "ControlSystemAdapter/ProcessScalar.h" +#include "ControlSystemAdapter/ProcessScalarAccessor.h" +#include "ControlSystemAdapter/ProcessVariable.h" +#include "ControlSystemAdapter/ProcessVariableAccessor.h" +#include "ControlSystemAdapter/ProcessVariableListener.h" +#include "ControlSystemAdapter/SynchronizationDirection.h" +#include "ControlSystemAdapter/TimeStamp.h" +#include "ControlSystemAdapter/TimeStampSource.h" diff --git a/include/ChimeraTK/ControlSystemAdapterDevice.h b/include/ChimeraTK/ControlSystemAdapterDevice.h new file mode 100644 index 0000000..255f7ef --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapterDevice.h @@ -0,0 +1,13 @@ +#include "ControlSystemAdapter/DevicePVManager.h" +#include "ControlSystemAdapter/DeviceSynchronizationUtility.h" +#include "ControlSystemAdapter/ManualTimeStampSource.h" +#include "ControlSystemAdapter/ProcessArray.h" +#include "ControlSystemAdapter/ProcessArrayAccessor.h" +#include "ControlSystemAdapter/ProcessScalar.h" +#include "ControlSystemAdapter/ProcessScalarAccessor.h" +#include "ControlSystemAdapter/ProcessVariable.h" +#include "ControlSystemAdapter/ProcessVariableAccessor.h" +#include "ControlSystemAdapter/ProcessVariableListener.h" +#include "ControlSystemAdapter/SynchronizationDirection.h" +#include "ControlSystemAdapter/TimeStamp.h" +#include "ControlSystemAdapter/TimeStampSource.h" diff --git a/include/ControlSystemAdapter b/include/ControlSystemAdapter deleted file mode 120000 index 945c9b4..0000000 --- a/include/ControlSystemAdapter +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index 2e60569..890a7b4 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -1,4 +1,4 @@ -#include "ControlSystemPVManager.h" +#include namespace ChimeraTK { diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index 3683afc..baeab03 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -1,6 +1,6 @@ #include -#include +#include namespace ChimeraTK { diff --git a/src/DevicePVManager.cc b/src/DevicePVManager.cc index e4bd211..0f991c5 100644 --- a/src/DevicePVManager.cc +++ b/src/DevicePVManager.cc @@ -1,6 +1,6 @@ #include -#include "DevicePVManager.h" +#include namespace ChimeraTK { diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 252d705..0bc3f0f 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -1,4 +1,4 @@ -#include +#include namespace ChimeraTK { diff --git a/src/PVManager.cc b/src/PVManager.cc index 6023abc..07535fa 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -1,8 +1,8 @@ #include -#include "PVManager.h" -#include "ControlSystemPVManager.h" -#include "DevicePVManager.h" +#include +#include +#include namespace ChimeraTK { diff --git a/src/ProcessVariable.cc b/src/ProcessVariable.cc index 107f956..e9cd935 100644 --- a/src/ProcessVariable.cc +++ b/src/ProcessVariable.cc @@ -1,4 +1,4 @@ -#include "ProcessVariable.h" +#include namespace ChimeraTK { diff --git a/tests/include/CountingProcessVariableListener.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h similarity index 92% rename from tests/include/CountingProcessVariableListener.h rename to tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h index 525146d..e977a2f 100644 --- a/tests/include/CountingProcessVariableListener.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H -#include +#include namespace ChimeraTK { diff --git a/tests/include/CountingTimeStampSource.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingTimeStampSource.h similarity index 92% rename from tests/include/CountingTimeStampSource.h rename to tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingTimeStampSource.h index 855229f..58ee324 100644 --- a/tests/include/CountingTimeStampSource.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingTimeStampSource.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_TIME_STAMP_SOURCE_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_TIME_STAMP_SOURCE_H -#include +#include namespace ChimeraTK { diff --git a/tests/include/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h similarity index 96% rename from tests/include/IndependentTestCore.h rename to tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h index 0ad73df..ce0e3a9 100644 --- a/tests/include/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h @@ -3,10 +3,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index cef92ff..cc4f5f2 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -2,8 +2,8 @@ // Only after defining the name include the unit test header. #include -#include "ControlSystemSynchronizationUtility.h" -#include "IndependentTestCore.h" +#include +#include using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testControlSystemSynchronizationUtility.cpp b/tests/src/testControlSystemSynchronizationUtility.cpp index b850541..02e70f8 100644 --- a/tests/src/testControlSystemSynchronizationUtility.cpp +++ b/tests/src/testControlSystemSynchronizationUtility.cpp @@ -9,11 +9,11 @@ #include #include -#include -#include -#include +#include +#include +#include -#include +#include using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index bc8eb73..7f1bc5d 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -9,11 +9,11 @@ #include #include -#include -#include -#include +#include +#include +#include -#include +#include using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 63e0aed..2a413ad 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -2,9 +2,9 @@ // Only after defining the name include the unit test header. #include -#include "ProcessScalarAccessor.h" -#include "ProcessArrayAccessor.h" -#include "ManualTimeStampSource.h" +#include +#include +#include using namespace ChimeraTK; diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 46bbc14..b299afe 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -8,9 +8,9 @@ #include #include -#include -#include -#include +#include +#include +#include using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 27d5123..cea685a 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -3,10 +3,10 @@ #include -#include +#include -#include -#include +#include +#include using namespace boost::unit_test_framework; diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp index dab3bf3..0296198 100644 --- a/tests/src/testProcessScalar.cpp +++ b/tests/src/testProcessScalar.cpp @@ -3,10 +3,10 @@ #include using namespace boost::unit_test_framework; -#include +#include -#include -#include +#include +#include namespace ChimeraTK { /** diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index abedf52..80861eb 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -2,8 +2,8 @@ // Only after defining the name include the unit test header. #include -#include "ControlSystemSynchronizationUtility.h" -#include "IndependentTestCore.h" +#include +#include using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testTimeStamp.cpp b/tests/src/testTimeStamp.cpp index 4a74cd4..7300f5e 100644 --- a/tests/src/testTimeStamp.cpp +++ b/tests/src/testTimeStamp.cpp @@ -6,7 +6,8 @@ using namespace boost::unit_test_framework; #include -#include "TimeStamp.h" +#include + using namespace ChimeraTK; // Create a test suite which holds all your tests. From 56b1d468bbc268fd7e50b98e2856549a1feb965f Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 12 Aug 2016 15:00:02 +0200 Subject: [PATCH 069/786] Renamed the project to ChimeraTK-ControlSystemAdapter (issue #3). Renaming the project has the effect that the libraries and configuration utility files that are created by CMake use the "ChimeraTK-" prefix. This way, there is no risk of name collissions when installing these files in a system where another "ControlSystemAdapter" library might be present. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb16dbc..da241de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 2.8.0) -project(ControlSystemAdapter) +project(ChimeraTK-ControlSystemAdapter) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 00) From 2fb926a14dd098ce4c9f4411f43dcb6c8b6960ad Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 13:32:34 +0200 Subject: [PATCH 070/786] clone from source directory when creating a debian package --- cmake/make_debian_package.sh.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index e1c02cb..b3ea221 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -44,14 +44,14 @@ cd debian_package #Note, we first clone and then checkout the tag because the old git version on #Ubuntu12.4 does not support to do it in one go. BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_@PACKAGE_FULL_LIBRARY_VERSION@ -git clone @PACKAGE_GIT_URI@ ${BUILD_DIR_NAME} +git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ git checkout @PACKAGE_TAG_VERSION@ &&\ rm -rf .git ) #Debian convention: file has to end on .orig.tar.gz tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} -#Copy the prepared debian packaging config files to the source code +#Copy the prepared debian packaging config files to the source code #directroy cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian cd ${BUILD_DIR_NAME} From 180534e58317515e2fbf423331456bd470fc490e Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 14:47:29 +0200 Subject: [PATCH 071/786] Added the new getDebianBuildVersion script and use it inside make_debian_package.sh.in to obtain the version IMPORTANT: Projects importing this commit must edit the (newly created) DebianPackaging.config file! --- cmake/DebianPackaging.config | 19 ++++++++ cmake/getDebianBuildVersion | 83 +++++++++++++++++++++++++++++++++ cmake/make_debian_package.sh.in | 10 ++-- 3 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 cmake/DebianPackaging.config create mode 100755 cmake/getDebianBuildVersion diff --git a/cmake/DebianPackaging.config b/cmake/DebianPackaging.config new file mode 100644 index 0000000..728f006 --- /dev/null +++ b/cmake/DebianPackaging.config @@ -0,0 +1,19 @@ +######################################################################################## +# Project-specific configuration for the Debian packaging scripts # +# # +# Please modify this file for your needs by copying a line, uncommenting it and then # +# modifying the uncommented copy. Keep the commented original copy to avoid conflicts! # +######################################################################################## + +# URI of the git repository containing the build version numbers +#DEBIAN_BUILD_VERSIONS_REPO=git@github.com:ChimeraTK/DebianBuildVersions.git + +######################################################################################## +# NO USER CONFIGURATION BELOW THIS POINT! DO NOT MODIFY OUTSIDE THE project-template # +######################################################################################## +ERROR_MESSAGE="DebianPackaging.config is not properly updated for this package!" +if [ -z "$DEBIAN_BUILD_VERSIONS_REPO" ]; then + echo ${ERROR_MESSAGE} + exit 1 +fi +unset ERROR_MESSAGE diff --git a/cmake/getDebianBuildVersion b/cmake/getDebianBuildVersion new file mode 100755 index 0000000..ed030d3 --- /dev/null +++ b/cmake/getDebianBuildVersion @@ -0,0 +1,83 @@ +#!/bin/bash + +# obtain configuration +source "$(dirname \"$0\")/DebianPackaging.config" + +# check minimum number of command line parameters +if [ $# -lt 3 ]; then + echo "Usage:" + echo "./getDebianBuildVersion [] [] [...]" + echo "" + echo "Example:" + echo "./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1" + echo "" + echo "Important note:" + echo "If you change the order of dependencies passed to this script, the build version will be unnecessarily increased!" + exit 1 +fi + +# parse mandatory command line parameters +PACKAGE_NAME="$1" +PACKAGE_VERSION="$2" +DEBIAN_CODENAME="$3" +shift ; shift ; shift + +# build sub-directory name for dependency versions (will start and end with a /) +DEPENDENCY_STRING="/" +while (( "$#" )); do + DEPENDENCY_STRING="${DEPENDENCY_STRING}${1}/" + shift +done + +# output information extracted from command line: +echo "PACKAGE_NAME=${PACKAGE_NAME}" >&2 +echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >&2 +echo "DEBIAN_CODENAME=${DEBIAN_CODENAME}" >&2 +echo "DEPENDENCY_STRING=${DEPENDENCY_STRING}" >&2 + +# create temporary directory for checking out the DebianBuildVersions repository and modifying it +WDIR=`mktemp --tmpdir -d DebianBuildVersions.XXXXXXXX` +echo Temporary directory to work in: $WDIR >&2 +echo Git repository containing the build versions: $DEBIAN_BUILD_VERSIONS_REPO >&2 + +# check out the DebianBuildVersions repository +cd $WDIR || exit 1 +git clone "$DEBIAN_BUILD_VERSIONS_REPO" . >&2 || exit 1 + +# go to the directory for the specific package version and debian codename (create it if not yet existing) +mkdir -p "${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}" +cd "${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}" + +# if LATEST_BUILD file is not there, this is the first build of this package version on this debian codename +if [ ! -f "LATEST_BUILD" ]; then + echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has never been build on ${DEBIAN_CODENAME} before." >&2 + # start with build number 1 and create the necessary control structure + BUILD_NUMBER=1 + echo ".${DEPENDENCY_STRING}BUILD_NUMBER" > LATEST_BUILD + mkdir -p ".${DEPENDENCY_STRING}" + echo ${BUILD_NUMBER} > ".${DEPENDENCY_STRING}BUILD_NUMBER" +else + # if the file BUILD_NUMBER exists in the dependency sub-directory, this is a patch build and we re-use the build number + if [ -f ".${DEPENDENCY_STRING}BUILD_NUMBER" ]; then + echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has been build on ${DEBIAN_CODENAME} before against the SAME dependency versions." >&2 + BUILD_NUMBER=`cat ".${DEPENDENCY_STRING}BUILD_NUMBER"` + else + # no build number has been assigned yet, so we have to increase the build number from the latest build + echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has been build on ${DEBIAN_CODENAME} before against DIFFERENT dependency versions." >&2 + BUILD_NUMBER=$(( `cat $(cat LATEST_BUILD)` + 1 )) + echo ".${DEPENDENCY_STRING}BUILD_NUMBER" > LATEST_BUILD + mkdir -p ".${DEPENDENCY_STRING}" + echo ${BUILD_NUMBER} > ".${DEPENDENCY_STRING}BUILD_NUMBER" + fi +fi + +# commit the changes to the repository +git add -A . >&2 || exit 1 +git commit -m "Update by getDebianBuildVersion: ${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}${DEPENDENCY_STRING} -> ${BUILD_NUMBER}" >&2 +git push >&2 || exit 1 + +# output the build number to stderr in human readable form and to stdout in plain form for further script processing +echo Build number: ${BUILD_NUMBER} >&2 +echo ${BUILD_NUMBER} + +rm -rf $WDIR diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index b3ea221..347d365 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -15,12 +15,9 @@ ####################################################################################################################### # This cmake.in file requires the following variables to be set: -# PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME Environment variable which contains the build version. -# Must not contain any hyphens. # PACKAGE_BASE_NAME This is the debian package name, not the project name (e.g. libraries must start with lib, -# all lover case, no underscore) +# all lower case, no underscore) # PACKAGE_FULL_LIBRARY_VERSION The full version, incl. build version and patch version -# PACKAGE_GIT_URI The URI where to check out the source code # PACKAGE_TAG_VERSION The tag for which the package is build # PACKAGE_MESSAGE The message for the debian package log @@ -59,11 +56,14 @@ cd ${BUILD_DIR_NAME} #The package versions for doocs / Ubuntu contain the codename of the distribution. Get it from the system. CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` +#Obtain build version +BUILD_VERSION=`"$(dirname \"$0\")/getDebianBuildVersion"` || exit 1 + #Before building the package we will update the changelog. This is easier from a shell script #because debchange does the right format and the current date, user name and email automatically for us. #Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is # user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). -debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_FULL_LIBRARY_VERSION@-${@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@} --distribution ${CODENAME} @PACKAGE_MESSAGE@ +debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_FULL_LIBRARY_VERSION@-${BUILD_VERSION} --distribution ${CODENAME} @PACKAGE_MESSAGE@ #Now everything is prepared and we can actually build the package. #If you have a gpg signature you can remove the -us and -uc flags and sign the package. From 6e53661b7446166aa3c1f33e681d9a1bd4c442d6 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 14:54:09 +0200 Subject: [PATCH 072/786] fixed taking getDebianBuildVersion from the wrong directory --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 347d365..8123798 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -57,7 +57,7 @@ cd ${BUILD_DIR_NAME} CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` #Obtain build version -BUILD_VERSION=`"$(dirname \"$0\")/getDebianBuildVersion"` || exit 1 +BUILD_VERSION=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion"` || exit 1 #Before building the package we will update the changelog. This is easier from a shell script #because debchange does the right format and the current date, user name and email automatically for us. From 42273f00142cd868ed08bd533e2dce25fbcf11ff Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 14:56:18 +0200 Subject: [PATCH 073/786] fixed wrong quotes --- cmake/getDebianBuildVersion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/getDebianBuildVersion b/cmake/getDebianBuildVersion index ed030d3..a5c561b 100755 --- a/cmake/getDebianBuildVersion +++ b/cmake/getDebianBuildVersion @@ -1,7 +1,7 @@ #!/bin/bash # obtain configuration -source "$(dirname \"$0\")/DebianPackaging.config" +source "$(dirname $0)/DebianPackaging.config" # check minimum number of command line parameters if [ $# -lt 3 ]; then From 0d5c990e82fa3095e57563b72034b9169974161a Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 15:44:28 +0200 Subject: [PATCH 074/786] fixed not using getDebianBuildVersion correctly --- cmake/make_debian_package.sh.in | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 8123798..c3c8487 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -19,15 +19,15 @@ # all lower case, no underscore) # PACKAGE_FULL_LIBRARY_VERSION The full version, incl. build version and patch version # PACKAGE_TAG_VERSION The tag for which the package is build +# PACKAGE_MAJORMINOR_VERSION The library version without the patch level (i.e. just "MAJOR.MINOR") # PACKAGE_MESSAGE The message for the debian package log +# PACKAGE_DEPENDENCY_VERSIONS A space-separated list of dependency names and versions, each connected by a dash, e.g.: +# "DeviceAccess-0.15 ControlSystemAdapter-0.1" +# Also the following standard cmake variables are used (should be always correctly set): +# CMAKE_SOURCE_DIR +# PROJECT_NAME -#Check that the @PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@ variable is set, otherwise reject creating a package -if [ -z "$@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@" ]; then - echo Environment variable '@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@' not set or empty. You need it to make a debian package! - exit 1 -fi - -#drop out of the script if anything goes wrong (e.g. non-existen git tag) +#drop out of the script if anything goes wrong (e.g. non-existing git tag) set -e #Create a working directory in order not to merge with the rest in the build directory @@ -57,7 +57,7 @@ cd ${BUILD_DIR_NAME} CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` #Obtain build version -BUILD_VERSION=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion"` || exit 1 +BUILD_VERSION=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 #Before building the package we will update the changelog. This is easier from a shell script #because debchange does the right format and the current date, user name and email automatically for us. From 99a9f1c0309ed5145442bef7adba7b300db1aca0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 16:36:59 +0200 Subject: [PATCH 075/786] added cmake script to set the version number cmake variables as needed by other scripts of the project-template --- cmake/set_version_numbers.cmake | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 cmake/set_version_numbers.cmake diff --git a/cmake/set_version_numbers.cmake b/cmake/set_version_numbers.cmake new file mode 100644 index 0000000..f6c1c9f --- /dev/null +++ b/cmake/set_version_numbers.cmake @@ -0,0 +1,37 @@ +####################################################################################################################### +# set_version_numbers.cmake +# +# Set the version numbers for a package in the format needed by other scripts of the project-template +# +# Expects the following input variables: +# ${PROJECT_NAME}_MAJOR_VERSION - major version number +# ${PROJECT_NAME}_MINOR_VERSION - minor version number +# ${PROJECT_NAME}_PATCH_VERSION - patch number +# +# Optional environment variables: +# $PROJECT_BUILDVERSION - additional build version number for the shared object version number (e.g. "xenial1") +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) + +set(${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}) +set(${PROJECT_NAME}_BUILDVERSION $ENV{PROJECT_BUILDVERSION}) +if( ${PROJECT_NAME}_BUILDVERSION ) + set(${PROJECT_NAME}_SOVERSION "${${PROJECT_NAME}_SOVERSION}${${PROJECT_NAME}_BUILDVERSION}") +endif( ${PROJECT_NAME}_BUILDVERSION ) + +set(${PROJECT_NAME}_FULL_LIBRARY_VERSION ${${PROJECT_NAME}_SOVERSION}.${${PROJECT_NAME}_PATCH_VERSION}) + From 69ed1262d61365b7624d7b804c1f4e4c3033604e Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 16:54:03 +0200 Subject: [PATCH 076/786] properly pass the build version to the cmake building the library inside the Debian packaging tools --- cmake/make_debian_package.sh.in | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index c3c8487..d9ea091 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -17,9 +17,9 @@ # This cmake.in file requires the following variables to be set: # PACKAGE_BASE_NAME This is the debian package name, not the project name (e.g. libraries must start with lib, # all lower case, no underscore) -# PACKAGE_FULL_LIBRARY_VERSION The full version, incl. build version and patch version # PACKAGE_TAG_VERSION The tag for which the package is build # PACKAGE_MAJORMINOR_VERSION The library version without the patch level (i.e. just "MAJOR.MINOR") +# PACKAGE_PATCH_VERSION The patch level # PACKAGE_MESSAGE The message for the debian package log # PACKAGE_DEPENDENCY_VERSIONS A space-separated list of dependency names and versions, each connected by a dash, e.g.: # "DeviceAccess-0.15 ControlSystemAdapter-0.1" @@ -53,17 +53,18 @@ tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian cd ${BUILD_DIR_NAME} -#The package versions for doocs / Ubuntu contain the codename of the distribution. Get it from the system. +#The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` #Obtain build version -BUILD_VERSION=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 +BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 +export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" #Before building the package we will update the changelog. This is easier from a shell script #because debchange does the right format and the current date, user name and email automatically for us. #Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is # user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). -debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_FULL_LIBRARY_VERSION@-${BUILD_VERSION} --distribution ${CODENAME} @PACKAGE_MESSAGE@ +debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ --distribution ${CODENAME} @PACKAGE_MESSAGE@ #Now everything is prepared and we can actually build the package. #If you have a gpg signature you can remove the -us and -uc flags and sign the package. From cb89524b41159193afd1a2dc5716fdece49f9877 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 17:16:46 +0200 Subject: [PATCH 077/786] added add_dependency macro --- cmake/add_dependency.cmake | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 cmake/add_dependency.cmake diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake new file mode 100644 index 0000000..62df752 --- /dev/null +++ b/cmake/add_dependency.cmake @@ -0,0 +1,39 @@ +####################################################################################################################### +# add_dependency.cmake +# +# Define macro to search for a dependency and add it to our project. This works only with dependency using the +# project-template! +# +# The macro is called "add_dependency" and takes 3 arguments: +# - name of the dependency project as passed to FIND_PACKAGE (e.g. "mtca4u-deviceaccess") +# - required version as passed to FIND_PACKAGE +# - a list of components used by this project including the REQUIRED keyword etc. +# +# The macro will call FIND_PACKAGE, add the returned include directories to the system list, add the library directories +# to the list of link directories, and update the CMAKE_CXX_FLAGS and the PACKAGE_DEPENDENCY_VERSIONS variables. +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +MACRO(add_dependency dependency_project_name required_version components) + + FIND_PACKAGE(${dependency_project_name} ${required_version} COMPONENTS ${components}) + include_directories(SYSTEM ${${dependency_project_name}_INCLUDE_DIRS}) + link_directories(${${dependency_project_name}_LIBRARY_DIRS}) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}") + + set(PACKAGE_DEPENDENCY_VERSIONS "${PACKAGE_DEPENDENCY_VERSIONS} ${dependency_project_name}-${${dependency_project_name}_VERSION}") + +ENDMACRO(add_dependency) + From 092fdcc0a24daa75243bc63d5f264904f3b19df6 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 17:01:46 +0200 Subject: [PATCH 078/786] fixed wrong directory name --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index d9ea091..4dbb361 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -40,7 +40,7 @@ cd debian_package # lowecasepackagenname_package.ver.sion #Note, we first clone and then checkout the tag because the old git version on #Ubuntu12.4 does not support to do it in one go. -BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_@PACKAGE_FULL_LIBRARY_VERSION@ +BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_ @PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ git checkout @PACKAGE_TAG_VERSION@ &&\ From d10cb0b929da58391d39b88666075794e8f50f8f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 17:02:35 +0200 Subject: [PATCH 079/786] fixed a typo --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 4dbb361..553e7c4 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -40,7 +40,7 @@ cd debian_package # lowecasepackagenname_package.ver.sion #Note, we first clone and then checkout the tag because the old git version on #Ubuntu12.4 does not support to do it in one go. -BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_ @PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ +BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@" git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ git checkout @PACKAGE_TAG_VERSION@ &&\ From 86f11fc9b1fe74f669fc8db3ad0ee183d51e89b9 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 17:04:06 +0200 Subject: [PATCH 080/786] fixed wrong order of commands... need to obtain versions first --- cmake/make_debian_package.sh.in | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 553e7c4..b35790d 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -35,6 +35,13 @@ rm -rf debian_package mkdir debian_package cd debian_package +#The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. +CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` + +#Obtain build version +BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 +export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" + #Check out the correct tag from the master git repository. #The local directory name has to follow the debian convention # lowecasepackagenname_package.ver.sion @@ -53,13 +60,6 @@ tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian cd ${BUILD_DIR_NAME} -#The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. -CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` - -#Obtain build version -BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 -export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" - #Before building the package we will update the changelog. This is easier from a shell script #because debchange does the right format and the current date, user name and email automatically for us. #Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is From e7d88b8d81ad9c274c130acc3e3cc5fa2759bde1 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 17 Aug 2016 17:05:07 +0200 Subject: [PATCH 081/786] removed the patch level from the build directory name, as expected by Debian --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index b35790d..97bde20 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -47,7 +47,7 @@ export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" # lowecasepackagenname_package.ver.sion #Note, we first clone and then checkout the tag because the old git version on #Ubuntu12.4 does not support to do it in one go. -BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@" +BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}" git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ git checkout @PACKAGE_TAG_VERSION@ &&\ From d4691e97b229dfec8df58f45187edd6cf427820d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 10:22:16 +0200 Subject: [PATCH 082/786] - replace #DEBVERSION# in files ending on .template in the debian_from_template directory - replace DEBVERSION in file names in the debian_from_template directory This is needed, since the debian version is not yet known in cmake. --- cmake/make_debian_package.sh.in | 56 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 97bde20..2508ea5 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -27,45 +27,61 @@ # CMAKE_SOURCE_DIR # PROJECT_NAME -#drop out of the script if anything goes wrong (e.g. non-existing git tag) +# drop out of the script if anything goes wrong (e.g. non-existing git tag) set -e -#Create a working directory in order not to merge with the rest in the build directory +# Create a working directory in order not to merge with the rest in the build directory rm -rf debian_package mkdir debian_package cd debian_package -#The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. +# The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` -#Obtain build version +# Obtain build version and the derived debian version BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" -#Check out the correct tag from the master git repository. -#The local directory name has to follow the debian convention -# lowecasepackagenname_package.ver.sion -#Note, we first clone and then checkout the tag because the old git version on -#Ubuntu12.4 does not support to do it in one go. -BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}" +DEBVERSION="@PACKAGE_MAJOR_VERSION@-@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" + +# replace #DEBVERSION# inside the template files in the debian_from_template directory, also replace DEBVERSION in the +# file names +for file_source in debian_from_template/* ; do + file_target=`echo ${file_source} | sed -e "s|DEBVERSION|${DEBVERSION}|g" -e "s|.template$||" ` + + echo "$file_source -> $file_target" + + # perform operation, if source and target file names are not identical + if [ "$file_source" != "$file_target" ] ; then + sed "$file_source" -e "s|#DEBVERSION#|${DEBVERSION}|" > "$file_target" + fi + +done + +# Check out the correct tag from the master git repository. +# The local directory name has to follow the debian convention +# lowecasepackagenname_package.ver.sion +# Note, we first clone and then checkout the tag because the old git version on +# Ubuntu12.4 does not support to do it in one go. +BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ git checkout @PACKAGE_TAG_VERSION@ &&\ rm -rf .git ) -#Debian convention: file has to end on .orig.tar.gz +# Debian convention: file has to end on .orig.tar.gz tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} -#Copy the prepared debian packaging config files to the source code -#directroy +# Copy the prepared debian packaging config files to the source code +# directroy cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian cd ${BUILD_DIR_NAME} -#Before building the package we will update the changelog. This is easier from a shell script -#because debchange does the right format and the current date, user name and email automatically for us. -#Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is -# user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). -debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJORMINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ --distribution ${CODENAME} @PACKAGE_MESSAGE@ +# Before building the package we will update the changelog. This is easier from a shell script +# because debchange does the right format and the current date, user name and email automatically for us. +# Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is +# user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). +debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ --distribution ${CODENAME} @PACKAGE_MESSAGE@ -#Now everything is prepared and we can actually build the package. -#If you have a gpg signature you can remove the -us and -uc flags and sign the package. +# Now everything is prepared and we can actually build the package. +# If you have a gpg signature you can remove the -us and -uc flags and sign the package. dpkg-buildpackage -rfakeroot -us -uc From 5dd2a86a41170adfda7a583d0a4ac1ec169641a6 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 10:29:11 +0200 Subject: [PATCH 083/786] also replace #SOVERSION# since it also depends on the build version --- cmake/make_debian_package.sh.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 2508ea5..a76a34a 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -38,11 +38,12 @@ cd debian_package # The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` -# Obtain build version and the derived debian version +# Obtain build version and the derived so-file and debian versions BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" DEBVERSION="@PACKAGE_MAJOR_VERSION@-@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" +SOVERSION="@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@${PROJECT_BUILDVERSION}" # replace #DEBVERSION# inside the template files in the debian_from_template directory, also replace DEBVERSION in the # file names @@ -53,7 +54,7 @@ for file_source in debian_from_template/* ; do # perform operation, if source and target file names are not identical if [ "$file_source" != "$file_target" ] ; then - sed "$file_source" -e "s|#DEBVERSION#|${DEBVERSION}|" > "$file_target" + sed "$file_source" -e "s|#DEBVERSION#|${DEBVERSION}|g" -e "s|#SOVERSION#|${SOVERSION}|g" > "$file_target" fi done From 33ad0dac9be8910faca3416f3eef57ffae6872dc Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 10:34:08 +0200 Subject: [PATCH 084/786] replaced an old variable name with the new version --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index a76a34a..1b21759 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -39,7 +39,7 @@ cd debian_package CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` # Obtain build version and the derived so-file and debian versions -BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJORMINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 +BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" DEBVERSION="@PACKAGE_MAJOR_VERSION@-@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" From 9f8e1295e2cf351e009a2e5fc480b32fa9d3e007 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 10:35:14 +0200 Subject: [PATCH 085/786] fixed that the configuration for loop for replacing the DEBVERSION and SOVERSION in the debian_from_template was running on the wrong (non-existing) directory --- cmake/make_debian_package.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 1b21759..87bf0e9 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -47,7 +47,7 @@ SOVERSION="@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@${PROJECT_BUILDVERSION # replace #DEBVERSION# inside the template files in the debian_from_template directory, also replace DEBVERSION in the # file names -for file_source in debian_from_template/* ; do +for file_source in ../debian_from_template/* ; do file_target=`echo ${file_source} | sed -e "s|DEBVERSION|${DEBVERSION}|g" -e "s|.template$||" ` echo "$file_source -> $file_target" From 63b2f50b78b2d4165219feaf28596f77da170fee Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 11:02:52 +0200 Subject: [PATCH 086/786] added prepare_debian_package.cmake (currently just a copy of the version in the DOOCSwrappers library) Warning: this commit will create conflicts, but it is anyway needed to adapt the projects using this templates due to the last commits. --- cmake/prepare_debian_package.cmake | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 cmake/prepare_debian_package.cmake diff --git a/cmake/prepare_debian_package.cmake b/cmake/prepare_debian_package.cmake new file mode 100644 index 0000000..49a7054 --- /dev/null +++ b/cmake/prepare_debian_package.cmake @@ -0,0 +1,52 @@ +# Prepare the debian control files from the template. +# Basically this is setting the correct version number in most of the files + +# We need the lower-case form of the project name, since Debian expects package names to be all lower-case +string(TOLOWER "${PROJECT_NAME}" project_name) + +# Set a number of variables, mainly to avoid having the project name in the variable name (which would require running +# configure_file twice) +set(PACKAGE_BASE_NAME "lib${project_name}") +set(PACKAGE_NAME "${PACKAGE_BASE_NAME}${${PROJECT_NAME}_DEBVERSION}") +set(PACKAGE_DEV_NAME "${PACKAGE_BASE_NAME}-dev") +set(PACKAGE_DOC_NAME "${PACKAGE_BASE_NAME}-doc") +set(PACKAGE_FILES_WILDCARDS "${PACKAGE_NAME}_*.deb ${PACKAGE_DEV_NAME}_*.deb ${PACKAGE_DOC_NAME}_*.deb ${PACKAGE_BASE_NAME}_*.changes") +set(PACKAGE_PATCH_VERSION "${${PROJECT_NAME}_PATCH_VERSION}") +set(PACKAGE_TAG_VERSION "${${PROJECT_NAME}_VERSION}") +set(PACKAGE_MAJOR_VERSION "${${PROJECT_NAME}_MAJOR_VERSION}") +set(PACKAGE_MINOR_VERSION "${${PROJECT_NAME}_MINOR_VERSION}") +set(PACKAGE_MESSAGE "Debian package for the ${PROJECT_NAME} library version ${${PROJECT_NAME}_VERSION}") + +# Nothing to change, just copy +file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/compat DESTINATION debian_from_template) +file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/rules DESTINATION debian_from_template) +file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/source/format DESTINATION debian_from_template/source) +file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/${PACKAGE_BASE_NAME}DEBVERSION.install DESTINATION debian_from_template) + +# Replace cmake variables only +configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/copyright.in + debian_from_template/copyright @ONLY) + +configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/dev-${PACKAGE_BASE_NAME}.install.in + debian_from_template/dev-${PACKAGE_BASE_NAME}.install @ONLY) + +# Replace cmake variables. The #DEBVERSION# will be replaced in make_debian_package.sh, so these are still only templates +configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/control.template.in + debian_from_template/control.template @ONLY) + +configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/${PACKAGE_BASE_NAME}DEBVERSION.shlib.template.in + debian_from_template/${PACKAGE_BASE_NAME}DEBVERSION.shlib.template @ONLY) + +# Copy and configure the shell script which performs the actual building of the package +configure_file(${CMAKE_SOURCE_DIR}/cmake/make_debian_package.sh.in + make_debian_package.sh @ONLY) + +# A custom target so you can just run make debian_package +# (You could instead run make_debian_package.sh yourself, hm...) +add_custom_target(debian_package ${CMAKE_BINARY_DIR}/make_debian_package.sh + COMMENT Building debian package for tag ${${PROJECT_NAME}_SOVERSION}) + +# For convenience: Also create an install script for DESY +# The shared library package has the version number in the package name +configure_file(${CMAKE_SOURCE_DIR}/cmake/install_debian_package_at_DESY_intern.sh.in + install_debian_package_at_DESY_intern.sh @ONLY) From 9fcc9d9fd439a683809d175f573fe1db6ce3b844 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 11:11:51 +0200 Subject: [PATCH 087/786] added some documentation --- cmake/getDebianBuildVersion | 32 +++++++++++++++++++++++++++++- cmake/prepare_debian_package.cmake | 22 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/cmake/getDebianBuildVersion b/cmake/getDebianBuildVersion index a5c561b..7ea0f94 100755 --- a/cmake/getDebianBuildVersion +++ b/cmake/getDebianBuildVersion @@ -1,4 +1,33 @@ #!/bin/bash +####################################################################################################################### +# getDebianBuildVersion +# +# Determine the Debian build version from the git repository specified in the DebianPackaging.config file. +# +# Usage: +# ./getDebianBuildVersion [] \ +# [] [...] +# +# Example: +# ./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1 +# +# IMPORTANT NOTES: +# If you change the order of dependencies passed to this script, the build version will be unnecessarily increased! +# Running this script requires access to the git repository and WILL eventually MAKE COMMITS to it! +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### # obtain configuration source "$(dirname $0)/DebianPackaging.config" @@ -11,8 +40,9 @@ if [ $# -lt 3 ]; then echo "Example:" echo "./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1" echo "" - echo "Important note:" + echo "IMPORTANT NOTES:" echo "If you change the order of dependencies passed to this script, the build version will be unnecessarily increased!" + echo "Running this script requires access to the git repository and WILL eventually MAKE COMMITS to it!" exit 1 fi diff --git a/cmake/prepare_debian_package.cmake b/cmake/prepare_debian_package.cmake index 49a7054..2309cf2 100644 --- a/cmake/prepare_debian_package.cmake +++ b/cmake/prepare_debian_package.cmake @@ -1,3 +1,25 @@ +####################################################################################################################### +# prepare_debian_package.cmake +# +# Prepares all files needed to build Debian packages for this project (by running "make debian_package"). +# +# The script uses a number of common variables provided by the project-template, especially it requires that +# previously the script set_version_numbers.cmake has been included. +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + # Prepare the debian control files from the template. # Basically this is setting the correct version number in most of the files From de39b4c29dd61618576d33a881beaa27e0c343d0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 13:31:03 +0200 Subject: [PATCH 088/786] added an option to build using pbuilder --- cmake/make_debian_package.sh.in | 38 ++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 87bf0e9..de0cd61 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -35,8 +35,36 @@ rm -rf debian_package mkdir debian_package cd debian_package -# The package versions for doocs / Ubuntu contain the codenamePROJECT_BUILDVERSION of the distribution. Get it from the system. -CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` +# Parse command line argument +if [ -z "$1" ]; then + # build on host system + USE_PBUILDER=0 + CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` +else + USE_PBUILDER=1 + CODENAME=$1 +fi + +# Print warning if not using pbuilder +if [ "$USE_PBUILDER" == "0" ]; then + echo "WARNING: You are building on your host system for ${CODENAME}." + echo "" + echo "It is strongly recommended to build in a chroot environment using pbuilder. This can be simply enabled by calling the make_debian_package.sh script with the codename as command line argument, i.e.:" + echo " ./make_debian_package.sh ${CODENAME}" + echo "" + echo "This will require root permissons and a previously setup pbuilder environment, but you can build for any target on every host system with pbuilder installed." + echo "" + echo "To setup the pbuilder environment, use the following command:" + echo 'sudo pbuilder --update --distribution ${CODENAME} --override-config --components "main universe" --othermirror "deb [trusted=yes] http://doocspkgs.desy.de/pub/doocs xenial main|deb [trusted=yes] http://doocs.desy.de/pub/doocs xenial main"' + echo "" + echo -n "Continuing in 5 seconds. Press Ctrl+C to abort." + sleep 1 ; echo -n "." + sleep 1 ; echo -n "." + sleep 1 ; echo -n "." + sleep 1 ; echo -n "." + sleep 1 ; echo "." +fi + # Obtain build version and the derived so-file and debian versions BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 @@ -85,4 +113,8 @@ debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJOR_VERSION@.@PAC # Now everything is prepared and we can actually build the package. # If you have a gpg signature you can remove the -us and -uc flags and sign the package. -dpkg-buildpackage -rfakeroot -us -uc +if [ "$USE_PBUILDER" == "0" ]; then + dpkg-buildpackage -rfakeroot -us -uc +else + pdebuild -- --distribution ${CODENAME} +fi From 63f959f4e8a17bd6167f9ac7dd264a1ccc18fc2c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 Aug 2016 14:12:52 +0200 Subject: [PATCH 089/786] added commands to maintain the pbuilder environment more easily --- cmake/make_debian_package.sh.in | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index de0cd61..2342ffd 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -36,10 +36,17 @@ mkdir debian_package cd debian_package # Parse command line argument -if [ -z "$1" ]; then +if [ -z "$1" ] ; then # build on host system USE_PBUILDER=0 CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` +elif [ "$1" == "--setup" ] ; then + if [ -z "$2" ] ; then + echo "Missing parameter to --setup" + exit 1 + fi + sudo pbuilder --create --distribution $2 + exit $? else USE_PBUILDER=1 CODENAME=$1 @@ -52,10 +59,10 @@ if [ "$USE_PBUILDER" == "0" ]; then echo "It is strongly recommended to build in a chroot environment using pbuilder. This can be simply enabled by calling the make_debian_package.sh script with the codename as command line argument, i.e.:" echo " ./make_debian_package.sh ${CODENAME}" echo "" - echo "This will require root permissons and a previously setup pbuilder environment, but you can build for any target on every host system with pbuilder installed." + echo "This will require root permissons (script will ask for password via sudo) and a previously setup pbuilder environment, but you can build for any target on every host system with pbuilder installed." echo "" echo "To setup the pbuilder environment, use the following command:" - echo 'sudo pbuilder --update --distribution ${CODENAME} --override-config --components "main universe" --othermirror "deb [trusted=yes] http://doocspkgs.desy.de/pub/doocs xenial main|deb [trusted=yes] http://doocs.desy.de/pub/doocs xenial main"' + echo " ./make_debian_package.sh --setup ${CODENAME}" echo "" echo -n "Continuing in 5 seconds. Press Ctrl+C to abort." sleep 1 ; echo -n "." @@ -116,5 +123,13 @@ debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJOR_VERSION@.@PAC if [ "$USE_PBUILDER" == "0" ]; then dpkg-buildpackage -rfakeroot -us -uc else - pdebuild -- --distribution ${CODENAME} + MIRRORLIST="deb [trusted=yes] http://doocspkgs.desy.de/pub/doocs xenial main|deb [trusted=yes] http://doocs.desy.de/pub/doocs xenial main|deb [trusted=yes] file:///var/cache/pbuilder/result ./" + sudo bash -c "cd /var/cache/pbuilder/result ; apt-ftparchive packages . > Packages" || exit 1 + sudo pbuilder --update --distribution ${CODENAME} --override-config --components "main universe" \ + --othermirror "$MIRRORLIST" --buildresult "/var/cache/pbuilder/result" \ + --bindmounts "/var/cache/pbuilder/result" || exit 1 + pdebuild -- --distribution ${CODENAME} --buildresult "/var/cache/pbuilder/result" \ + --bindmounts "/var/cache/pbuilder/result" || exit 1 + echo "" + echo "You can find the resulting debian packages in this directory: /var/cache/pbuilder/result" fi From 34d4786222e8a7072d9c242cd0409c18a375dfe6 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 19 Aug 2016 13:55:17 +0200 Subject: [PATCH 090/786] removed stuff for debian packaging, since this is now handled by external scripts --- cmake/DebianPackaging.config | 19 ---- cmake/add_dependency.cmake | 4 +- cmake/getDebianBuildVersion | 113 ------------------------ cmake/make_debian_package.sh.in | 135 ----------------------------- cmake/prepare_debian_package.cmake | 74 ---------------- 5 files changed, 1 insertion(+), 344 deletions(-) delete mode 100644 cmake/DebianPackaging.config delete mode 100755 cmake/getDebianBuildVersion delete mode 100755 cmake/make_debian_package.sh.in delete mode 100644 cmake/prepare_debian_package.cmake diff --git a/cmake/DebianPackaging.config b/cmake/DebianPackaging.config deleted file mode 100644 index 728f006..0000000 --- a/cmake/DebianPackaging.config +++ /dev/null @@ -1,19 +0,0 @@ -######################################################################################## -# Project-specific configuration for the Debian packaging scripts # -# # -# Please modify this file for your needs by copying a line, uncommenting it and then # -# modifying the uncommented copy. Keep the commented original copy to avoid conflicts! # -######################################################################################## - -# URI of the git repository containing the build version numbers -#DEBIAN_BUILD_VERSIONS_REPO=git@github.com:ChimeraTK/DebianBuildVersions.git - -######################################################################################## -# NO USER CONFIGURATION BELOW THIS POINT! DO NOT MODIFY OUTSIDE THE project-template # -######################################################################################## -ERROR_MESSAGE="DebianPackaging.config is not properly updated for this package!" -if [ -z "$DEBIAN_BUILD_VERSIONS_REPO" ]; then - echo ${ERROR_MESSAGE} - exit 1 -fi -unset ERROR_MESSAGE diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index 62df752..1cd1ca0 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -10,7 +10,7 @@ # - a list of components used by this project including the REQUIRED keyword etc. # # The macro will call FIND_PACKAGE, add the returned include directories to the system list, add the library directories -# to the list of link directories, and update the CMAKE_CXX_FLAGS and the PACKAGE_DEPENDENCY_VERSIONS variables. +# to the list of link directories, and update the CMAKE_CXX_FLAGS variable. # ####################################################################################################################### @@ -33,7 +33,5 @@ MACRO(add_dependency dependency_project_name required_version components) link_directories(${${dependency_project_name}_LIBRARY_DIRS}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}") - set(PACKAGE_DEPENDENCY_VERSIONS "${PACKAGE_DEPENDENCY_VERSIONS} ${dependency_project_name}-${${dependency_project_name}_VERSION}") - ENDMACRO(add_dependency) diff --git a/cmake/getDebianBuildVersion b/cmake/getDebianBuildVersion deleted file mode 100755 index 7ea0f94..0000000 --- a/cmake/getDebianBuildVersion +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -####################################################################################################################### -# getDebianBuildVersion -# -# Determine the Debian build version from the git repository specified in the DebianPackaging.config file. -# -# Usage: -# ./getDebianBuildVersion [] \ -# [] [...] -# -# Example: -# ./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1 -# -# IMPORTANT NOTES: -# If you change the order of dependencies passed to this script, the build version will be unnecessarily increased! -# Running this script requires access to the git repository and WILL eventually MAKE COMMITS to it! -# -####################################################################################################################### - -####################################################################################################################### -# -# IMPORTANT NOTE: -# -# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from -# there. Make sure to keep the file generic, since it will be used by other projects, too. -# -# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes -# into the project-template repository immediately. -# -####################################################################################################################### - -# obtain configuration -source "$(dirname $0)/DebianPackaging.config" - -# check minimum number of command line parameters -if [ $# -lt 3 ]; then - echo "Usage:" - echo "./getDebianBuildVersion [] [] [...]" - echo "" - echo "Example:" - echo "./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1" - echo "" - echo "IMPORTANT NOTES:" - echo "If you change the order of dependencies passed to this script, the build version will be unnecessarily increased!" - echo "Running this script requires access to the git repository and WILL eventually MAKE COMMITS to it!" - exit 1 -fi - -# parse mandatory command line parameters -PACKAGE_NAME="$1" -PACKAGE_VERSION="$2" -DEBIAN_CODENAME="$3" -shift ; shift ; shift - -# build sub-directory name for dependency versions (will start and end with a /) -DEPENDENCY_STRING="/" -while (( "$#" )); do - DEPENDENCY_STRING="${DEPENDENCY_STRING}${1}/" - shift -done - -# output information extracted from command line: -echo "PACKAGE_NAME=${PACKAGE_NAME}" >&2 -echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >&2 -echo "DEBIAN_CODENAME=${DEBIAN_CODENAME}" >&2 -echo "DEPENDENCY_STRING=${DEPENDENCY_STRING}" >&2 - -# create temporary directory for checking out the DebianBuildVersions repository and modifying it -WDIR=`mktemp --tmpdir -d DebianBuildVersions.XXXXXXXX` -echo Temporary directory to work in: $WDIR >&2 -echo Git repository containing the build versions: $DEBIAN_BUILD_VERSIONS_REPO >&2 - -# check out the DebianBuildVersions repository -cd $WDIR || exit 1 -git clone "$DEBIAN_BUILD_VERSIONS_REPO" . >&2 || exit 1 - -# go to the directory for the specific package version and debian codename (create it if not yet existing) -mkdir -p "${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}" -cd "${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}" - -# if LATEST_BUILD file is not there, this is the first build of this package version on this debian codename -if [ ! -f "LATEST_BUILD" ]; then - echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has never been build on ${DEBIAN_CODENAME} before." >&2 - # start with build number 1 and create the necessary control structure - BUILD_NUMBER=1 - echo ".${DEPENDENCY_STRING}BUILD_NUMBER" > LATEST_BUILD - mkdir -p ".${DEPENDENCY_STRING}" - echo ${BUILD_NUMBER} > ".${DEPENDENCY_STRING}BUILD_NUMBER" -else - # if the file BUILD_NUMBER exists in the dependency sub-directory, this is a patch build and we re-use the build number - if [ -f ".${DEPENDENCY_STRING}BUILD_NUMBER" ]; then - echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has been build on ${DEBIAN_CODENAME} before against the SAME dependency versions." >&2 - BUILD_NUMBER=`cat ".${DEPENDENCY_STRING}BUILD_NUMBER"` - else - # no build number has been assigned yet, so we have to increase the build number from the latest build - echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has been build on ${DEBIAN_CODENAME} before against DIFFERENT dependency versions." >&2 - BUILD_NUMBER=$(( `cat $(cat LATEST_BUILD)` + 1 )) - echo ".${DEPENDENCY_STRING}BUILD_NUMBER" > LATEST_BUILD - mkdir -p ".${DEPENDENCY_STRING}" - echo ${BUILD_NUMBER} > ".${DEPENDENCY_STRING}BUILD_NUMBER" - fi -fi - -# commit the changes to the repository -git add -A . >&2 || exit 1 -git commit -m "Update by getDebianBuildVersion: ${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}${DEPENDENCY_STRING} -> ${BUILD_NUMBER}" >&2 -git push >&2 || exit 1 - -# output the build number to stderr in human readable form and to stdout in plain form for further script processing -echo Build number: ${BUILD_NUMBER} >&2 -echo ${BUILD_NUMBER} - -rm -rf $WDIR diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in deleted file mode 100755 index 2342ffd..0000000 --- a/cmake/make_debian_package.sh.in +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/bash - -# Shell script to generate a debian package from a particular tag (configured during the cmake configuration). - -####################################################################################################################### -# -# IMPORTANT NOTE: -# -# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from -# there. Make sure to keep the file generic, since it will be used by other projects, too. -# -# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes -# into the project-template repository immediately. -# -####################################################################################################################### - -# This cmake.in file requires the following variables to be set: -# PACKAGE_BASE_NAME This is the debian package name, not the project name (e.g. libraries must start with lib, -# all lower case, no underscore) -# PACKAGE_TAG_VERSION The tag for which the package is build -# PACKAGE_MAJORMINOR_VERSION The library version without the patch level (i.e. just "MAJOR.MINOR") -# PACKAGE_PATCH_VERSION The patch level -# PACKAGE_MESSAGE The message for the debian package log -# PACKAGE_DEPENDENCY_VERSIONS A space-separated list of dependency names and versions, each connected by a dash, e.g.: -# "DeviceAccess-0.15 ControlSystemAdapter-0.1" -# Also the following standard cmake variables are used (should be always correctly set): -# CMAKE_SOURCE_DIR -# PROJECT_NAME - -# drop out of the script if anything goes wrong (e.g. non-existing git tag) -set -e - -# Create a working directory in order not to merge with the rest in the build directory -rm -rf debian_package -mkdir debian_package -cd debian_package - -# Parse command line argument -if [ -z "$1" ] ; then - # build on host system - USE_PBUILDER=0 - CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` -elif [ "$1" == "--setup" ] ; then - if [ -z "$2" ] ; then - echo "Missing parameter to --setup" - exit 1 - fi - sudo pbuilder --create --distribution $2 - exit $? -else - USE_PBUILDER=1 - CODENAME=$1 -fi - -# Print warning if not using pbuilder -if [ "$USE_PBUILDER" == "0" ]; then - echo "WARNING: You are building on your host system for ${CODENAME}." - echo "" - echo "It is strongly recommended to build in a chroot environment using pbuilder. This can be simply enabled by calling the make_debian_package.sh script with the codename as command line argument, i.e.:" - echo " ./make_debian_package.sh ${CODENAME}" - echo "" - echo "This will require root permissons (script will ask for password via sudo) and a previously setup pbuilder environment, but you can build for any target on every host system with pbuilder installed." - echo "" - echo "To setup the pbuilder environment, use the following command:" - echo " ./make_debian_package.sh --setup ${CODENAME}" - echo "" - echo -n "Continuing in 5 seconds. Press Ctrl+C to abort." - sleep 1 ; echo -n "." - sleep 1 ; echo -n "." - sleep 1 ; echo -n "." - sleep 1 ; echo -n "." - sleep 1 ; echo "." -fi - - -# Obtain build version and the derived so-file and debian versions -BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 -export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" - -DEBVERSION="@PACKAGE_MAJOR_VERSION@-@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" -SOVERSION="@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@${PROJECT_BUILDVERSION}" - -# replace #DEBVERSION# inside the template files in the debian_from_template directory, also replace DEBVERSION in the -# file names -for file_source in ../debian_from_template/* ; do - file_target=`echo ${file_source} | sed -e "s|DEBVERSION|${DEBVERSION}|g" -e "s|.template$||" ` - - echo "$file_source -> $file_target" - - # perform operation, if source and target file names are not identical - if [ "$file_source" != "$file_target" ] ; then - sed "$file_source" -e "s|#DEBVERSION#|${DEBVERSION}|g" -e "s|#SOVERSION#|${SOVERSION}|g" > "$file_target" - fi - -done - -# Check out the correct tag from the master git repository. -# The local directory name has to follow the debian convention -# lowecasepackagenname_package.ver.sion -# Note, we first clone and then checkout the tag because the old git version on -# Ubuntu12.4 does not support to do it in one go. -BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" -git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} -( cd ${BUILD_DIR_NAME} &&\ - git checkout @PACKAGE_TAG_VERSION@ &&\ - rm -rf .git ) -# Debian convention: file has to end on .orig.tar.gz -tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} - -# Copy the prepared debian packaging config files to the source code -# directroy -cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian -cd ${BUILD_DIR_NAME} - -# Before building the package we will update the changelog. This is easier from a shell script -# because debchange does the right format and the current date, user name and email automatically for us. -# Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is -# user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). -debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ --distribution ${CODENAME} @PACKAGE_MESSAGE@ - -# Now everything is prepared and we can actually build the package. -# If you have a gpg signature you can remove the -us and -uc flags and sign the package. -if [ "$USE_PBUILDER" == "0" ]; then - dpkg-buildpackage -rfakeroot -us -uc -else - MIRRORLIST="deb [trusted=yes] http://doocspkgs.desy.de/pub/doocs xenial main|deb [trusted=yes] http://doocs.desy.de/pub/doocs xenial main|deb [trusted=yes] file:///var/cache/pbuilder/result ./" - sudo bash -c "cd /var/cache/pbuilder/result ; apt-ftparchive packages . > Packages" || exit 1 - sudo pbuilder --update --distribution ${CODENAME} --override-config --components "main universe" \ - --othermirror "$MIRRORLIST" --buildresult "/var/cache/pbuilder/result" \ - --bindmounts "/var/cache/pbuilder/result" || exit 1 - pdebuild -- --distribution ${CODENAME} --buildresult "/var/cache/pbuilder/result" \ - --bindmounts "/var/cache/pbuilder/result" || exit 1 - echo "" - echo "You can find the resulting debian packages in this directory: /var/cache/pbuilder/result" -fi diff --git a/cmake/prepare_debian_package.cmake b/cmake/prepare_debian_package.cmake deleted file mode 100644 index 2309cf2..0000000 --- a/cmake/prepare_debian_package.cmake +++ /dev/null @@ -1,74 +0,0 @@ -####################################################################################################################### -# prepare_debian_package.cmake -# -# Prepares all files needed to build Debian packages for this project (by running "make debian_package"). -# -# The script uses a number of common variables provided by the project-template, especially it requires that -# previously the script set_version_numbers.cmake has been included. -# -####################################################################################################################### - -####################################################################################################################### -# -# IMPORTANT NOTE: -# -# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from -# there. Make sure to keep the file generic, since it will be used by other projects, too. -# -# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes -# into the project-template repository immediately. -# -####################################################################################################################### - -# Prepare the debian control files from the template. -# Basically this is setting the correct version number in most of the files - -# We need the lower-case form of the project name, since Debian expects package names to be all lower-case -string(TOLOWER "${PROJECT_NAME}" project_name) - -# Set a number of variables, mainly to avoid having the project name in the variable name (which would require running -# configure_file twice) -set(PACKAGE_BASE_NAME "lib${project_name}") -set(PACKAGE_NAME "${PACKAGE_BASE_NAME}${${PROJECT_NAME}_DEBVERSION}") -set(PACKAGE_DEV_NAME "${PACKAGE_BASE_NAME}-dev") -set(PACKAGE_DOC_NAME "${PACKAGE_BASE_NAME}-doc") -set(PACKAGE_FILES_WILDCARDS "${PACKAGE_NAME}_*.deb ${PACKAGE_DEV_NAME}_*.deb ${PACKAGE_DOC_NAME}_*.deb ${PACKAGE_BASE_NAME}_*.changes") -set(PACKAGE_PATCH_VERSION "${${PROJECT_NAME}_PATCH_VERSION}") -set(PACKAGE_TAG_VERSION "${${PROJECT_NAME}_VERSION}") -set(PACKAGE_MAJOR_VERSION "${${PROJECT_NAME}_MAJOR_VERSION}") -set(PACKAGE_MINOR_VERSION "${${PROJECT_NAME}_MINOR_VERSION}") -set(PACKAGE_MESSAGE "Debian package for the ${PROJECT_NAME} library version ${${PROJECT_NAME}_VERSION}") - -# Nothing to change, just copy -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/compat DESTINATION debian_from_template) -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/rules DESTINATION debian_from_template) -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/source/format DESTINATION debian_from_template/source) -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/${PACKAGE_BASE_NAME}DEBVERSION.install DESTINATION debian_from_template) - -# Replace cmake variables only -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/copyright.in - debian_from_template/copyright @ONLY) - -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/dev-${PACKAGE_BASE_NAME}.install.in - debian_from_template/dev-${PACKAGE_BASE_NAME}.install @ONLY) - -# Replace cmake variables. The #DEBVERSION# will be replaced in make_debian_package.sh, so these are still only templates -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/control.template.in - debian_from_template/control.template @ONLY) - -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/${PACKAGE_BASE_NAME}DEBVERSION.shlib.template.in - debian_from_template/${PACKAGE_BASE_NAME}DEBVERSION.shlib.template @ONLY) - -# Copy and configure the shell script which performs the actual building of the package -configure_file(${CMAKE_SOURCE_DIR}/cmake/make_debian_package.sh.in - make_debian_package.sh @ONLY) - -# A custom target so you can just run make debian_package -# (You could instead run make_debian_package.sh yourself, hm...) -add_custom_target(debian_package ${CMAKE_BINARY_DIR}/make_debian_package.sh - COMMENT Building debian package for tag ${${PROJECT_NAME}_SOVERSION}) - -# For convenience: Also create an install script for DESY -# The shared library package has the version number in the package name -configure_file(${CMAKE_SOURCE_DIR}/cmake/install_debian_package_at_DESY_intern.sh.in - install_debian_package_at_DESY_intern.sh @ONLY) From fe2ae18ba98780c626158f299548e56b88f2ddc4 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 29 Aug 2016 19:51:13 +0200 Subject: [PATCH 091/786] using TransferElement instead of ProcessVariable (currently with typedef) --- CMakeLists.txt | 6 +- .../ControlSystemAdapter/ProcessArray.h | 1 + .../ControlSystemAdapter/ProcessVariable.h | 116 +----------------- .../ProcessVariableListener.h | 2 +- .../ControlSystemAdapter/TimeStamp.h | 72 +---------- 5 files changed, 13 insertions(+), 184 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index da241de..b93ad99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,10 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) message("Boost unit_test_framework not found, disabling testing") ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) +FIND_PACKAGE(mtca4u-deviceaccess 00.17 REQUIRED) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${mtca4u-deviceaccess_CXX_FLAGS}") +include_directories(${mtca4u-deviceaccess_INCLUDE_DIRS}) + include_directories(${CMAKE_SOURCE_DIR}/include) aux_source_directory(${CMAKE_SOURCE_DIR}/src library_sources) @@ -58,7 +62,7 @@ if(TESTING_IS_ENABLED) # Linking the boost libraries and using their included counterparts at the # same time causes problems. #target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_LIBRARIES}) - target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) add_test(${executableName} ${executableName}) endforeach( testSourceFile ) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 602ee44..7cd7a2f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -17,6 +17,7 @@ #include "ProcessVariable.h" #include "ProcessVariableListener.h" +#include "TimeStampSource.h" namespace ChimeraTK { /** diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h index 82a0a5f..3f5e897 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h @@ -1,119 +1,11 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H -namespace ChimeraTK { - class ProcessVariable; -} - -#include -#include - -#include - -#include "TimeStampSource.h" - -#include "ProcessVariableDecl.h" - -namespace ChimeraTK { - - /** - * Base class for all {@link ProcessScalar}s and {@link ProcessArray}s. - * This class provides methods that are independent of the actual type or - * implementation of a process variable. - */ - class ProcessVariable: public boost::enable_shared_from_this { - public: - /** - * Type alias for a shared pointer to a process variable. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Returns the name that identifies the process variable. - */ - const std::string& getName() const; - - /** - * Returns the \c std::type_info for the value type of this process - * variable or array. This can be used to determine the type of a process - * variable at runtime. - */ - virtual const std::type_info& getValueType() const =0; - - /** - * Returns true if this object is an instance of - * {@link ProcessArray}\ and false if this object is an - * instance of {@link ProcessScalar}\. - */ - virtual bool isArray() const =0; - - /** - * Returns true if this object represents the receiver in a - * sender / receiver pair, false otherwise. - */ - virtual bool isReceiver() const =0; - - /** - * Returns true if this object represents the sender in a - * sender / receiver pair, false otherwise. - */ - virtual bool isSender() const =0; - - /** - * Returns the time stamp associated with the current value of the process - * variable. Typically, this is the time when the value was updated. - */ - virtual TimeStamp getTimeStamp() const =0; - - /** - * Receives a new value from the sender if this process variable is a - * receiver. Returns true if a new value has been received and - * false if there is no new value available. - * - * Throws an exception if this process variable is not a receiver. - */ - virtual bool receive() =0; - - /** - * Sends the current value to the receiver if this process variable is a - * sender. Returns true if an empty buffer was available and - * false if no empty buffer was available and thus a previously - * sent value has been dropped in order to send the current value. - * - * Throws an exception if this process variable is not a sender. - */ - virtual bool send() =0; - - protected: - /** - * Creates a process variable with the specified name. - */ - ProcessVariable(const std::string& name = std::string()); - - /** - * Virtual destructor. The destructor is protected so that an instance - * cannot be destroyed through a pointer to this interface. - */ - virtual ~ProcessVariable(); - - private: - /** - * Identifier uniquely identifying the process variable. - */ - std::string _name; - - // Theoretically, the copy constructor and copy assignment operator should - // not be generated if a destructor is defined. However, some compilers do - // not enforce this. By making the constructor and operator private, we - // ensure that a process variable is never copied accidentally. This also - // applies to derived classes (the automatically generated implementations - // need the implementation in the parent class), so that it is sufficient - // if we do it once here. - ProcessVariable(const ProcessVariable&); - ProcessVariable& operator=(const ProcessVariable&); - - }; +#include +#include "TimeStamp.h" +namespace ChimeraTK{ + typedef mtca4u::TransferElement ProcessVariable; } #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h index 5714c70..486d842 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h @@ -3,7 +3,7 @@ #include -#include "ProcessVariableDecl.h" +#include "ProcessVariable.h" namespace ChimeraTK { diff --git a/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h b/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h index 00b57e8..8c85bbd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h +++ b/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h @@ -1,78 +1,10 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H -#include +#include namespace ChimeraTK { - - /** - * The ChimeraTK time stamp consists of the unix time stamp (number of seconds - * since 01.01.1970 00:00:00 UTC). - * The unsigned 32 bit value will have an overflow on Sun, 7 February 2106. - * If you need higher resolution than once per second, the nanoseconds field - * can be used. - * In addition two 32 bit user indices are provided, which can hold for - * instance a run and an event number. - */ - struct TimeStamp { - - /** - * Default constructor. Initializes all fields with zeros. - */ - inline TimeStamp() : - seconds(0), nanoSeconds(0), index0(0), index1(0) { - } - - /** - * The constructor can set all parameters. Only the seconds have to be - * specified, all other values default to 0. - */ - inline TimeStamp(uint32_t seconds_, uint32_t nanoSeconds_ = 0, - uint32_t index0_ = 0, uint32_t index1_ = 0) : - seconds(seconds_), nanoSeconds(nanoSeconds_), index0(index0_), index1( - index1_) { - } - - uint32_t seconds; ///< Unix time in seconds - uint32_t nanoSeconds; ///< Nano seconds should give enough resolution - uint32_t index0; ///< An index to hold a unique number, for instance an event number. - uint32_t index1; ///< Another index to hold a unique number, for instance a run number. - - inline bool operator==(TimeStamp const & right) { - return ((seconds == right.seconds) && (nanoSeconds == right.nanoSeconds) - && (index0 == right.index0) && (index1 == right.index1)); - } - - /** - * Returns the time-stamp corresponding to the current system time. - * Optionally, the two index numbers may be specified, which are simply - * passed on to the constructor. - */ - static inline TimeStamp currentTime(uint32_t index0 = 0, - uint32_t index1 = 0) { - // If we can use gettimeofday(), we prefer it because it is much more - // efficient than doing all the time conversions. -#ifdef BOOST_HAS_GETTIMEOFDAY - ::timeval now; - // We intentionally do not check the return code. Boost does not check it - // either and it should always be zero, unless our parameters are invalid. - ::gettimeofday(&now, NULL); - return TimeStamp(now.tv_sec, now.tv_usec * 1000, index0, index1); -#else // BOOST_HAS_GETTIMEOFDAY - boost::posix_time::ptime now = - boost::posix_time::microsec_clock::universal_time(); - boost::posix_time::ptime epoch = boost::posix_time::ptime( - boost::gregorian::date(1970, 01, 01)); - boost::posix_time::time_duration diff = now - epoch; - long seconds = diff.total_seconds(); - diff -= boost::posix_time::time_duration(0, 0, seconds, 0); - long nanoseconds = diff.total_nanoseconds(); - return TimeStamp(seconds, nanoseconds, index0, index1); -#endif // BOOST_HAS_GETTIMEOFDAY - } - - }; - + using mtca4u::TimeStamp; } // namespace ChimeraTK #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H From 39fa09f24606e214647a7a8ea30660ac46c43e37 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 1 Sep 2016 09:24:20 +0200 Subject: [PATCH 092/786] first version which compiles against TransferElement as ProcessVariable --- src/ProcessVariable.cc | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/ProcessVariable.cc diff --git a/src/ProcessVariable.cc b/src/ProcessVariable.cc deleted file mode 100644 index e9cd935..0000000 --- a/src/ProcessVariable.cc +++ /dev/null @@ -1,16 +0,0 @@ -#include - -namespace ChimeraTK { - - ProcessVariable::ProcessVariable(const std::string& name) : - _name(name) { - } - - ProcessVariable::~ProcessVariable() { - } - - const std::string& ProcessVariable::getName() const { - return _name; - } - -} From 9c3449c1bf510aab395280eb0da5e248a77f5e30 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 2 Sep 2016 12:03:33 +0200 Subject: [PATCH 093/786] undid the changes to use pbuilder in make_debian_package.sh. This conceptualluy does not work and will completely be changed in future. --- cmake/DebianPackaging.config | 19 ----- cmake/getDebianBuildVersion | 113 --------------------------- cmake/make_debian_package.sh.in | 131 ++++++++------------------------ 3 files changed, 33 insertions(+), 230 deletions(-) delete mode 100644 cmake/DebianPackaging.config delete mode 100755 cmake/getDebianBuildVersion diff --git a/cmake/DebianPackaging.config b/cmake/DebianPackaging.config deleted file mode 100644 index 728f006..0000000 --- a/cmake/DebianPackaging.config +++ /dev/null @@ -1,19 +0,0 @@ -######################################################################################## -# Project-specific configuration for the Debian packaging scripts # -# # -# Please modify this file for your needs by copying a line, uncommenting it and then # -# modifying the uncommented copy. Keep the commented original copy to avoid conflicts! # -######################################################################################## - -# URI of the git repository containing the build version numbers -#DEBIAN_BUILD_VERSIONS_REPO=git@github.com:ChimeraTK/DebianBuildVersions.git - -######################################################################################## -# NO USER CONFIGURATION BELOW THIS POINT! DO NOT MODIFY OUTSIDE THE project-template # -######################################################################################## -ERROR_MESSAGE="DebianPackaging.config is not properly updated for this package!" -if [ -z "$DEBIAN_BUILD_VERSIONS_REPO" ]; then - echo ${ERROR_MESSAGE} - exit 1 -fi -unset ERROR_MESSAGE diff --git a/cmake/getDebianBuildVersion b/cmake/getDebianBuildVersion deleted file mode 100755 index 7ea0f94..0000000 --- a/cmake/getDebianBuildVersion +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -####################################################################################################################### -# getDebianBuildVersion -# -# Determine the Debian build version from the git repository specified in the DebianPackaging.config file. -# -# Usage: -# ./getDebianBuildVersion [] \ -# [] [...] -# -# Example: -# ./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1 -# -# IMPORTANT NOTES: -# If you change the order of dependencies passed to this script, the build version will be unnecessarily increased! -# Running this script requires access to the git repository and WILL eventually MAKE COMMITS to it! -# -####################################################################################################################### - -####################################################################################################################### -# -# IMPORTANT NOTE: -# -# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from -# there. Make sure to keep the file generic, since it will be used by other projects, too. -# -# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes -# into the project-template repository immediately. -# -####################################################################################################################### - -# obtain configuration -source "$(dirname $0)/DebianPackaging.config" - -# check minimum number of command line parameters -if [ $# -lt 3 ]; then - echo "Usage:" - echo "./getDebianBuildVersion [] [] [...]" - echo "" - echo "Example:" - echo "./getDebianBuildVersion DeviceAccess-DoocsBackend 0.1 xenial DeviceAccess-0.14 DOOCS-18.10.5 DoocsServerTestHelper-0.1" - echo "" - echo "IMPORTANT NOTES:" - echo "If you change the order of dependencies passed to this script, the build version will be unnecessarily increased!" - echo "Running this script requires access to the git repository and WILL eventually MAKE COMMITS to it!" - exit 1 -fi - -# parse mandatory command line parameters -PACKAGE_NAME="$1" -PACKAGE_VERSION="$2" -DEBIAN_CODENAME="$3" -shift ; shift ; shift - -# build sub-directory name for dependency versions (will start and end with a /) -DEPENDENCY_STRING="/" -while (( "$#" )); do - DEPENDENCY_STRING="${DEPENDENCY_STRING}${1}/" - shift -done - -# output information extracted from command line: -echo "PACKAGE_NAME=${PACKAGE_NAME}" >&2 -echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >&2 -echo "DEBIAN_CODENAME=${DEBIAN_CODENAME}" >&2 -echo "DEPENDENCY_STRING=${DEPENDENCY_STRING}" >&2 - -# create temporary directory for checking out the DebianBuildVersions repository and modifying it -WDIR=`mktemp --tmpdir -d DebianBuildVersions.XXXXXXXX` -echo Temporary directory to work in: $WDIR >&2 -echo Git repository containing the build versions: $DEBIAN_BUILD_VERSIONS_REPO >&2 - -# check out the DebianBuildVersions repository -cd $WDIR || exit 1 -git clone "$DEBIAN_BUILD_VERSIONS_REPO" . >&2 || exit 1 - -# go to the directory for the specific package version and debian codename (create it if not yet existing) -mkdir -p "${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}" -cd "${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}" - -# if LATEST_BUILD file is not there, this is the first build of this package version on this debian codename -if [ ! -f "LATEST_BUILD" ]; then - echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has never been build on ${DEBIAN_CODENAME} before." >&2 - # start with build number 1 and create the necessary control structure - BUILD_NUMBER=1 - echo ".${DEPENDENCY_STRING}BUILD_NUMBER" > LATEST_BUILD - mkdir -p ".${DEPENDENCY_STRING}" - echo ${BUILD_NUMBER} > ".${DEPENDENCY_STRING}BUILD_NUMBER" -else - # if the file BUILD_NUMBER exists in the dependency sub-directory, this is a patch build and we re-use the build number - if [ -f ".${DEPENDENCY_STRING}BUILD_NUMBER" ]; then - echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has been build on ${DEBIAN_CODENAME} before against the SAME dependency versions." >&2 - BUILD_NUMBER=`cat ".${DEPENDENCY_STRING}BUILD_NUMBER"` - else - # no build number has been assigned yet, so we have to increase the build number from the latest build - echo "The package ${PACKAGE_NAME} in version ${PACKAGE_VERSION} has been build on ${DEBIAN_CODENAME} before against DIFFERENT dependency versions." >&2 - BUILD_NUMBER=$(( `cat $(cat LATEST_BUILD)` + 1 )) - echo ".${DEPENDENCY_STRING}BUILD_NUMBER" > LATEST_BUILD - mkdir -p ".${DEPENDENCY_STRING}" - echo ${BUILD_NUMBER} > ".${DEPENDENCY_STRING}BUILD_NUMBER" - fi -fi - -# commit the changes to the repository -git add -A . >&2 || exit 1 -git commit -m "Update by getDebianBuildVersion: ${PACKAGE_NAME}/${PACKAGE_VERSION}/${DEBIAN_CODENAME}${DEPENDENCY_STRING} -> ${BUILD_NUMBER}" >&2 -git push >&2 || exit 1 - -# output the build number to stderr in human readable form and to stdout in plain form for further script processing -echo Build number: ${BUILD_NUMBER} >&2 -echo ${BUILD_NUMBER} - -rm -rf $WDIR diff --git a/cmake/make_debian_package.sh.in b/cmake/make_debian_package.sh.in index 2342ffd..b3ea221 100755 --- a/cmake/make_debian_package.sh.in +++ b/cmake/make_debian_package.sh.in @@ -15,121 +15,56 @@ ####################################################################################################################### # This cmake.in file requires the following variables to be set: +# PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME Environment variable which contains the build version. +# Must not contain any hyphens. # PACKAGE_BASE_NAME This is the debian package name, not the project name (e.g. libraries must start with lib, -# all lower case, no underscore) +# all lover case, no underscore) +# PACKAGE_FULL_LIBRARY_VERSION The full version, incl. build version and patch version +# PACKAGE_GIT_URI The URI where to check out the source code # PACKAGE_TAG_VERSION The tag for which the package is build -# PACKAGE_MAJORMINOR_VERSION The library version without the patch level (i.e. just "MAJOR.MINOR") -# PACKAGE_PATCH_VERSION The patch level # PACKAGE_MESSAGE The message for the debian package log -# PACKAGE_DEPENDENCY_VERSIONS A space-separated list of dependency names and versions, each connected by a dash, e.g.: -# "DeviceAccess-0.15 ControlSystemAdapter-0.1" -# Also the following standard cmake variables are used (should be always correctly set): -# CMAKE_SOURCE_DIR -# PROJECT_NAME -# drop out of the script if anything goes wrong (e.g. non-existing git tag) +#Check that the @PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@ variable is set, otherwise reject creating a package +if [ -z "$@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@" ]; then + echo Environment variable '@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@' not set or empty. You need it to make a debian package! + exit 1 +fi + +#drop out of the script if anything goes wrong (e.g. non-existen git tag) set -e -# Create a working directory in order not to merge with the rest in the build directory +#Create a working directory in order not to merge with the rest in the build directory rm -rf debian_package mkdir debian_package cd debian_package -# Parse command line argument -if [ -z "$1" ] ; then - # build on host system - USE_PBUILDER=0 - CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` -elif [ "$1" == "--setup" ] ; then - if [ -z "$2" ] ; then - echo "Missing parameter to --setup" - exit 1 - fi - sudo pbuilder --create --distribution $2 - exit $? -else - USE_PBUILDER=1 - CODENAME=$1 -fi - -# Print warning if not using pbuilder -if [ "$USE_PBUILDER" == "0" ]; then - echo "WARNING: You are building on your host system for ${CODENAME}." - echo "" - echo "It is strongly recommended to build in a chroot environment using pbuilder. This can be simply enabled by calling the make_debian_package.sh script with the codename as command line argument, i.e.:" - echo " ./make_debian_package.sh ${CODENAME}" - echo "" - echo "This will require root permissons (script will ask for password via sudo) and a previously setup pbuilder environment, but you can build for any target on every host system with pbuilder installed." - echo "" - echo "To setup the pbuilder environment, use the following command:" - echo " ./make_debian_package.sh --setup ${CODENAME}" - echo "" - echo -n "Continuing in 5 seconds. Press Ctrl+C to abort." - sleep 1 ; echo -n "." - sleep 1 ; echo -n "." - sleep 1 ; echo -n "." - sleep 1 ; echo -n "." - sleep 1 ; echo "." -fi - - -# Obtain build version and the derived so-file and debian versions -BUILD_NUMBER=`"@CMAKE_SOURCE_DIR@/cmake/getDebianBuildVersion" @PROJECT_NAME@ @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@ ${CODENAME} @PACKAGE_DEPENDENCY_VERSIONS@` || exit 1 -export PROJECT_BUILDVERSION="${CODENAME}${BUILD_NUMBER}" - -DEBVERSION="@PACKAGE_MAJOR_VERSION@-@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" -SOVERSION="@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@${PROJECT_BUILDVERSION}" - -# replace #DEBVERSION# inside the template files in the debian_from_template directory, also replace DEBVERSION in the -# file names -for file_source in ../debian_from_template/* ; do - file_target=`echo ${file_source} | sed -e "s|DEBVERSION|${DEBVERSION}|g" -e "s|.template$||" ` - - echo "$file_source -> $file_target" - - # perform operation, if source and target file names are not identical - if [ "$file_source" != "$file_target" ] ; then - sed "$file_source" -e "s|#DEBVERSION#|${DEBVERSION}|g" -e "s|#SOVERSION#|${SOVERSION}|g" > "$file_target" - fi - -done - -# Check out the correct tag from the master git repository. -# The local directory name has to follow the debian convention -# lowecasepackagenname_package.ver.sion -# Note, we first clone and then checkout the tag because the old git version on -# Ubuntu12.4 does not support to do it in one go. -BUILD_DIR_NAME="@PACKAGE_BASE_NAME@_@PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}" +#Check out the correct tag from the master git repository. +#The local directory name has to follow the debian convention +# lowecasepackagenname_package.ver.sion +#Note, we first clone and then checkout the tag because the old git version on +#Ubuntu12.4 does not support to do it in one go. +BUILD_DIR_NAME=@PACKAGE_BASE_NAME@_@PACKAGE_FULL_LIBRARY_VERSION@ git clone @CMAKE_SOURCE_DIR@ ${BUILD_DIR_NAME} ( cd ${BUILD_DIR_NAME} &&\ git checkout @PACKAGE_TAG_VERSION@ &&\ rm -rf .git ) -# Debian convention: file has to end on .orig.tar.gz +#Debian convention: file has to end on .orig.tar.gz tar -czf ${BUILD_DIR_NAME}.orig.tar.gz ${BUILD_DIR_NAME} -# Copy the prepared debian packaging config files to the source code -# directroy +#Copy the prepared debian packaging config files to the source code +#directroy cp -r ../debian_from_template ${BUILD_DIR_NAME}/debian cd ${BUILD_DIR_NAME} -# Before building the package we will update the changelog. This is easier from a shell script -# because debchange does the right format and the current date, user name and email automatically for us. -# Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is -# user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). -debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_MAJOR_VERSION@.@PACKAGE_MINOR_VERSION@-${PROJECT_BUILDVERSION}-@PACKAGE_PATCH_VERSION@ --distribution ${CODENAME} @PACKAGE_MESSAGE@ +#The package versions for doocs / Ubuntu contain the codename of the distribution. Get it from the system. +CODENAME=`lsb_release -c | sed "{s/Codename:\s*//}"` -# Now everything is prepared and we can actually build the package. -# If you have a gpg signature you can remove the -us and -uc flags and sign the package. -if [ "$USE_PBUILDER" == "0" ]; then - dpkg-buildpackage -rfakeroot -us -uc -else - MIRRORLIST="deb [trusted=yes] http://doocspkgs.desy.de/pub/doocs xenial main|deb [trusted=yes] http://doocs.desy.de/pub/doocs xenial main|deb [trusted=yes] file:///var/cache/pbuilder/result ./" - sudo bash -c "cd /var/cache/pbuilder/result ; apt-ftparchive packages . > Packages" || exit 1 - sudo pbuilder --update --distribution ${CODENAME} --override-config --components "main universe" \ - --othermirror "$MIRRORLIST" --buildresult "/var/cache/pbuilder/result" \ - --bindmounts "/var/cache/pbuilder/result" || exit 1 - pdebuild -- --distribution ${CODENAME} --buildresult "/var/cache/pbuilder/result" \ - --bindmounts "/var/cache/pbuilder/result" || exit 1 - echo "" - echo "You can find the resulting debian packages in this directory: /var/cache/pbuilder/result" -fi +#Before building the package we will update the changelog. This is easier from a shell script +#because debchange does the right format and the current date, user name and email automatically for us. +#Use the NAME and EMAIL environment variables to get correct values if needed (usually the email is +# user@host instead of first.last@institute, for instance killenb@mskpcx18571.desy.de instead of martin.killenberg@desy.de). +debchange --create --package @PACKAGE_BASE_NAME@ -v @PACKAGE_FULL_LIBRARY_VERSION@-${@PACKAGE_BUILDVERSION_ENVIRONMENT_VARIABLE_NAME@} --distribution ${CODENAME} @PACKAGE_MESSAGE@ + +#Now everything is prepared and we can actually build the package. +#If you have a gpg signature you can remove the -us and -uc flags and sign the package. +dpkg-buildpackage -rfakeroot -us -uc From bfdbcdf7b2bb499d065966d7e1b06010ba0d77eb Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 12 Jul 2016 10:50:08 +0200 Subject: [PATCH 094/786] use readelf instead of readlink to extract the DOOCS version and do not strip the system-dependent part of the version (e.g. -xenial1), since it is needed to identify the version correctly. --- cmake/Modules/FindDOOCS.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 3694694..9955675 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -55,11 +55,11 @@ set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") # extract DOOCS version from librar so symlink. Note: This is platform dependent and only works # if DOOCS was installed from the Debian pagackes. Find a better version detection scheme! -execute_process(COMMAND bash -c "readlink ${DOOCS_DIR}/libDOOCSapi.so | sed -e 's/^.*libDOOCSapi.so.//' -e 's/-.*$//'" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) +execute_process(COMMAND bash -c "readelf -d ${DOOCS_DIR}/libDOOCSapi.so | grep SONAME | sed -e 's/^.*Library soname: \\[libDOOCSapi\\.so\\.//' -e 's/\\]$//'" + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) # use a macro provided by CMake to check if all the listed arguments are valid and set DOOCS_FOUND accordingly include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS REQUIRED_VARS DOOCS_DIR - VERSION_VAR DOOCS_VERSION ) - + VERSION_VAR DOOCS_VERSION ) From 64b0f87170f4827d44431cd639e14f44e6c85a33 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 2 Sep 2016 15:29:32 +0200 Subject: [PATCH 095/786] removed already deprecated prepare_debian_package.cmake, which has just been added and will unnecessarily cause conflicts in the anyway deprecated way to create debian packages --- cmake/prepare_debian_package.cmake | 74 ------------------------------ 1 file changed, 74 deletions(-) delete mode 100644 cmake/prepare_debian_package.cmake diff --git a/cmake/prepare_debian_package.cmake b/cmake/prepare_debian_package.cmake deleted file mode 100644 index 2309cf2..0000000 --- a/cmake/prepare_debian_package.cmake +++ /dev/null @@ -1,74 +0,0 @@ -####################################################################################################################### -# prepare_debian_package.cmake -# -# Prepares all files needed to build Debian packages for this project (by running "make debian_package"). -# -# The script uses a number of common variables provided by the project-template, especially it requires that -# previously the script set_version_numbers.cmake has been included. -# -####################################################################################################################### - -####################################################################################################################### -# -# IMPORTANT NOTE: -# -# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from -# there. Make sure to keep the file generic, since it will be used by other projects, too. -# -# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes -# into the project-template repository immediately. -# -####################################################################################################################### - -# Prepare the debian control files from the template. -# Basically this is setting the correct version number in most of the files - -# We need the lower-case form of the project name, since Debian expects package names to be all lower-case -string(TOLOWER "${PROJECT_NAME}" project_name) - -# Set a number of variables, mainly to avoid having the project name in the variable name (which would require running -# configure_file twice) -set(PACKAGE_BASE_NAME "lib${project_name}") -set(PACKAGE_NAME "${PACKAGE_BASE_NAME}${${PROJECT_NAME}_DEBVERSION}") -set(PACKAGE_DEV_NAME "${PACKAGE_BASE_NAME}-dev") -set(PACKAGE_DOC_NAME "${PACKAGE_BASE_NAME}-doc") -set(PACKAGE_FILES_WILDCARDS "${PACKAGE_NAME}_*.deb ${PACKAGE_DEV_NAME}_*.deb ${PACKAGE_DOC_NAME}_*.deb ${PACKAGE_BASE_NAME}_*.changes") -set(PACKAGE_PATCH_VERSION "${${PROJECT_NAME}_PATCH_VERSION}") -set(PACKAGE_TAG_VERSION "${${PROJECT_NAME}_VERSION}") -set(PACKAGE_MAJOR_VERSION "${${PROJECT_NAME}_MAJOR_VERSION}") -set(PACKAGE_MINOR_VERSION "${${PROJECT_NAME}_MINOR_VERSION}") -set(PACKAGE_MESSAGE "Debian package for the ${PROJECT_NAME} library version ${${PROJECT_NAME}_VERSION}") - -# Nothing to change, just copy -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/compat DESTINATION debian_from_template) -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/rules DESTINATION debian_from_template) -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/source/format DESTINATION debian_from_template/source) -file(COPY ${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/${PACKAGE_BASE_NAME}DEBVERSION.install DESTINATION debian_from_template) - -# Replace cmake variables only -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/copyright.in - debian_from_template/copyright @ONLY) - -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/dev-${PACKAGE_BASE_NAME}.install.in - debian_from_template/dev-${PACKAGE_BASE_NAME}.install @ONLY) - -# Replace cmake variables. The #DEBVERSION# will be replaced in make_debian_package.sh, so these are still only templates -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/control.template.in - debian_from_template/control.template @ONLY) - -configure_file(${CMAKE_SOURCE_DIR}/cmake/debian_package_templates/${PACKAGE_BASE_NAME}DEBVERSION.shlib.template.in - debian_from_template/${PACKAGE_BASE_NAME}DEBVERSION.shlib.template @ONLY) - -# Copy and configure the shell script which performs the actual building of the package -configure_file(${CMAKE_SOURCE_DIR}/cmake/make_debian_package.sh.in - make_debian_package.sh @ONLY) - -# A custom target so you can just run make debian_package -# (You could instead run make_debian_package.sh yourself, hm...) -add_custom_target(debian_package ${CMAKE_BINARY_DIR}/make_debian_package.sh - COMMENT Building debian package for tag ${${PROJECT_NAME}_SOVERSION}) - -# For convenience: Also create an install script for DESY -# The shared library package has the version number in the package name -configure_file(${CMAKE_SOURCE_DIR}/cmake/install_debian_package_at_DESY_intern.sh.in - install_debian_package_at_DESY_intern.sh @ONLY) From ba616ebf79d363d6ebe91eae7ba08d313ad3a28c Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 7 Sep 2016 13:11:52 +0200 Subject: [PATCH 096/786] - refactored to use the TransferElement interface instead of ProcessVariable (send/receive -> write/readNonBlocking etc) - Changed include bug local/system directories --- CMakeLists.txt | 9 ++- .../ControlSystemSynchronizationUtility.h | 8 +- .../DeviceSynchronizationUtility.h | 8 +- .../ControlSystemAdapter/ProcessArray.h | 33 ++++++--- .../ControlSystemAdapter/ProcessScalar.h | 46 ++++++++---- .../ProcessVariableAccessor.h | 16 ++-- src/ControlSystemPVManager.cc | 2 +- src/ControlSystemSynchronizationUtility.cc | 12 +-- src/DevicePVManager.cc | 2 +- src/DeviceSynchronizationUtility.cc | 12 +-- src/PVManager.cc | 6 +- .../Testing/CountingProcessVariableListener.h | 2 +- .../Testing/IndependentTestCore.h | 4 +- tests/src/fullStubTest.cpp | 4 +- ...estControlSystemSynchronizationUtility.cpp | 58 +++++++-------- .../src/testDeviceSynchronizationUtility.cpp | 58 +++++++-------- tests/src/testPVAccessors.cpp | 18 ++--- tests/src/testPVManager.cpp | 60 +++++++-------- tests/src/testProcessArray.cpp | 62 ++++++++-------- tests/src/testProcessScalar.cpp | 74 +++++++++---------- tests/src/testReferenceTestCoreWithTread.cpp | 4 +- 21 files changed, 266 insertions(+), 232 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b93ad99..c5d9ef0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8.0) project(ChimeraTK-ControlSystemAdapter) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 00) +set(${PROJECT_NAME}_MINOR_VERSION 02) set(${PROJECT_NAME}_PATCH_VERSION 00) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) @@ -41,9 +41,9 @@ ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) FIND_PACKAGE(mtca4u-deviceaccess 00.17 REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${mtca4u-deviceaccess_CXX_FLAGS}") -include_directories(${mtca4u-deviceaccess_INCLUDE_DIRS}) +include_directories(SYSTEM ${mtca4u-deviceaccess_INCLUDE_DIRS}) -include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_SOURCE_DIR}/include/ChimeraTK/ControlSystemAdapter) aux_source_directory(${CMAKE_SOURCE_DIR}/src library_sources) #Create the executables for automated unit testing. @@ -57,7 +57,8 @@ if(TESTING_IS_ENABLED) #NAME_WE means the base name without path and (longest) extension get_filename_component(executableName ${testSourceFile} NAME_WE) add_executable(${executableName} ${testSourceFile} ${testAuxSources}) - target_include_directories(${executableName} PRIVATE ${CMAKE_SOURCE_DIR}/tests/include) + target_include_directories(${executableName} PRIVATE ${CMAKE_SOURCE_DIR}/tests/include/ChimeraTK/ControlSystemAdapter) + target_include_directories(${executableName} SYSTEM BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/include) #no target library yet # Linking the boost libraries and using their included counterparts at the # same time causes problems. diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index 6220112..e8c287b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -142,18 +142,18 @@ namespace ChimeraTK { void ControlSystemSynchronizationUtility::receive(T processVariablesBegin, T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { - if ((*i)->receive()) { + if ((*i)->readNonBlocking()) { boost::unordered_map::iterator listenerIterator = _receiveNotificationListeners.find((*i)->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); receiveListener->notify((*i)); - while ((*i)->receive()) { + while ((*i)->readNonBlocking()) { receiveListener->notify((*i)); } } else { - while ((*i)->receive()) { + while ((*i)->readNonBlocking()) { continue; } } @@ -170,7 +170,7 @@ namespace ChimeraTK { void ControlSystemSynchronizationUtility::send(T processVariablesBegin, T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { - (*i)->send(); + (*i)->write(); } } diff --git a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h index 9547c4b..4ed683e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h @@ -141,18 +141,18 @@ namespace ChimeraTK { void DeviceSynchronizationUtility::receive(T processVariablesBegin, T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { - if ((*i)->receive()) { + if ((*i)->readNonBlocking()) { boost::unordered_map::iterator listenerIterator = _receiveNotificationListeners.find((*i)->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); receiveListener->notify((*i)); - while ((*i)->receive()) { + while ((*i)->readNonBlocking()) { receiveListener->notify((*i)); } } else { - while ((*i)->receive()) { + while ((*i)->readNonBlocking()) { continue; } } @@ -169,7 +169,7 @@ namespace ChimeraTK { void DeviceSynchronizationUtility::send(T processVariablesBegin, T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { - (*i)->send(); + (*i)->write(); } } diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 7cd7a2f..38c0cfb 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -347,18 +347,26 @@ namespace ChimeraTK { return *(((*_buffers)[_lastSentIndex]).value); } - bool isReceiver() const { + virtual bool isReadable() const { return _instanceType == RECEIVER; } - bool isSender() const { + virtual bool isWriteable() const { return _instanceType == SENDER; } + virtual bool isReadOnly() const { + return !isWriteable(); + } + TimeStamp getTimeStamp() const { return ((*_buffers)[_currentIndex]).timeStamp; } + virtual void read(){ + throw std::logic_error("Blocking read is not supported by process array."); + } + /** * Tells whether this array supports swapping. If true, the * swap method can be used to swap the vector backing this @@ -377,7 +385,7 @@ namespace ChimeraTK { } } - bool receive() { + bool readNonBlocking() { if (_instanceType != RECEIVER) { throw std::logic_error( "Receive operation is only allowed for a receiver process variable."); @@ -399,7 +407,7 @@ namespace ChimeraTK { } } - bool send() { + void write() { if (_instanceType != SENDER) { throw std::logic_error( "Send operation is only allowed for a sender process variable."); @@ -418,14 +426,11 @@ namespace ChimeraTK { _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); std::size_t nextIndex; - bool foundEmptyBuffer; if (_emptyBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = true; _fullBufferQueue->push(_currentIndex); } else { // We can still send, but we will lose older data. if (_fullBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = false; _fullBufferQueue->push(_currentIndex); } else { // It is possible, that we did not find an empty buffer but before @@ -438,7 +443,6 @@ namespace ChimeraTK { throw std::runtime_error( "Assertion that empty-buffer queue has at least one element failed."); } - foundEmptyBuffer = true; _fullBufferQueue->push(_currentIndex); } } @@ -447,7 +451,6 @@ namespace ChimeraTK { if (_sendNotificationListener) { _sendNotificationListener->notify(_receiver); } - return foundEmptyBuffer; } const std::type_info& getValueType() const { @@ -458,6 +461,18 @@ namespace ChimeraTK { return true; } + virtual bool isSameRegister(const boost::shared_ptr& e) const{ + // only true if the very instance of the transfer element is the same + return e.get() == this; + } + + virtual std::vector > getHardwareAccessingElements(){ + return { boost::enable_shared_from_this::shared_from_this() }; + } + + virtual void replaceTransferElement(boost::shared_ptr){ + // You can't replace anything here. Just do nothing. + } private: diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h b/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h index 18f5caa..1841312 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h @@ -17,6 +17,7 @@ #include "ProcessVariable.h" #include "ProcessVariableListener.h" +#include "TimeStampSource.h" namespace ChimeraTK { @@ -217,20 +218,24 @@ namespace ChimeraTK { // ----------------------------------------------- // Implementations of the derrived functions. They are documented in ProcessVariable - bool isReceiver() const { + virtual bool isReadable() const { return _instanceType == RECEIVER; } - bool isSender() const { + virtual bool isWriteable() const { return _instanceType == SENDER; } + virtual bool isReadOnly() const { + return !isWriteable(); + } + // deprecated TimeStamp getTimeStamp() const { return _timeStamp; } - bool receive() { + virtual bool readNonBlocking() { if (_instanceType != RECEIVER) { throw std::logic_error( "Receive operation is only allowed for a receiver process variable."); @@ -245,7 +250,12 @@ namespace ChimeraTK { } } - bool send() { + virtual void read(){ + throw std::logic_error("Blocking read is not supported by process scalar."); + + } + + virtual void write() { if (_instanceType != SENDER) { throw std::logic_error( "Send operation is only allowed for a sender process variable."); @@ -258,24 +268,32 @@ namespace ChimeraTK { Buffer nextBuffer; nextBuffer.timeStamp = _timeStamp; nextBuffer.value = _value; - bool foundEmptyBuffer; - if (_bufferQueue->bounded_push(nextBuffer)) { - foundEmptyBuffer = true; - } else { - // We are not interested in the old value, but we have to provided + if (!_bufferQueue->bounded_push(nextBuffer)) { + // sending failed because the queue is full. We have to pop a buffer to make space. + + // We are not interested in the old value, but we have to provide // a reference. Buffer oldBuffer; - // If we remove an element, pop returns true, otherwise the receiving - // thread just took the last element and we can continue without - // losing data. - foundEmptyBuffer = !_bufferQueue->pop(oldBuffer); + _bufferQueue->pop(oldBuffer); // Now we can be sure that the push is successful. _bufferQueue->bounded_push(nextBuffer); } if (_sendNotificationListener) { _sendNotificationListener->notify(_receiver); } - return foundEmptyBuffer; + } + + virtual bool isSameRegister(const boost::shared_ptr& e) const{ + // only true if the very instance of the transfer element is the same + return e.get() == this; + } + + virtual std::vector > getHardwareAccessingElements(){ + return { boost::enable_shared_from_this::shared_from_this() }; + } + + virtual void replaceTransferElement(boost::shared_ptr){ + // You can't replace anything here. Just do nothing. } private: diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h index e5fac23..9d8c40f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h @@ -36,16 +36,16 @@ namespace ChimeraTK { * Returns true if this object represents the receiver in a * sender / receiver pair, false otherwise. */ - virtual bool isReceiver() const{ - return _impl->isReceiver(); + virtual bool isReadable() const{ + return _impl->isReadable(); } /** * Returns true if this object represents the sender in a * sender / receiver pair, false otherwise. */ - bool isSender() const{ - return _impl->isSender(); + bool isWriteable() const{ + return _impl->isWriteable(); } /** @@ -63,8 +63,8 @@ namespace ChimeraTK { * * Throws an exception if this process variable is not a receiver. */ - bool receive(){ - return _impl->receive(); + bool readNonBlocking(){ + return _impl->readNonBlocking(); } /** @@ -75,8 +75,8 @@ namespace ChimeraTK { * * Throws an exception if this process variable is not a sender. */ - bool send(){ - return _impl->send(); + void write(){ + _impl->write(); } /** diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index 890a7b4..2e60569 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -1,4 +1,4 @@ -#include +#include "ControlSystemPVManager.h" namespace ChimeraTK { diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index baeab03..9fbab1c 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -1,6 +1,6 @@ #include -#include +#include "ControlSystemSynchronizationUtility.h" namespace ChimeraTK { @@ -29,18 +29,18 @@ namespace ChimeraTK { void ControlSystemSynchronizationUtility::receiveAll() { ProcessVariable::SharedPtr pv; while ((pv = _pvManager->nextNotification())) { - if (pv->receive()) { + if (pv->readNonBlocking()) { boost::unordered_map::iterator listenerIterator = _receiveNotificationListeners.find(pv->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); receiveListener->notify(pv); - while (pv->receive()) { + while (pv->readNonBlocking()) { receiveListener->notify(pv); } } else { - while (pv->receive()) { + while (pv->readNonBlocking()) { continue; } } @@ -53,8 +53,8 @@ namespace ChimeraTK { _pvManager->getAllProcessVariables()); for (std::vector::iterator i = processVariables.begin(); i != processVariables.end(); ++i) { - if ((*i)->isSender()) { - (*i)->send(); + if ((*i)->isWriteable()) { + (*i)->write(); } } } diff --git a/src/DevicePVManager.cc b/src/DevicePVManager.cc index 0f991c5..e4bd211 100644 --- a/src/DevicePVManager.cc +++ b/src/DevicePVManager.cc @@ -1,6 +1,6 @@ #include -#include +#include "DevicePVManager.h" namespace ChimeraTK { diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 0bc3f0f..d4dca92 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -1,4 +1,4 @@ -#include +#include "DeviceSynchronizationUtility.h" namespace ChimeraTK { @@ -27,18 +27,18 @@ namespace ChimeraTK { void DeviceSynchronizationUtility::receiveAll() { ProcessVariable::SharedPtr pv; while ((pv = _pvManager->nextNotification())) { - if (pv->receive()) { + if (pv->readNonBlocking()) { boost::unordered_map::iterator listenerIterator = _receiveNotificationListeners.find(pv->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); receiveListener->notify(pv); - while (pv->receive()) { + while (pv->readNonBlocking()) { receiveListener->notify(pv); } } else { - while (pv->receive()) { + while (pv->readNonBlocking()) { continue; } } @@ -51,8 +51,8 @@ namespace ChimeraTK { _pvManager->getAllProcessVariables()); for (std::vector::iterator i = processVariables.begin(); i != processVariables.end(); ++i) { - if ((*i)->isSender()) { - (*i)->send(); + if ((*i)->isWriteable()) { + (*i)->write(); } } } diff --git a/src/PVManager.cc b/src/PVManager.cc index 07535fa..6023abc 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -1,8 +1,8 @@ #include -#include -#include -#include +#include "PVManager.h" +#include "ControlSystemPVManager.h" +#include "DevicePVManager.h" namespace ChimeraTK { diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h index e977a2f..0b67a6c 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/CountingProcessVariableListener.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_COUNTING_PROCESS_VARIABLE_LISTENER_H -#include +#include "ProcessVariableListener.h" namespace ChimeraTK { diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h index ce0e3a9..5792a1d 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h @@ -59,11 +59,11 @@ struct TypedPVHolder{ void inputToOutput(){ fromDeviceScalar->set(*toDeviceScalar); - fromDeviceScalar->send(); + fromDeviceScalar->write(); for (size_t i = 0; i < fromDeviceArray->get().size() && i < toDeviceArray->get().size() ; ++i){ fromDeviceArray->get()[i] = toDeviceArray->get()[i]; } - fromDeviceArray->send(); + fromDeviceArray->write(); } }; diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index cc4f5f2..cc92088 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -2,8 +2,8 @@ // Only after defining the name include the unit test header. #include -#include -#include +#include "ControlSystemSynchronizationUtility.h" +#include "Testing/IndependentTestCore.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testControlSystemSynchronizationUtility.cpp b/tests/src/testControlSystemSynchronizationUtility.cpp index 02e70f8..6eafd32 100644 --- a/tests/src/testControlSystemSynchronizationUtility.cpp +++ b/tests/src/testControlSystemSynchronizationUtility.cpp @@ -9,11 +9,11 @@ #include #include -#include -#include -#include +#include "ControlSystemPVManager.h" +#include "DevicePVManager.h" +#include "ControlSystemSynchronizationUtility.h" -#include +#include "Testing/CountingProcessVariableListener.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; @@ -67,36 +67,36 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) ProcessVariable::SharedPtr singletonRawArray[1]; singletonRawArray[0] = csIntIn; *devIntIn = 42; - devIntIn->send(); + devIntIn->write(); syncUtil.receive(singletonRawArray, singletonRawArray + 1); BOOST_CHECK(*csIntIn == 42); singletonRawArray[0] = csIntOut; *csIntOut = 43; syncUtil.send(singletonRawArray, singletonRawArray + 1); - devIntOut->receive(); + devIntOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 43); std::vector singletonVector(1); singletonVector[0] = csIntIn; *devIntIn = 44; - devIntIn->send(); + devIntIn->write(); syncUtil.receive(singletonVector); BOOST_CHECK(*csIntIn == 44); singletonVector[0] = csIntOut; *csIntOut = 45; syncUtil.send(singletonVector); - devIntOut->receive(); + devIntOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 45); std::list singletonList; singletonList.push_back(csIntIn); *devIntIn = 46; - devIntIn->send(); + devIntIn->write(); syncUtil.receive(singletonList); BOOST_CHECK(*csIntIn == 46); singletonList.clear(); singletonList.push_back(csIntOut); *csIntOut = 47; syncUtil.send(singletonList); - devIntOut->receive(); + devIntOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 47); // Finally, we test with two elements. @@ -105,8 +105,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) doubleRawArray[1] = csFloatIn; *devIntIn = 48; *devFloatIn = 49.0f; - devIntIn->send(); - devFloatIn->send(); + devIntIn->write(); + devFloatIn->write(); syncUtil.receive(doubleRawArray, doubleRawArray + 2); BOOST_CHECK(*csIntIn == 48); BOOST_CHECK(*csFloatIn == 49.0f); @@ -115,8 +115,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) *csIntOut = 49; *csFloatOut = 50.0f; syncUtil.send(doubleRawArray, doubleRawArray + 2); - devIntOut->receive(); - devFloatOut->receive(); + devIntOut->readNonBlocking(); + devFloatOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 49); BOOST_CHECK(*devFloatOut == 50.0f); std::vector doubleVector(2); @@ -124,8 +124,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) doubleVector[1] = csFloatIn; *devIntIn = 51; *devFloatIn = 52.0f; - devIntIn->send(); - devFloatIn->send(); + devIntIn->write(); + devFloatIn->write(); syncUtil.receive(doubleVector); BOOST_CHECK(*csIntIn == 51); BOOST_CHECK(*csFloatIn == 52.0f); @@ -134,8 +134,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) *csIntOut = 53; *csFloatOut = 54.0f; syncUtil.send(doubleVector); - devIntOut->receive(); - devFloatOut->receive(); + devIntOut->readNonBlocking(); + devFloatOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 53); BOOST_CHECK(*devFloatOut == 54.0f); std::list doubleList; @@ -143,8 +143,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) doubleList.push_back(csFloatIn); *devIntIn = 55; *devFloatIn = 56.0f; - devIntIn->send(); - devFloatIn->send(); + devIntIn->write(); + devFloatIn->write(); syncUtil.receive(doubleList); BOOST_CHECK(*csIntIn == 55); BOOST_CHECK(*csFloatIn == 56.0f); @@ -154,8 +154,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) *csIntOut = 57; *csFloatOut = 58.0f; syncUtil.send(doubleList); - devIntOut->receive(); - devFloatOut->receive(); + devIntOut->readNonBlocking(); + devFloatOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 57); BOOST_CHECK(*devFloatOut == 58.0f); } @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) ControlSystemSynchronizationUtility syncUtil(csManager); *devIntIn = 5; - devIntIn->send(); + devIntIn->write(); syncUtil.receiveAll(); BOOST_CHECK(*csIntIn == 5); } @@ -207,8 +207,8 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) *csIntOut = 15; *csFloatOut = 16.0f; syncUtil.sendAll(); - devIntOut->receive(); - devFloatOut->receive(); + devIntOut->readNonBlocking(); + devFloatOut->readNonBlocking(); BOOST_CHECK(*devIntOut == 15); BOOST_CHECK(*devFloatOut == 16.0f); } @@ -237,14 +237,14 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) syncUtil.addReceiveNotificationListener("intIn", receiveNotificationListener); - devIntIn->send(); - devFloatIn->send(); + devIntIn->write(); + devFloatIn->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 1); BOOST_CHECK( receiveNotificationListener->lastProcessVariable->getName() == "intIn"); - devIntIn->send(); + devIntIn->write(); std::vector pvList(1); pvList[0] = csIntIn; syncUtil.receive(pvList); @@ -253,7 +253,7 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) receiveNotificationListener->lastProcessVariable->getName() == "intIn"); syncUtil.removeReceiveNotificationListener("intIn"); - devIntIn->send(); + devIntIn->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 2); } diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index 7f1bc5d..8e9446a 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -9,11 +9,11 @@ #include #include -#include -#include -#include +#include "ControlSystemPVManager.h" +#include "DevicePVManager.h" +#include "DeviceSynchronizationUtility.h" -#include +#include "Testing/CountingProcessVariableListener.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; @@ -67,36 +67,36 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) ProcessVariable::SharedPtr singletonRawArray[1]; singletonRawArray[0] = devIntOut; *csIntOut = 42; - csIntOut->send(); + csIntOut->write(); syncUtil.receive(singletonRawArray, singletonRawArray + 1); BOOST_CHECK(*devIntOut == 42); singletonRawArray[0] = devIntIn; *devIntIn = 43; syncUtil.send(singletonRawArray, singletonRawArray + 1); - csIntIn->receive(); + csIntIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 43); std::vector singletonVector(1); singletonVector[0] = devIntOut; *csIntOut = 44; - csIntOut->send(); + csIntOut->write(); syncUtil.receive(singletonVector); BOOST_CHECK(*devIntOut == 44); singletonVector[0] = devIntIn; *devIntIn = 45; syncUtil.send(singletonVector); - csIntIn->receive(); + csIntIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 45); std::list singletonList; singletonList.push_back(devIntOut); *csIntOut = 46; - csIntOut->send(); + csIntOut->write(); syncUtil.receive(singletonList); BOOST_CHECK(*devIntOut == 46); singletonList.clear(); singletonList.push_back(devIntIn); *devIntIn = 47; syncUtil.send(singletonList); - csIntIn->receive(); + csIntIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 47); // Finally, we test with two elements. @@ -105,8 +105,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) doubleRawArray[1] = devFloatOut; *csIntOut = 48; *csFloatOut = 49.0f; - csIntOut->send(); - csFloatOut->send(); + csIntOut->write(); + csFloatOut->write(); syncUtil.receive(doubleRawArray, doubleRawArray + 2); BOOST_CHECK(*devIntOut == 48); BOOST_CHECK(*devFloatOut == 49.0f); @@ -115,8 +115,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) *devIntIn = 49; *devFloatIn = 50.0f; syncUtil.send(doubleRawArray, doubleRawArray + 2); - csIntIn->receive(); - csFloatIn->receive(); + csIntIn->readNonBlocking(); + csFloatIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 49); BOOST_CHECK(*csFloatIn == 50.0f); std::vector doubleVector(2); @@ -124,8 +124,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) doubleVector[1] = devFloatOut; *csIntOut = 51; *csFloatOut = 52.0f; - csIntOut->send(); - csFloatOut->send(); + csIntOut->write(); + csFloatOut->write(); syncUtil.receive(doubleVector); BOOST_CHECK(*devIntOut == 51); BOOST_CHECK(*devFloatOut == 52.0f); @@ -134,8 +134,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) *devIntIn = 53; *devFloatIn = 54.0f; syncUtil.send(doubleVector); - csIntIn->receive(); - csFloatIn->receive(); + csIntIn->readNonBlocking(); + csFloatIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 53); BOOST_CHECK(*csFloatIn == 54.0f); std::list doubleList; @@ -143,8 +143,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) doubleList.push_back(devFloatOut); *csIntOut = 55; *csFloatOut = 56.0f; - csIntOut->send(); - csFloatOut->send(); + csIntOut->write(); + csFloatOut->write(); syncUtil.receive(doubleList); BOOST_CHECK(*devIntOut == 55); BOOST_CHECK(*devFloatOut == 56.0f); @@ -154,8 +154,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) *devIntIn = 57; *devFloatIn = 58.0f; syncUtil.send(doubleList); - csIntIn->receive(); - csFloatIn->receive(); + csIntIn->readNonBlocking(); + csFloatIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 57); BOOST_CHECK(*csFloatIn == 58.0f); } @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) DeviceSynchronizationUtility syncUtil(devManager); *csIntOut = 5; - csIntOut->send(); + csIntOut->write(); syncUtil.receiveAll(); BOOST_CHECK(*devIntOut == 5); } @@ -207,8 +207,8 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) *devIntIn = 15; *devFloatIn = 16.0f; syncUtil.sendAll(); - csIntIn->receive(); - csFloatIn->receive(); + csIntIn->readNonBlocking(); + csFloatIn->readNonBlocking(); BOOST_CHECK(*csIntIn == 15); BOOST_CHECK(*csFloatIn == 16.0f); } @@ -237,15 +237,15 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) syncUtil.addReceiveNotificationListener("intOut", receiveNotificationListener); - csIntOut->send(); - csFloatOut->send(); + csIntOut->write(); + csFloatOut->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 1); BOOST_CHECK( receiveNotificationListener->lastProcessVariable->getName() == "intOut"); - csIntOut->send(); + csIntOut->write(); std::vector pvList(1); pvList[0] = devIntOut; syncUtil.receive(pvList); @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) == "intOut"); syncUtil.removeReceiveNotificationListener("intOut"); - csIntOut->send(); + csIntOut->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 2); } diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp index 2a413ad..2877eaa 100644 --- a/tests/src/testPVAccessors.cpp +++ b/tests/src/testPVAccessors.cpp @@ -2,9 +2,9 @@ // Only after defining the name include the unit test header. #include -#include -#include -#include +#include "ProcessScalarAccessor.h" +#include "ProcessArrayAccessor.h" +#include "ManualTimeStampSource.h" using namespace ChimeraTK; @@ -66,17 +66,17 @@ BOOST_AUTO_TEST_CASE( testPVAccessor ){ BOOST_CHECK( sender.getValueType() == typeid(int) ); BOOST_CHECK( sender.isArray() == false ); - BOOST_CHECK( sender.isReceiver() == false ); - BOOST_CHECK( sender.isSender() == true ); - BOOST_CHECK( receiver.isReceiver() == true ); - BOOST_CHECK( receiver.isSender() == false ); + BOOST_CHECK( sender.isReadable() == false ); + BOOST_CHECK( sender.isWriteable() == true ); + BOOST_CHECK( receiver.isReadable() == true ); + BOOST_CHECK( receiver.isWriteable() == false ); BOOST_CHECK( !(sender.getTimeStamp() == testTimeStamp) ); BOOST_CHECK( scalarReceiver != 2 ); scalarSender.set(2); - sender.send(); + sender.write(); BOOST_CHECK( sender.getTimeStamp() == testTimeStamp ); - receiver.receive(); + receiver.readNonBlocking(); BOOST_CHECK( receiver.getTimeStamp() == testTimeStamp ); BOOST_CHECK( scalarReceiver == 2 ); diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index b299afe..2661b27 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -8,9 +8,9 @@ #include #include -#include -#include -#include +#include "ControlSystemPVManager.h" +#include "DevicePVManager.h" +#include "SynchronizationDirection.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; @@ -31,7 +31,7 @@ static void receiveAll( processVariables.begin(); i != processVariables.end(); ++i) { // Receive all pending values so that we can be sure that we have the most // up-to-date value. - while ((*i)->receive()) { + while ((*i)->readNonBlocking()) { continue; } } @@ -43,7 +43,7 @@ static void receiveAll( static void sendAll(list const & processVariables) { for (list::const_iterator i = processVariables.begin(); i != processVariables.end(); ++i) { - (*i)->send(); + (*i)->write(); } } @@ -291,12 +291,12 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) while (stopDeviceThread->get() == 0) { *int32In = *int32Out; *floatArrayIn = *floatArrayOut; - int32In->send(); - floatArrayIn->send(); + int32In->write(); + floatArrayIn->write(); boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); - stopDeviceThread->receive(); - int32Out->receive(); - floatArrayOut->receive(); + stopDeviceThread->readNonBlocking(); + int32Out->readNonBlocking(); + floatArrayOut->readNonBlocking(); } } }; @@ -382,7 +382,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_CHECK(floatArrayIn->get().at(9) == 120.0f); *stopDeviceThread = 1; - stopDeviceThread->send(); + stopDeviceThread->write(); } struct TestDeviceCallable2 { @@ -406,19 +406,19 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) while (i < 50000) { int32In->set(i); ++i; - int32In->send(); + int32In->write(); } doubleIn->set(2.0); - doubleIn->send(); + doubleIn->write(); } else { // After the flood test, we send notifications in regular intervals // to ensure that the reset mechanism works (multiple notifications // can be sent if they are collected). int32In->set(55); - int32In->send(); + int32In->write(); } boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); - stopDeviceThread->receive(); + stopDeviceThread->readNonBlocking(); } } }; @@ -479,7 +479,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_CHECK(doubleNotificationCount >= 1); *stopDeviceThread = 1; - stopDeviceThread->send(); + stopDeviceThread->write(); } struct TestDeviceCallable3 { @@ -518,7 +518,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_CHECK(doubleNotificationCount >= 1); *stopControlSystemThread = 1; - stopControlSystemThread->send(); + stopControlSystemThread->write(); } }; @@ -563,19 +563,19 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) while (i < 50000) { int32Out->set(i); ++i; - int32Out->send(); + int32Out->write(); } doubleOut->set(2.0); - doubleOut->send(); + doubleOut->write(); } else { // After the flood test, we send notifications in regular intervals // to ensure that the reset mechanism works (multiple notifications // can be sent if they are collected). int32Out->set(55); - int32Out->send(); + int32Out->write(); } boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); - stopControlSystemThread->receive(); + stopControlSystemThread->readNonBlocking(); } } @@ -619,11 +619,11 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) intA->set(intA->get() + 2); intB->set(intB->get() + 2); - index0->send(); - intA->send(); - intB->send(); + index0->write(); + intA->write(); + intB->write(); boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); - stopDeviceThread->receive(); + stopDeviceThread->readNonBlocking(); } } }; @@ -697,7 +697,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) } *stopDeviceThread = 1; - stopDeviceThread->send(); + stopDeviceThread->write(); } BOOST_AUTO_TEST_CASE( automaticTimeStampMode ) { @@ -719,18 +719,18 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) // By default, the PV manager should be in automatic time-stamp mode. BOOST_CHECK(devManager->isAutomaticReferenceTimeStampMode()); - intAdev->send(); + intAdev->write(); // We sleep slightly more than a second, this should ensure that the time // changes even on systems that do not have a high precision timer. boost::this_thread::sleep_for(boost::chrono::milliseconds(1100)); - intBdev->send(); + intBdev->write(); // intB should have a time stamp that is greater than the time stamp of // intA. - intAcs->receive(); - intBcs->receive(); + intAcs->readNonBlocking(); + intBcs->readNonBlocking(); BOOST_CHECK( intAcs->getTimeStamp().seconds < intBcs->getTimeStamp().seconds); } diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index cea685a..3f1cd56 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -3,10 +3,10 @@ #include -#include +#include "ProcessArray.h" -#include -#include +#include "Testing/CountingProcessVariableListener.h" +#include "Testing/CountingTimeStampSource.h" using namespace boost::unit_test_framework; @@ -77,8 +77,8 @@ namespace ChimeraTK { BOOST_CHECK(*i == SOME_NUMBER); } BOOST_CHECK(simpleArray->get().size() == N_ELEMENTS); - BOOST_CHECK(!simpleArray->isReceiver()); - BOOST_CHECK(!simpleArray->isSender()); + BOOST_CHECK(!simpleArray->isReadable()); + BOOST_CHECK(!simpleArray->isWriteable()); // Now we test the constructor that takes a whole reference vector. std::vector referenceVector; referenceVector.push_back(0); @@ -91,8 +91,8 @@ namespace ChimeraTK { BOOST_CHECK( std::equal(simpleArray->get().begin(), simpleArray->get().end(), referenceVector.begin())); - BOOST_CHECK(!simpleArray->isReceiver()); - BOOST_CHECK(!simpleArray->isSender()); + BOOST_CHECK(!simpleArray->isReadable()); + BOOST_CHECK(!simpleArray->isWriteable()); // Now we repeat the tests but for a sender / receiver pair. We do not test // the notification listener and time-stamp source because there is no @@ -114,10 +114,10 @@ namespace ChimeraTK { BOOST_CHECK(*i == 0); } BOOST_CHECK(receiver->get().size() == N_ELEMENTS); - BOOST_CHECK(!sender->isReceiver()); - BOOST_CHECK(sender->isSender()); - BOOST_CHECK(receiver->isReceiver()); - BOOST_CHECK(!receiver->isSender()); + BOOST_CHECK(!sender->isReadable()); + BOOST_CHECK(sender->isWriteable()); + BOOST_CHECK(receiver->isReadable()); + BOOST_CHECK(!receiver->isWriteable()); senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "test", SOME_NUMBER, false, 5); sender = senderReceiver.first; @@ -250,7 +250,7 @@ namespace ChimeraTK { BOOST_CHECK_THROW(receiver->getLastSent(), std::logic_error); // Now we send and then modify the array. The getLastSent() method should // still return the old values, while get() should return the new ones. - sender->send(); + sender->write(); sender->set(std::vector(N_ELEMENTS, SOME_NUMBER + 1)); typename std::vector & v2 = sender->get(); for (typename std::vector::iterator i = v2.begin(); i != v2.end(); ++i) { @@ -353,10 +353,10 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; BOOST_CHECK(sendNotificationListener->count == 0); - sender->send(); + sender->write(); BOOST_CHECK(sendNotificationListener->count == 1); BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); - sender->send(); + sender->write(); BOOST_CHECK(sendNotificationListener->count == 2); BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); } @@ -371,14 +371,14 @@ namespace ChimeraTK { timeStampSource); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - sender->send(); - receiver->receive(); + sender->write(); + receiver->readNonBlocking(); BOOST_CHECK(receiver->getTimeStamp().index0 == 0); - sender->send(); - receiver->receive(); + sender->write(); + receiver->readNonBlocking(); BOOST_CHECK(receiver->getTimeStamp().index0 == 1); - sender->send(); - receiver->receive(); + sender->write(); + receiver->readNonBlocking(); BOOST_CHECK(receiver->getTimeStamp().index0 == 2); } @@ -392,41 +392,41 @@ namespace ChimeraTK { // If we send two values consecutively, they both should be received because // the number of buffers is two. sender->get().assign(N_ELEMENTS, SOME_NUMBER); - BOOST_CHECK(sender->send()); + sender->write(); sender->get().assign(N_ELEMENTS, SOME_NUMBER + 1); - BOOST_CHECK(sender->send()); - BOOST_CHECK(receiver->receive()); + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } // We have received all values, so no more values should be available. - BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(!receiver->readNonBlocking()); // Now we try to send three values consecutively. This should result in the // first value being dropped. sender->get().assign(N_ELEMENTS, SOME_NUMBER + 2); - BOOST_CHECK(sender->send()); + sender->write(); sender->get().assign(N_ELEMENTS, SOME_NUMBER + 3); - BOOST_CHECK(sender->send()); + sender->write(); sender->get().assign(N_ELEMENTS, SOME_NUMBER + 4); - BOOST_CHECK(!sender->send()); - BOOST_CHECK(receiver->receive()); + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 3); } - BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 4); } // We have received all values, so no more values should be available. - BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(!receiver->readNonBlocking()); } } //namespace ChimeraTK diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp index 0296198..aba5734 100644 --- a/tests/src/testProcessScalar.cpp +++ b/tests/src/testProcessScalar.cpp @@ -3,10 +3,10 @@ #include using namespace boost::unit_test_framework; -#include +#include "ProcessScalar.h" -#include -#include +#include "Testing/CountingProcessVariableListener.h" +#include "Testing/CountingTimeStampSource.h" namespace ChimeraTK { /** @@ -51,8 +51,8 @@ namespace ChimeraTK { // zero. BOOST_CHECK(simpleScalar->getName() == ""); BOOST_CHECK(simpleScalar->get() == 0); - BOOST_CHECK(!simpleScalar->isReceiver()); - BOOST_CHECK(!simpleScalar->isSender()); + BOOST_CHECK(!simpleScalar->isReadable()); + BOOST_CHECK(!simpleScalar->isWriteable()); // Now we test the constructor with non-default parameters. simpleScalar = createSimpleProcessScalar("test", 42); BOOST_CHECK(simpleScalar->getName() == "test"); @@ -70,10 +70,10 @@ namespace ChimeraTK { BOOST_CHECK(sender->get() == 0); BOOST_CHECK(receiver->getName() == ""); BOOST_CHECK(receiver->get() == 0); - BOOST_CHECK(!sender->isReceiver()); - BOOST_CHECK(sender->isSender()); - BOOST_CHECK(receiver->isReceiver()); - BOOST_CHECK(!receiver->isSender()); + BOOST_CHECK(!sender->isReadable()); + BOOST_CHECK(sender->isWriteable()); + BOOST_CHECK(receiver->isReadable()); + BOOST_CHECK(!receiver->isWriteable()); senderReceiver = createSynchronizedProcessScalar("test", 42); sender = senderReceiver.first; receiver = senderReceiver.second; @@ -139,10 +139,10 @@ namespace ChimeraTK { typename ProcessScalar::SharedPtr sender = senderReceiver.first; typename ProcessScalar::SharedPtr receiver = senderReceiver.second; BOOST_CHECK(sendNotificationListener->count == 0); - sender->send(); + sender->write(); BOOST_CHECK(sendNotificationListener->count == 1); BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); - sender->send(); + sender->write(); BOOST_CHECK(sendNotificationListener->count == 2); BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); } @@ -156,14 +156,14 @@ namespace ChimeraTK { createSynchronizedProcessScalar("", 0, 1, timeStampSource); typename ProcessScalar::SharedPtr sender = senderReceiver.first; typename ProcessScalar::SharedPtr receiver = senderReceiver.second; - sender->send(); - receiver->receive(); + sender->write(); + receiver->readNonBlocking(); BOOST_CHECK(receiver->getTimeStamp().index0 == 0); - sender->send(); - receiver->receive(); + sender->write(); + receiver->readNonBlocking(); BOOST_CHECK(receiver->getTimeStamp().index0 == 1); - sender->send(); - receiver->receive(); + sender->write(); + receiver->readNonBlocking(); BOOST_CHECK(receiver->getTimeStamp().index0 == 2); } @@ -175,19 +175,19 @@ namespace ChimeraTK { typename ProcessScalar::SharedPtr sender = senderReceiver.first; typename ProcessScalar::SharedPtr receiver = senderReceiver.second; *sender = 42; - BOOST_CHECK(sender->send()); - BOOST_CHECK(receiver->receive()); + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(*receiver == 42); // No more values should be available. - BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(!receiver->readNonBlocking()); *sender = 43; - BOOST_CHECK(sender->send()); + sender->write(); *sender = 44; - // When sending this value, we should lose the previous value. - BOOST_CHECK(!sender->send()); - BOOST_CHECK(receiver->receive()); + // We send again, and the queue will overflow, so when reading we will lose the 43 and just see 44 + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(*receiver == 44); - BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(!receiver->readNonBlocking()); // Now we create a sender/receiver pair with a slight larger queue and run // the test again to check that we can transfer more data without losing // values. @@ -195,29 +195,29 @@ namespace ChimeraTK { sender = senderReceiver.first; receiver = senderReceiver.second; *sender = 42; - BOOST_CHECK(sender->send()); + sender->write(); *sender = 43; - BOOST_CHECK(sender->send()); - BOOST_CHECK(receiver->receive()); + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(*receiver == 42); - BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(*receiver == 43); // No more values should be available. - BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(!receiver->readNonBlocking()); BOOST_CHECK(*receiver == 43); *sender = 44; - BOOST_CHECK(sender->send()); + sender->write(); *sender = 45; - BOOST_CHECK(sender->send()); + sender->write(); *sender = 46; - // When sending this value, we should lose the value we sent first. - BOOST_CHECK(!sender->send()); - BOOST_CHECK(receiver->receive()); + // When sending this value, we should lose the value we sent first (44). + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(*receiver == 45); - BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(*receiver == 46); // No more values should be available. - BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(!receiver->readNonBlocking()); BOOST_CHECK(*receiver == 46); } diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index 80861eb..a33f9ec 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -2,8 +2,8 @@ // Only after defining the name include the unit test header. #include -#include -#include +#include "ControlSystemSynchronizationUtility.h" +#include "Testing/IndependentTestCore.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; From e1140140f332dcf904f5b9533e0d5aaa850800f2 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 7 Sep 2016 13:54:58 +0200 Subject: [PATCH 097/786] the library internally is using local includes. This prevents conflicts when developing while an older version is installed on the system or a global ChimeraTK testing installation. --- CMakeLists.txt | 10 +++++++--- src/ControlSystemPVManager.cc | 2 +- src/ControlSystemSynchronizationUtility.cc | 2 +- src/DevicePVManager.cc | 2 +- src/DeviceSynchronizationUtility.cc | 2 +- src/PVManager.cc | 6 +++--- src/ProcessVariable.cc | 2 +- tests/src/fullStubTest.cpp | 2 +- tests/src/testControlSystemSynchronizationUtility.cpp | 2 +- tests/src/testDeviceSynchronizationUtility.cpp | 2 +- tests/src/testProcessArray.cpp | 4 ++-- tests/src/testProcessScalar.cpp | 4 ++-- tests/src/testReferenceTestCoreWithTread.cpp | 2 +- 13 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index da241de..7d268f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8.0) project(ChimeraTK-ControlSystemAdapter) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 00) +set(${PROJECT_NAME}_MINOR_VERSION 02) set(${PROJECT_NAME}_PATCH_VERSION 00) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) @@ -39,7 +39,8 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) message("Boost unit_test_framework not found, disabling testing") ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) -include_directories(${CMAKE_SOURCE_DIR}/include) +#Includes inside the library are local includes, so we give the full path to the include directory +include_directories(${CMAKE_SOURCE_DIR}/include/ChimeraTK/ControlSystemAdapter) aux_source_directory(${CMAKE_SOURCE_DIR}/src library_sources) #Create the executables for automated unit testing. @@ -53,7 +54,10 @@ if(TESTING_IS_ENABLED) #NAME_WE means the base name without path and (longest) extension get_filename_component(executableName ${testSourceFile} NAME_WE) add_executable(${executableName} ${testSourceFile} ${testAuxSources}) - target_include_directories(${executableName} PRIVATE ${CMAKE_SOURCE_DIR}/tests/include) + target_include_directories(${executableName} PRIVATE ${CMAKE_SOURCE_DIR}/tests/include/ChimeraTK/ControlSystemAdapter/Testing) + #The IndependentTestCore is written to work with the control system adapter as installed, so + #we add that path as system just for the tests. + target_include_directories(${executableName} SYSTEM BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/include) #no target library yet # Linking the boost libraries and using their included counterparts at the # same time causes problems. diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index 890a7b4..2e60569 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -1,4 +1,4 @@ -#include +#include "ControlSystemPVManager.h" namespace ChimeraTK { diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index baeab03..af032dc 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -1,6 +1,6 @@ #include -#include +#include "ControlSystemSynchronizationUtility.h" namespace ChimeraTK { diff --git a/src/DevicePVManager.cc b/src/DevicePVManager.cc index 0f991c5..e4bd211 100644 --- a/src/DevicePVManager.cc +++ b/src/DevicePVManager.cc @@ -1,6 +1,6 @@ #include -#include +#include "DevicePVManager.h" namespace ChimeraTK { diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 0bc3f0f..361e2d9 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -1,4 +1,4 @@ -#include +#include "DeviceSynchronizationUtility.h" namespace ChimeraTK { diff --git a/src/PVManager.cc b/src/PVManager.cc index 07535fa..6023abc 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -1,8 +1,8 @@ #include -#include -#include -#include +#include "PVManager.h" +#include "ControlSystemPVManager.h" +#include "DevicePVManager.h" namespace ChimeraTK { diff --git a/src/ProcessVariable.cc b/src/ProcessVariable.cc index e9cd935..107f956 100644 --- a/src/ProcessVariable.cc +++ b/src/ProcessVariable.cc @@ -1,4 +1,4 @@ -#include +#include "ProcessVariable.h" namespace ChimeraTK { diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index cc4f5f2..a14a870 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include "IndependentTestCore.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testControlSystemSynchronizationUtility.cpp b/tests/src/testControlSystemSynchronizationUtility.cpp index 02e70f8..667627d 100644 --- a/tests/src/testControlSystemSynchronizationUtility.cpp +++ b/tests/src/testControlSystemSynchronizationUtility.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include "CountingProcessVariableListener.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index 7f1bc5d..0bd1ffd 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include "CountingProcessVariableListener.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index cea685a..0b79f0b 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -5,8 +5,8 @@ #include -#include -#include +#include "CountingProcessVariableListener.h" +#include "CountingTimeStampSource.h" using namespace boost::unit_test_framework; diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp index 0296198..fd7dbb3 100644 --- a/tests/src/testProcessScalar.cpp +++ b/tests/src/testProcessScalar.cpp @@ -5,8 +5,8 @@ using namespace boost::unit_test_framework; #include -#include -#include +#include "CountingProcessVariableListener.h" +#include "CountingTimeStampSource.h" namespace ChimeraTK { /** diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index 80861eb..7447b96 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include "IndependentTestCore.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; From bcb65773a737d03e31ed09bd9f5d3d1701018115 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 7 Sep 2016 16:18:35 +0200 Subject: [PATCH 098/786] Made boost include directories system directories. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d268f3..08e7930 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 --coverage") #boost as such is required for shared pointers e.g. FIND_PACKAGE(Boost REQUIRED) -include_directories(${Boost_INCLUDE_DIRS}) +include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) #boost chrono is needed for measuring time intervals. FIND_PACKAGE(Boost REQUIRED COMPONENTS chrono) From 199d4b51e3b88048c591167eec596e0d1995ddbe Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 9 Sep 2016 14:29:19 +0200 Subject: [PATCH 099/786] Add version-numbers for process-variable values (issue #2). This commit introduces a version-number logic for process variables, thus making it possible to avoid overwriting newer values with older ones. This is important when changes in process variables are triggered by changes in other process variables, because these changes could otherwise result in an infinite update loop. --- .../ControlSystemAdapter/PVManager.h | 18 +- .../ControlSystemAdapter/ProcessArray.h | 246 +++++++++++++----- .../ControlSystemAdapter/ProcessScalar.h | 172 +++++++++--- .../ControlSystemAdapter/VersionNumber.h | 16 ++ .../VersionNumberSource.h | 60 +++++ src/PVManager.cc | 2 + tests/src/testProcessArray.cpp | 12 +- tests/src/testProcessScalar.cpp | 6 +- 8 files changed, 427 insertions(+), 105 deletions(-) create mode 100644 include/ChimeraTK/ControlSystemAdapter/VersionNumber.h create mode 100644 include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 1256225..0c8fb08 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -429,6 +429,14 @@ namespace ChimeraTK { }; + /** + * Version number source used for all process variables that are created by + * all process-variable managers. The source is shared by all PV managers so + * that version numbers work as expected even when data is passed between + * process variables handled by different PV managers. + */ + static VersionNumberSource::SharedPtr _versionNumberSource; + /** * Map storing the process variables. */ @@ -509,7 +517,8 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessScalar::SharedPtr> processVariables = createSynchronizedProcessScalar(processVariableName, initialValue, - numberOfBuffers, timeStampSource, sendNotificationListener); + numberOfBuffers, timeStampSource, _versionNumberSource, + sendNotificationListener); _processVariables.insert( std::make_pair(processVariableName, @@ -545,7 +554,8 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessScalar::SharedPtr> processVariables = createSynchronizedProcessScalar(processVariableName, initialValue, - numberOfBuffers, timeStampSource, sendNotificationListener); + numberOfBuffers, timeStampSource, _versionNumberSource, + sendNotificationListener); _processVariables.insert( std::make_pair(processVariableName, @@ -581,7 +591,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, - swappable, numberOfBuffers, timeStampSource, + swappable, numberOfBuffers, timeStampSource, _versionNumberSource, sendNotificationListener); _processVariables.insert( @@ -619,7 +629,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, - swappable, numberOfBuffers, timeStampSource, + swappable, numberOfBuffers, timeStampSource, _versionNumberSource, sendNotificationListener); _processVariables.insert( diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 602ee44..07fa7fb 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -17,25 +17,32 @@ #include "ProcessVariable.h" #include "ProcessVariableListener.h" +#include "TimeStampSource.h" +#include "VersionNumberSource.h" namespace ChimeraTK { /** - * Array implementation of the ProcessVariable. This implementation is used for all - * three use cases (sender, receiver, and stand-alone). + * Array implementation of the ProcessVariable. This implementation is used + * for all three use cases (sender, receiver, and stand-alone). * - * This class is not thread-safe and - * should only be used from a single thread. + * This class is not thread-safe and should only be used from a single thread. + * + * If no version number source is specified when creating an instance of this + * class, version number checks are disabled. This means that a receive + * operation will always proceed, regardless of the version number of the new + * value. The version number of the process array without a version number + * source will always stay at zero. */ template - class ProcessArray: public ProcessVariable { - + class ProcessArray: public ProcessVariable { + public: - + /** * Type alias for a shared pointer to this type. */ typedef boost::shared_ptr SharedPtr; - + /** * Type of the instance. This defines the behavior (send or receive * possible, modifications allowed, etc.). This enum type is private so @@ -48,18 +55,18 @@ namespace ChimeraTK { * Instance acts on is own. */ STAND_ALONE, - + /** * Instance acts as the sender in a sender / receiver pair. */ SENDER, - + /** * Instance acts as the receiver in a sender / receiver pair. */ RECEIVER }; - + /** * Creates a process array that works independently. This means that the * instance is not synchronized with any other instance and thus the send @@ -100,14 +107,15 @@ namespace ChimeraTK { */ ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue, std::size_t numberOfBuffers, - bool swappable) : + bool swappable, VersionNumberSource::SharedPtr versionNumberSource) : ProcessVariable(name), _instanceType(instanceType), _vectorSize( initialValue.size()), _swappable(swappable), _buffers( boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( boost::make_shared >( numberOfBuffers)), _emptyBufferQueue( boost::make_shared >( - numberOfBuffers)), _currentIndex(0), _lastSentIndex(0) { + numberOfBuffers)), _currentIndex(0), _lastSentIndex(0), _versionNumberSource( + versionNumberSource) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != RECEIVER) { @@ -136,8 +144,7 @@ namespace ChimeraTK { // move semantics). for (typename std::vector::iterator i = _buffers->begin(); i != _buffers->end(); ++i) { - boost::scoped_ptr > v( - new std::vector(initialValue)); + boost::scoped_ptr > v(new std::vector(initialValue)); i->value.swap(v); } // The buffer with the index 0 is assigned to the receiver and the @@ -166,14 +173,16 @@ namespace ChimeraTK { * variable passed to the listener is the receiver and not the sender. */ ProcessArray(InstanceType instanceType, bool swappable, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener, - boost::shared_ptr receiver) : + TimeStampSource::SharedPtr timeStampSource, + VersionNumberSource::SharedPtr versionNumberSource, + ProcessVariableListener::SharedPtr sendNotificationListener, + ProcessArray::SharedPtr receiver) : ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( receiver->_vectorSize), _swappable(swappable), _buffers( receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( - receiver), _timeStampSource(timeStampSource), _sendNotificationListener( + receiver), _timeStampSource(timeStampSource), _versionNumberSource( + versionNumberSource), _sendNotificationListener( sendNotificationListener) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. @@ -200,7 +209,7 @@ namespace ChimeraTK { * This behaves excactly like the set(...) method. */ ProcessArray & operator=(ProcessArray const & other) { - set(other.getConst()); + set(other); return (*this); } @@ -264,8 +273,7 @@ namespace ChimeraTK { */ void swap(boost::scoped_ptr > & otherVector) { if (!isSwappable()) { - throw std::logic_error( - "Swap is not supported by this process array."); + throw std::logic_error("Swap is not supported by this process array."); } if (otherVector->size() != get().size()) { throw std::runtime_error("Vector sizes do not match"); @@ -358,6 +366,56 @@ namespace ChimeraTK { return ((*_buffers)[_currentIndex]).timeStamp; } + /** + * Returns the version number that is associated with the current value. + * This is the version number that was received with the {@link receive()} + * operation that received the respective value. Typically, this will be the + * last receive operation. If this process variable has just been sent, the + * value and thus the version number is undefined (it can be any value / + * version-number combination that has been sent at an earlier point in + * time). + * + * The version number is used to resolve conflicting updates. When an update + * is received using the {@link receive()} method, it is only used if its + * value has a version number that is greater than the version number of the + * current value. Initially, each process variable has a version number of + * zero. + * + * When this process variable has not been initialized with a version number + * source, its version number always stays at zero and the version-number + * logic is disabled. + */ + VersionNumber getVersionNumber() const { + // On the other hand, this should not matter too much because the current + // value will be in an undefined state anyway and thus we might not care. + return ((*_buffers)[_currentIndex]).versionNumber; + } + + /** + * Returns the version number associated with the value that has been sent + * with the last sent operation. + * + * This method may only be used on a process variable that is a sender and + * if swapping is not allowed for the respective receiver. In all other + * cases, calling this method results in an exception. + * + * @deprecated + * This method is going to be removed together with the + * {@link getLastSent()} and {@link isSwappable()} methods. + */ + VersionNumber getLastSentVersionNumber() const { + // TODO Typically, there should be no reason to check the version number + // of the last sent value, but we still have this method for symmetry + // reasons (because we also have a getLastSent() method). + // We should really consider getting rid of both methods and allow for an + // (optional) copy when sending instead of disabling swapping. + if (_instanceType != SENDER || _swappable) { + throw std::logic_error( + "The last sent version number is only available for a sender that is associated with a receiver that does not allow swapping."); + } + return ((*_buffers)[_lastSentIndex]).versionNumber; + } + /** * Tells whether this array supports swapping. If true, the * swap method can be used to swap the vector backing this @@ -390,15 +448,58 @@ namespace ChimeraTK { } std::size_t nextIndex; if (_fullBufferQueue->pop(nextIndex)) { - _emptyBufferQueue->push(_currentIndex); - _currentIndex = nextIndex; - return true; + // We only use the incoming update if it has a higher version number + // than the current value. This check is disabled when there is no + // version number source. + if (!_versionNumberSource + || ((*_buffers)[nextIndex]).versionNumber > getVersionNumber()) { + _emptyBufferQueue->push(_currentIndex); + _currentIndex = nextIndex; + return true; + } else { + _emptyBufferQueue->push(nextIndex); + return false; + } } else { return false; } } + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * If this process variable has a version-number source, a new version + * number is retrieved from this source and used for the value being sent to + * the receiver. Otherwise, a version number of zero is used. + * + * Throws an exception if this process variable is not a sender. + */ bool send() { + VersionNumber newVersionNumber; + if (_versionNumberSource) { + newVersionNumber = _versionNumberSource->nextVersionNumber(); + } else { + newVersionNumber = 0; + } + return send(newVersionNumber); + } + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * Throws an exception if this process variable is not a sender. + */ + bool send(VersionNumber newVersionNumber) { if (_instanceType != SENDER) { throw std::logic_error( "Send operation is only allowed for a sender process variable."); @@ -416,6 +517,7 @@ namespace ChimeraTK { _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); + ((*_buffers)[_currentIndex]).versionNumber = newVersionNumber; std::size_t nextIndex; bool foundEmptyBuffer; if (_emptyBufferQueue->pop(nextIndex)) { @@ -448,15 +550,14 @@ namespace ChimeraTK { } return foundEmptyBuffer; } - + const std::type_info& getValueType() const { - return typeid(T); + return typeid(T); } - + bool isArray() const { - return true; + return true; } - private: @@ -468,12 +569,14 @@ namespace ChimeraTK { TimeStamp timeStamp; boost::scoped_ptr > value; + VersionNumber versionNumber; /** * Default constructor. Has to be defined explicitly because we have an * non-default copy constructor. */ - Buffer() { + Buffer() : + versionNumber(0) { } /** @@ -489,7 +592,8 @@ namespace ChimeraTK { */ Buffer(Buffer const & other) : timeStamp(other.timeStamp), value( - other.value ? new std::vector(*(other.value)) : 0) { + other.value ? new std::vector(*(other.value)) : 0), versionNumber( + other.versionNumber) { assert(!(other.value)); } @@ -546,6 +650,18 @@ namespace ChimeraTK { */ std::size_t _lastSentIndex; + /** + * Flag indicating whether the {@link originalVersionNumber} field stores + * a version number that should be used for the next send operation. + */ + bool _haveOriginalVersionNumber; + + /** + * Version number that shall be used for the next send operation if + * {@link haveOriginalVersionNumber} is set. + */ + VersionNumber _originalVersionNumber; + /** * Pointer to the receiver associated with this sender. This field is only * used if this process variable represents a sender. @@ -557,6 +673,12 @@ namespace ChimeraTK { */ boost::shared_ptr _timeStampSource; + /** + * Version number source used for getting the next version number when one + * of the {@code set} methods is called without specifying a version number. + */ + boost::shared_ptr _versionNumberSource; + /** * Listener that is notified when the process variable is sent. */ @@ -634,10 +756,11 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const std::string & name = "", T initialValue = 0, bool swappable = true, std::size_t numberOfBuffers = 2, - boost::shared_ptr timeStampSource = boost::shared_ptr< - TimeStampSource>(), - boost::shared_ptr sendNotificationListener = - boost::shared_ptr()); + TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + VersionNumberSource::SharedPtr versionNumberSource = + VersionNumberSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = + ProcessVariableListener::SharedPtr()); /** * Creates a synchronized process array. A synchronized process array works @@ -687,24 +810,24 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createSynchronizedProcessArray( const std::vector& initialValue, const std::string & name = "", bool swappable = true, std::size_t numberOfBuffers = 2, - boost::shared_ptr timeStampSource = boost::shared_ptr< - TimeStampSource>(), - boost::shared_ptr sendNotificationListener = - boost::shared_ptr()); + TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + VersionNumberSource::SharedPtr versionNumberSource = + VersionNumberSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = + ProcessVariableListener::SharedPtr()); template typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string & name, T initialValue) { - return boost::make_shared< ProcessArray >( - ProcessArray::STAND_ALONE, name, - std::vector(size, initialValue)); + return boost::make_shared >(ProcessArray::STAND_ALONE, + name, std::vector(size, initialValue)); } template typename ProcessArray::SharedPtr createSimpleProcessArray( const std::vector& initialValue, const std::string & name) { - return boost::make_shared< ProcessArray >( - ProcessArray::STAND_ALONE, name, initialValue); + return boost::make_shared >(ProcessArray::STAND_ALONE, + name, initialValue); } template @@ -712,15 +835,16 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const std::string & name, T initialValue, bool swappable, std::size_t numberOfBuffers, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener) { - typename boost::shared_ptr< ProcessArray > receiver = - boost::make_shared< ProcessArray >( - ProcessArray::RECEIVER, name, - std::vector(size, initialValue), numberOfBuffers, swappable); + TimeStampSource::SharedPtr timeStampSource, + VersionNumberSource::SharedPtr versionNumberSource, + ProcessVariableListener::SharedPtr sendNotificationListener) { + typename boost::shared_ptr > receiver = boost::make_shared< + ProcessArray >(ProcessArray::RECEIVER, name, + std::vector(size, initialValue), numberOfBuffers, swappable, + versionNumberSource); typename ProcessArray::SharedPtr sender = boost::make_shared< - ProcessArray >(ProcessArray::SENDER, - swappable, timeStampSource, sendNotificationListener, receiver); + ProcessArray >(ProcessArray::SENDER, swappable, timeStampSource, + versionNumberSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); } @@ -730,15 +854,15 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createSynchronizedProcessArray( const std::vector& initialValue, const std::string & name, bool swappable, std::size_t numberOfBuffers, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener) { - typename boost::shared_ptr< ProcessArray > receiver = - boost::make_shared< ProcessArray >( - ProcessArray::RECEIVER, name, initialValue, - numberOfBuffers, swappable); + TimeStampSource::SharedPtr timeStampSource, + VersionNumberSource::SharedPtr versionNumberSource, + ProcessVariableListener::SharedPtr sendNotificationListener) { + typename boost::shared_ptr > receiver = boost::make_shared< + ProcessArray >(ProcessArray::RECEIVER, name, initialValue, + numberOfBuffers, swappable, versionNumberSource); typename ProcessArray::SharedPtr sender = boost::make_shared< - ProcessArray >(ProcessArray::SENDER, - swappable, timeStampSource, sendNotificationListener, receiver); + ProcessArray >(ProcessArray::SENDER, swappable, timeStampSource, + versionNumberSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); } diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h b/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h index 18f5caa..95b5abb 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h @@ -17,6 +17,8 @@ #include "ProcessVariable.h" #include "ProcessVariableListener.h" +#include "TimeStampSource.h" +#include "VersionNumberSource.h" namespace ChimeraTK { @@ -68,7 +70,7 @@ namespace ChimeraTK { ProcessScalar(InstanceType instanceType, const std::string& name, T initialValue) : ProcessVariable(name), _instanceType(instanceType), _value( - initialValue) { + initialValue), _versionNumber(0) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != STAND_ALONE) { @@ -86,11 +88,12 @@ namespace ChimeraTK { * send/receive process and are shared with the sender. */ ProcessScalar(InstanceType instanceType, const std::string& name, - T initialValue, std::size_t numberOfBuffers) : + T initialValue, std::size_t numberOfBuffers, + VersionNumberSource::SharedPtr versionNumberSource) : ProcessVariable(name), _instanceType(instanceType), _value( - initialValue), _bufferQueue( + initialValue), _versionNumber(0), _bufferQueue( boost::make_shared >( - numberOfBuffers)) { + numberOfBuffers)), _versionNumberSource(versionNumberSource) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != RECEIVER) { @@ -121,13 +124,14 @@ namespace ChimeraTK { * variable passed to the listener is the receiver and not the sender. */ ProcessScalar(InstanceType instanceType, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener, - boost::shared_ptr receiver) : + TimeStampSource::SharedPtr timeStampSource, + VersionNumberSource::SharedPtr versionNumberSource, + ProcessVariableListener::SharedPtr sendNotificationListener, + ProcessScalar::SharedPtr receiver) : ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( - receiver->_timeStamp), _value(receiver->_value), _bufferQueue( + receiver->_timeStamp), _value(receiver->_value), _versionNumber(0), _bufferQueue( receiver->_bufferQueue), _receiver(receiver), _timeStampSource( - timeStampSource), _sendNotificationListener( + timeStampSource), _versionNumberSource(versionNumberSource), _sendNotificationListener( sendNotificationListener) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. @@ -154,7 +158,7 @@ namespace ChimeraTK { * expected, and the base class is intentionally non-copyable. */ ProcessScalar & operator=(ProcessScalar const & other) { - set(other.get()); + set(other); return (*this); } @@ -167,13 +171,12 @@ namespace ChimeraTK { return *this; } - /** * Automatic conversion operator which returns a \b copy of this process * variable's value. As no reference is returned, this cannot be used for * assignment. */ - operator T() const { + operator T() const { return get(); } @@ -185,15 +188,43 @@ namespace ChimeraTK { void set(T const & t) { _value = t; } - + + /** + * Sets and sends the value of this process variable only if the version + * number of the new value is greater than the version number of the current + * value. + * + * This is a convenience method that is provided for when the value of a + * process variable is updated with a value calculated from the value of a + * different process variable. Calling this method is equivalent to the + * following code: + * + *
+     * if (newVersionNumber > getVersionNumber()) {
+     *   set(t);
+     *   send(newVersionNumber);
+     * }
+     * 
+ * + * By using this method, one can easily avoid overwriting a more recent + * value with an older value, which could otherwise happen due to updates + * being handled asynchronously. + */ + void setAndSendIfNewVersionGreater(T const & t, VersionNumber newVersionNumber) { + if (newVersionNumber > getVersionNumber()) { + set(t); + send(newVersionNumber); + } + } + // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl // inheritence for creating the pimpl pattern, where impl and not are typedefed. const std::type_info& getValueType() const { - return typeid(T); + return typeid(T); } bool isArray() const { - return false; + return false; } /** @@ -230,6 +261,26 @@ namespace ChimeraTK { return _timeStamp; } + /** + * Returns the version number that is associated with the current value. + * This is the version number that was received with the last + * {@link receive()} operation or sent with the last {@link send()} + * operation (whichever happened later). + * + * The version number is used to resolve conflicting updates. When an update + * is received using the {@link receive()} method, it is only used if its + * value has a version number that is greater than the version number of the + * current value. Initially, each process variable has a version number of + * zero. + * + * When this process variable has not been initialized with a version number + * source, its version number always stays at zero and the version-number + * logic is disabled. + */ + VersionNumber getVersionNumber() const { + return _versionNumber; + } + bool receive() { if (_instanceType != RECEIVER) { throw std::logic_error( @@ -237,15 +288,55 @@ namespace ChimeraTK { } Buffer nextBuffer; if (_bufferQueue->pop(nextBuffer)) { - _timeStamp = nextBuffer.timeStamp; - _value = nextBuffer.value; - return true; + if (!_versionNumberSource + || nextBuffer.versionNumber > getVersionNumber()) { + _timeStamp = nextBuffer.timeStamp; + _value = nextBuffer.value; + _versionNumber = nextBuffer.versionNumber; + return true; + } else { + return false; + } } else { return false; } } + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * If this process variable has a version-number source, a new version + * number is retrieved from this source and used for the value being sent to + * the receiver. Otherwise, a version number of zero is used. + * + * Throws an exception if this process variable is not a sender. + */ bool send() { + VersionNumber newVersionNumber; + if (_versionNumberSource) { + newVersionNumber = _versionNumberSource->nextVersionNumber(); + } else { + newVersionNumber = 0; + } + return send(newVersionNumber); + } + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * Throws an exception if this process variable is not a sender. + */ + bool send(VersionNumber newVersionNumber) { if (_instanceType != SENDER) { throw std::logic_error( "Send operation is only allowed for a sender process variable."); @@ -258,6 +349,8 @@ namespace ChimeraTK { Buffer nextBuffer; nextBuffer.timeStamp = _timeStamp; nextBuffer.value = _value; + nextBuffer.versionNumber = newVersionNumber; + _versionNumber = newVersionNumber; bool foundEmptyBuffer; if (_bufferQueue->bounded_push(nextBuffer)) { foundEmptyBuffer = true; @@ -286,6 +379,7 @@ namespace ChimeraTK { struct Buffer { TimeStamp timeStamp; T value; + VersionNumber versionNumber; }; /** @@ -303,6 +397,11 @@ namespace ChimeraTK { */ T _value; + /** + * Version number associated with the value. + */ + VersionNumber _versionNumber; + /** * Queue holding the values that have been sent but not received yet. * We do not use an spsc_queue for this queue, because might want to take @@ -322,6 +421,12 @@ namespace ChimeraTK { */ boost::shared_ptr _timeStampSource; + /** + * Version number source used to update the version number when setting a + * new value. + */ + boost::shared_ptr _versionNumberSource; + /** * Listener that is notified when the process variable is sent. */ @@ -329,7 +434,6 @@ namespace ChimeraTK { }; - /** * Creates a simple process scalar. A simple process scalar just works on its * own and does not implement a synchronization mechanism. Apart from this, @@ -377,10 +481,11 @@ namespace ChimeraTK { typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar( const std::string & name = "", T initialValue = 0, std::size_t numberOfBuffers = 1, - boost::shared_ptr timeStampSource = boost::shared_ptr< - TimeStampSource>(), - boost::shared_ptr sendNotificationListener = - boost::shared_ptr()); + TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + VersionNumberSource::SharedPtr versionNumberSource = + VersionNumberSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = + ProcessVariableListener::SharedPtr()); /** * A process variable which is not a sender/receiver pair but a single instance. @@ -390,24 +495,23 @@ namespace ChimeraTK { template typename ProcessScalar::SharedPtr createSimpleProcessScalar( const std::string & name, T initialValue) { - return boost::make_shared >( - ProcessScalar::STAND_ALONE, name, initialValue); + return boost::make_shared >(ProcessScalar::STAND_ALONE, + name, initialValue); } template typename std::pair::SharedPtr, typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar( const std::string & name, T initialValue, std::size_t numberOfBuffers, - boost::shared_ptr timeStampSource, - boost::shared_ptr sendNotificationListener) { - boost::shared_ptr > receiver = - boost::make_shared >( - ProcessScalar::RECEIVER, name, initialValue, - numberOfBuffers); + TimeStampSource::SharedPtr timeStampSource, + VersionNumberSource::SharedPtr versionNumberSource, + ProcessVariableListener::SharedPtr sendNotificationListener) { + boost::shared_ptr > receiver = boost::make_shared< + ProcessScalar >(ProcessScalar::RECEIVER, name, initialValue, + numberOfBuffers, versionNumberSource); typename ProcessScalar::SharedPtr sender = boost::make_shared< - ProcessScalar >( - ProcessScalar::SENDER, timeStampSource, - sendNotificationListener, receiver); + ProcessScalar >(ProcessScalar::SENDER, timeStampSource, + versionNumberSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessScalar::SharedPtr>(sender, receiver); } diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h new file mode 100644 index 0000000..80239e0 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h @@ -0,0 +1,16 @@ +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_H + +#include + +namespace ChimeraTK { + + /** + * Type alias for the version number type. The version number is used for + * resolving conflicting updates. + */ + typedef std::uint64_t VersionNumber; + +} // namespace ChimeraTK + +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_H diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h new file mode 100644 index 0000000..3dda4a7 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h @@ -0,0 +1,60 @@ +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H + +#include +#include + +#include "VersionNumber.h" + +namespace ChimeraTK { + + /** + * Source generating version numbers. Version numbers are used to resolve + * competing updates that are applied to the same process variable. For + * example, it they can help in breaking an infinite update loop that might + * occur when two process variables are related and update each other. + * + * Each version number returned by the same source is unique, but different + * sources may return the same version numbers. + */ + class VersionNumberSource { + + public: + /** + * Shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + + /** + * Returns the next version number. The next version number is determined in + * an atomic way, so that it is guaranteed that this method never returns + * the same version number twice (unless the counter overflows, which is + * very unlikely). The first version number returned by this method is one. + * The version number that is returned is guaranteed to be greater than the + * version numbers returned for earlier calls to this method. This method + * may safely be called by any thread without any synchronization. + */ + inline VersionNumber nextVersionNumber() { + VersionNumber currentVersionNumber = _lastReturnedVersionNumber.load( + boost::memory_order_acquire); + VersionNumber nextVersionNumber = currentVersionNumber + 1; + while (!_lastReturnedVersionNumber.compare_exchange_weak( + currentVersionNumber, nextVersionNumber, boost::memory_order_acq_rel, + boost::memory_order_acquire)) { + nextVersionNumber = currentVersionNumber + 1; + } + return nextVersionNumber; + } + + private: + /** + * Last version number that was returned by a call to + * {@link nextVersionNumber()}. + */ + boost::atomic _lastReturnedVersionNumber; + + }; + +} + +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H diff --git a/src/PVManager.cc b/src/PVManager.cc index 6023abc..e35b719 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -10,6 +10,8 @@ namespace ChimeraTK { using boost::shared_ptr; using std::list; + VersionNumberSource::SharedPtr PVManager::_versionNumberSource = boost::make_shared(); + PVManager::PVManager() : _controlSystemNotificationQueue(0), _deviceNotificationQueue(0), _automaticReferenceTimeStampMode( true), _referenceTimeStamp(0) { diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 0b79f0b..5d12b44 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -349,7 +349,8 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", 0, true, 2, - TimeStampSource::SharedPtr(), sendNotificationListener); + TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr(), + sendNotificationListener); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; BOOST_CHECK(sendNotificationListener->count == 0); @@ -443,11 +444,14 @@ init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { new ChimeraTK::ProcessArrayTestSuite); framework::master_test_suite().add( new ChimeraTK::ProcessArrayTestSuite); - framework::master_test_suite().add(new ChimeraTK::ProcessArrayTestSuite); + framework::master_test_suite().add( + new ChimeraTK::ProcessArrayTestSuite); framework::master_test_suite().add( new ChimeraTK::ProcessArrayTestSuite); - framework::master_test_suite().add(new ChimeraTK::ProcessArrayTestSuite); - framework::master_test_suite().add(new ChimeraTK::ProcessArrayTestSuite); + framework::master_test_suite().add( + new ChimeraTK::ProcessArrayTestSuite); + framework::master_test_suite().add( + new ChimeraTK::ProcessArrayTestSuite); return NULL; } diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp index fd7dbb3..e78a02c 100644 --- a/tests/src/testProcessScalar.cpp +++ b/tests/src/testProcessScalar.cpp @@ -135,7 +135,8 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessScalar::SharedPtr> senderReceiver = createSynchronizedProcessScalar("", 0, 1, - TimeStampSource::SharedPtr(), sendNotificationListener); + TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr(), + sendNotificationListener); typename ProcessScalar::SharedPtr sender = senderReceiver.first; typename ProcessScalar::SharedPtr receiver = senderReceiver.second; BOOST_CHECK(sendNotificationListener->count == 0); @@ -241,7 +242,8 @@ init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { new ChimeraTK::ProcessScalarTestSuite); framework::master_test_suite().add( new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add(new ChimeraTK::ProcessScalarTestSuite); + framework::master_test_suite().add( + new ChimeraTK::ProcessScalarTestSuite); return NULL; } From c39edcebe3a30df63d21f6ef36d20f9943e7d9cb Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Mon, 12 Sep 2016 19:04:38 +0200 Subject: [PATCH 100/786] Added tests for the version-number mechanism (issue #2). These tests verify that values send with an old version are actually discarded by the receiver. --- tests/src/testProcessArray.cpp | 40 +++++++++++++++++++++++++ tests/src/testProcessScalar.cpp | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 5d12b44..f522458 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -27,6 +27,7 @@ namespace ChimeraTK { static void testSendNotification(); static void testTimeStampSource(); static void testSynchronization(); + static void testVersionNumbers(); private: static size_t const N_ELEMENTS = 12; @@ -54,6 +55,7 @@ namespace ChimeraTK { add(BOOST_TEST_CASE(&ProcessArrayTest::testSendNotification)); add(BOOST_TEST_CASE(&ProcessArrayTest::testTimeStampSource)); add(BOOST_TEST_CASE(&ProcessArrayTest::testSynchronization)); + add(BOOST_TEST_CASE(&ProcessArrayTest::testVersionNumbers)); } }; @@ -430,6 +432,44 @@ namespace ChimeraTK { BOOST_CHECK(!receiver->receive()); } + template + void ProcessArrayTest::testVersionNumbers() { + VersionNumberSource::SharedPtr versionNumberSource = boost::make_shared< + VersionNumberSource>(); + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", 0, true, 2, + TimeStampSource::SharedPtr(), versionNumberSource); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + // Initially, the version number should be zero. + BOOST_CHECK(sender->getVersionNumber() == 0); + BOOST_CHECK(receiver->getVersionNumber() == 0); + // After sending and receiving a value, the version number on the receiver + // should be greater. + VersionNumber initialVersionNumber = receiver->getVersionNumber(); + sender->get()[0] = 1; + BOOST_CHECK(sender->send()); + BOOST_CHECK(receiver->receive()); + VersionNumber versionNumber = receiver->getVersionNumber(); + BOOST_CHECK(versionNumber > initialVersionNumber); + BOOST_CHECK(receiver->get()[0] == 1); + // When we send again specifying the same version number, there should be no + // update of the receiver. + sender->get()[0] = 2; + BOOST_CHECK(sender->send(versionNumber)); + BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(versionNumber == receiver->getVersionNumber()); + BOOST_CHECK(receiver->get()[0] == 1); + // When we explicitly use a greater version number, the receiver should be + // updated again. + sender->get()[0] = 3; + BOOST_CHECK(sender->send(versionNumberSource->nextVersionNumber())); + BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->getVersionNumber() > versionNumber); + BOOST_CHECK(receiver->get()[0] == 3); + } + } //namespace ChimeraTK test_suite* diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp index e78a02c..08c4ba2 100644 --- a/tests/src/testProcessScalar.cpp +++ b/tests/src/testProcessScalar.cpp @@ -23,6 +23,7 @@ namespace ChimeraTK { static void testSendNotification(); static void testTimeStampSource(); static void testSynchronization(); + static void testVersionNumbers(); }; /** @@ -40,6 +41,7 @@ namespace ChimeraTK { add(BOOST_TEST_CASE(&ProcessScalarTest::testSendNotification)); add(BOOST_TEST_CASE(&ProcessScalarTest::testTimeStampSource)); add(BOOST_TEST_CASE(&ProcessScalarTest::testSynchronization)); + add(BOOST_TEST_CASE(&ProcessScalarTest::testVersionNumbers)); } }; @@ -222,6 +224,56 @@ namespace ChimeraTK { BOOST_CHECK(*receiver == 46); } + template + void ProcessScalarTest::testVersionNumbers() { + VersionNumberSource::SharedPtr versionNumberSource = boost::make_shared< + VersionNumberSource>(); + typename std::pair::SharedPtr, + typename ProcessScalar::SharedPtr> senderReceiver = + createSynchronizedProcessScalar("", 0, 1, + TimeStampSource::SharedPtr(), versionNumberSource); + typename ProcessScalar::SharedPtr sender = senderReceiver.first; + typename ProcessScalar::SharedPtr receiver = senderReceiver.second; + // Initially, the version number should be zero. + BOOST_CHECK(sender->getVersionNumber() == 0); + BOOST_CHECK(receiver->getVersionNumber() == 0); + // After sending a value, the version number of the sender should be greater + // than the version number of the receiver. After receiving that value, the + // version numbers should match. + *sender = 1; + BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->getVersionNumber() > receiver->getVersionNumber()); + BOOST_CHECK(receiver->receive()); + BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); + BOOST_CHECK(*receiver == 1); + // Sending another value should have the same effect. + *sender = 2; + BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->getVersionNumber() > receiver->getVersionNumber()); + BOOST_CHECK(receiver->receive()); + BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); + BOOST_CHECK(*receiver == 2); + // Sending a value with the same version number should not result in an + // update on the receiver side. + *sender = 3; + BOOST_CHECK(sender->send(sender->getVersionNumber())); + BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); + BOOST_CHECK(!receiver->receive()); + BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); + BOOST_CHECK(*receiver == 2); + // Finally we send an update with a version number that we explicitly get + // from the version number source. + VersionNumber newVersionNumber = versionNumberSource->nextVersionNumber(); + *sender = 4; + BOOST_CHECK(sender->send(newVersionNumber)); + BOOST_CHECK(sender->getVersionNumber() == newVersionNumber); + BOOST_CHECK(sender->getVersionNumber() > receiver->getVersionNumber()); + BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->getVersionNumber() == newVersionNumber); + BOOST_CHECK(sender->getVersionNumber() == newVersionNumber); + BOOST_CHECK(*receiver == 4); + } + } //namespace ChimeraTK test_suite* From c026bef655ed95cb576c6ee465271a0c63ced54f Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Tue, 13 Sep 2016 12:15:42 +0200 Subject: [PATCH 101/786] Removed unused version number fields (issue #2). The removed fields were accidentally left over from a different implementation of the version-number mechanism and where not used at all by the current code. --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 07fa7fb..6c86dd9 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -650,18 +650,6 @@ namespace ChimeraTK { */ std::size_t _lastSentIndex; - /** - * Flag indicating whether the {@link originalVersionNumber} field stores - * a version number that should be used for the next send operation. - */ - bool _haveOriginalVersionNumber; - - /** - * Version number that shall be used for the next send operation if - * {@link haveOriginalVersionNumber} is set. - */ - VersionNumber _originalVersionNumber; - /** * Pointer to the receiver associated with this sender. This field is only * used if this process variable represents a sender. From f2d9307f5e2f6b91178cdf7b0cfacad298bd2d49 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Tue, 13 Sep 2016 14:31:46 +0200 Subject: [PATCH 102/786] Implement copy-on-send for ProcessArrays (issue #5). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using send() on a ProcessArray, the array’s value is now copied by default. A new sendDestructively() method has been added that allows for sending a value without copying. This method is only available if the corresponding flag has been set when creating the process array. As a result of this change, the getConst(), getLastSent(), getLastSentVersionNumber(), and isSwappable() methods have been removed and a ProcessArray’s value cano now always be modified, even if it is a receiver. --- .../ControlSystemAdapter/DevicePVManager.h | 42 +- .../ControlSystemAdapter/PVManager.h | 34 +- .../ControlSystemAdapter/ProcessArray.h | 359 ++++++++---------- .../ProcessArrayAccessor.h | 4 +- tests/src/testProcessArray.cpp | 138 +++---- 5 files changed, 244 insertions(+), 333 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index e4a6dae..5f5e2c6 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -84,11 +84,10 @@ namespace ChimeraTK { * process scalar or array is an error and causes an * \c std::invalid_argument exception to be thrown. * - * If the swappable flag is true (the default), - * the control-system PV is marked as swappable and thus the control system - * can retrieve data by swapping instead of copying. This means that the - * device library cannot access the data (not even for reading) after - * sending it. + * If the maySendDestructively flag is true (it is + * false by default), the sendDestructively() + * method may be used to transfer values without copying but losing them on + * the sender side. * * The number of buffers (the minimum and default value is two) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -103,8 +102,8 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, const std::string& processVariableName, std::size_t size, - T initialValue = 0, bool swappable = true, std::size_t numberOfBuffers = - 2); + T initialValue = 0, bool maySendDestructively = false, + std::size_t numberOfBuffers = 2); /** * Creates a new process array and registers it with the PV manager. @@ -116,11 +115,10 @@ namespace ChimeraTK { * provided for initialization and all elements are initialized with the * values provided by this vector. * - * If the swappable flag is true (the default), - * the control-system PV is marked as swappable and thus the control system - * can retrieve data by swapping instead of copying. This means that the - * device library cannot access the data (not even for reading) after - * sending it. + * If the maySendDestructively flag is true (it is + * false by default), the sendDestructively() + * method may be used to transfer values without copying but losing them on + * the sender side. * * The number of buffers (the minimum and default value is two) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -135,7 +133,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, const std::string& processVariableName, - const std::vector& initialValue, bool swappable = true, + const std::vector& initialValue, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** @@ -278,16 +276,16 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, const std::string& processVariableName, std::size_t size, T initialValue, - bool swappable, std::size_t numberOfBuffers) { + bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { case controlSystemToDevice: return _pvManager->createProcessArrayControlSystemToDevice( - processVariableName, std::vector(size, initialValue), swappable, - numberOfBuffers).second; + processVariableName, std::vector(size, initialValue), + maySendDestructively, numberOfBuffers).second; case deviceToControlSystem: return _pvManager->createProcessArrayDeviceToControlSystem( - processVariableName, std::vector(size, initialValue), swappable, - numberOfBuffers).second; + processVariableName, std::vector(size, initialValue), + maySendDestructively, numberOfBuffers).second; default: throw std::invalid_argument("invalid SynchronizationDirection"); } @@ -297,15 +295,17 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, const std::string& processVariableName, - const std::vector& initialValue, bool swappable, + const std::vector& initialValue, bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { case controlSystemToDevice: return _pvManager->createProcessArrayControlSystemToDevice( - processVariableName, initialValue, swappable, numberOfBuffers).second; + processVariableName, initialValue, maySendDestructively, + numberOfBuffers).second; case deviceToControlSystem: return _pvManager->createProcessArrayDeviceToControlSystem( - processVariableName, initialValue, swappable, numberOfBuffers).second; + processVariableName, initialValue, maySendDestructively, + numberOfBuffers).second; default: throw std::invalid_argument("invalid SynchronizationDirection"); } diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 0c8fb08..13364f7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -117,11 +117,10 @@ namespace ChimeraTK { * provided for initialization and all elements are initialized with the * values provided by this vector. * - * If the swappable flag is true (the default), - * the control-system PV is marked as swappable and thus the control system - * can retrieve data by swapping instead of copying. This means that the - * device library cannot access the data (not even for reading) after - * sending it. + * If the maySendDestructively flag is true (it is + * false by default), the sendDestructively() + * method may be used to transfer values without copying but losing them on + * the sender side. * * The number of buffers (the minimum and default value is two) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -137,7 +136,7 @@ namespace ChimeraTK { std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( const std::string& processVariableName, - const std::vector& initialValue, bool swappable = true, + const std::vector& initialValue, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** @@ -151,11 +150,10 @@ namespace ChimeraTK { * provided for initialization and all elements are initialized with the * values provided by this vector. * - * If the swappable flag is true (the default), - * the device-library PV is marked as swappable and thus the device library - * can retrieve data by swapping instead of copying. This means that the - * control system cannot access the data (not even for reading) after - * sending it. + * If the maySendDestructively flag is true (it is + * false by default), the sendDestructively() + * method may be used to transfer values without copying but losing them on + * the sender side. * * The number of buffers (the minimum and default value is two) is the max. * number of values that can be queued in the transfer queue. Specifying a @@ -171,7 +169,7 @@ namespace ChimeraTK { std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( const std::string& processVariableName, - const std::vector& initialValue, bool swappable = true, + const std::vector& initialValue, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** @@ -573,7 +571,7 @@ namespace ChimeraTK { std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( const std::string& processVariableName, - const std::vector& initialValue, bool swappable, + const std::vector& initialValue, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) != _processVariables.end()) { @@ -591,8 +589,8 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, - swappable, numberOfBuffers, timeStampSource, _versionNumberSource, - sendNotificationListener); + numberOfBuffers, maySendDestructively, timeStampSource, + _versionNumberSource, sendNotificationListener); _processVariables.insert( std::make_pair(processVariableName, @@ -610,7 +608,7 @@ namespace ChimeraTK { std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( const std::string& processVariableName, - const std::vector& initialValue, bool swappable, + const std::vector& initialValue, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) != _processVariables.end()) { @@ -629,8 +627,8 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, - swappable, numberOfBuffers, timeStampSource, _versionNumberSource, - sendNotificationListener); + numberOfBuffers, maySendDestructively, timeStampSource, + _versionNumberSource, sendNotificationListener); _processVariables.insert( std::make_pair(processVariableName, diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 6c86dd9..7a2cb16 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -76,7 +76,7 @@ namespace ChimeraTK { ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue) : ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(true), _buffers( + initialValue.size()), _maySendDestructively(false), _buffers( boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( 0) { // It would be better to do the validation before initializing, but this @@ -100,16 +100,12 @@ namespace ChimeraTK { * * This constructor creates the buffers and queues that are needed for the * send/receive process and are shared with the sender. - * - * If the swappable option is true, the swap operation is - * allowed. The swappable flag must be the same for the - * sender and receiver. */ ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue, std::size_t numberOfBuffers, - bool swappable, VersionNumberSource::SharedPtr versionNumberSource) : + VersionNumberSource::SharedPtr versionNumberSource) : ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(swappable), _buffers( + initialValue.size()), _maySendDestructively(false), _buffers( boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( boost::make_shared >( numberOfBuffers)), _emptyBufferQueue( @@ -161,10 +157,9 @@ namespace ChimeraTK { * it. It uses the buffers and queues that have been created by the * receiver. * - * If the swappable option is true, the swap operation is - * allowed on the receiver. The swappable flag must be the - * same for the sender and receiver. Allowing swapping has the side effect - * that a value cannot be read after having been sent. + * If the maySendDestructively flag is true, the + * {@link sendDestructively()} method may be used to transfer values without + * copying but losing them on the sender side. * * The optional send-notification listener is notified every time the * sender's {@link ProcessArray::send()} method is called. It can be @@ -172,13 +167,13 @@ namespace ChimeraTK { * {@link ProcessArray::receive()} method to be called. The process * variable passed to the listener is the receiver and not the sender. */ - ProcessArray(InstanceType instanceType, bool swappable, + ProcessArray(InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener, ProcessArray::SharedPtr receiver) : ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( - receiver->_vectorSize), _swappable(swappable), _buffers( + receiver->_vectorSize), _maySendDestructively(maySendDestructively), _buffers( receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( receiver), _timeStampSource(timeStampSource), _versionNumberSource( @@ -225,20 +220,13 @@ namespace ChimeraTK { } operator std::vector() const { - return getConst(); + return get(); } /** * Updates this process variable's value with the elements from the * specified vector. The vector's number of elements must match this process * variable's number of elements. - * - * If this instance of the process array must not be modified (because it is - * a receiver and does not allow swapping), this method throws an exception. - * - * FIXME: The description says it is throwing, but the implementation does not - * do it. Throwing will be obsolete in future because it is planned to have - * receivers always swappable. */ void set(std::vector const & v) { get() = v; @@ -248,33 +236,19 @@ namespace ChimeraTK { * Updates this process variable's value with the other process variable's * value. The other process variable's number of elements must match this * process variable's number of elements. - * - * If this instance of the process array must not be modified (because it is - * a receiver and does not allow swapping), this method throws an exception. - * - * FIXME: The description says it is throwing, but the implementation does not - * do it. Throwing will be obsolete in future because it is planned to have - * receivers always swappable. */ void set(ProcessArray const & other) { - set(other.getConst()); + set(other.get()); } /** * Swaps the vector backing this process array with a different vector. - * This method may only be called by the peer control-system process array - * when synchronizing with this process array and only if - * isSwappable() returns true. Otherwise, this - * method throws an std::logic_error. * * The boost::scoped_ptr passed must not be null * and must point to a vector that has the same size as the vector it is * swapped with. */ void swap(boost::scoped_ptr > & otherVector) { - if (!isSwappable()) { - throw std::logic_error("Swap is not supported by this process array."); - } if (otherVector->size() != get().size()) { throw std::runtime_error("Vector sizes do not match"); } @@ -285,20 +259,11 @@ namespace ChimeraTK { * Returns a reference to the vector that represents the current value of * this process array. * - * If this instance of the process array must not be modified - * (because it is a receiver and does not allow swapping), this - * method throws an exception. In this case, you should use the - * {@link #getConst()} method instead. - * * The reference returned by this method becomes invalid when a receive, * send, or swap operation is performed on this process variable. Use of an * invalid reference results in undefined behavior. */ std::vector & get() { - if (_instanceType == RECEIVER && !_swappable) { - throw std::logic_error( - "Attempt to modify a read-only process variable."); - } return *(((*_buffers)[_currentIndex]).value); } @@ -312,48 +277,9 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector const & get() const { - return getConst(); - } - - /** - * Returns a constant reference to the the vector that represents the - * current value of this process array. This method should be preferred for - * receiver process variables because it works even if the process array - * does not allow swapping and thus the {@link #get()} method cannot be - * used. - * - * The reference returned by this method becomes invalid when a receive, - * send, or swap operation is performed on this process variable. Use of an - * invalid reference results in undefined behavior. - */ - std::vector const & getConst() const { return *(((*_buffers)[_currentIndex]).value); } - /** - * @deprecated - * It is planned that receivers are always swappable. - * - * Returns a constant reference to the vector that represents the value that - * has been sent with the last sent operation. - * - * This method may only be used on a process variable that is a sender and - * if swapping is not allowed for the respective receiver. In all other - * cases, calling this method results in an exception. - * - * While the reference returned by this method stays valid even after a - * receive, send, or swap operation is performed on this process variable, - * it is recommended to not retain this reference because it might point - * to a different vector than expected. - */ - std::vector const & getLastSent() const { - if (_instanceType != SENDER || _swappable) { - throw std::logic_error( - "The last sent value is only available for a sender that is associated with a receiver that does not allow swapping."); - } - return *(((*_buffers)[_lastSentIndex]).value); - } - bool isReceiver() const { return _instanceType == RECEIVER; } @@ -369,11 +295,9 @@ namespace ChimeraTK { /** * Returns the version number that is associated with the current value. * This is the version number that was received with the {@link receive()} - * operation that received the respective value. Typically, this will be the - * last receive operation. If this process variable has just been sent, the - * value and thus the version number is undefined (it can be any value / - * version-number combination that has been sent at an earlier point in - * time). + * operation that received the respective value or the last {@link send()} + * operation. If the last send operation was destructive, the version number + * (like the current value) is undefined. * * The version number is used to resolve conflicting updates. When an update * is received using the {@link receive()} method, it is only used if its @@ -391,49 +315,6 @@ namespace ChimeraTK { return ((*_buffers)[_currentIndex]).versionNumber; } - /** - * Returns the version number associated with the value that has been sent - * with the last sent operation. - * - * This method may only be used on a process variable that is a sender and - * if swapping is not allowed for the respective receiver. In all other - * cases, calling this method results in an exception. - * - * @deprecated - * This method is going to be removed together with the - * {@link getLastSent()} and {@link isSwappable()} methods. - */ - VersionNumber getLastSentVersionNumber() const { - // TODO Typically, there should be no reason to check the version number - // of the last sent value, but we still have this method for symmetry - // reasons (because we also have a getLastSent() method). - // We should really consider getting rid of both methods and allow for an - // (optional) copy when sending instead of disabling swapping. - if (_instanceType != SENDER || _swappable) { - throw std::logic_error( - "The last sent version number is only available for a sender that is associated with a receiver that does not allow swapping."); - } - return ((*_buffers)[_lastSentIndex]).versionNumber; - } - - /** - * Tells whether this array supports swapping. If true, the - * swap method can be used to swap the vector backing this - * array with a different one, thus avoiding copying during synchronization. - * If false, calling swap results in an exception. - * Swapping (and other modifications) are always supported on the sender - * side but might not be supported on the receiver side. - */ - bool isSwappable() const { - // Senders and stand-alone instances are always swappable. For - // receivers, it depends on the swappable flag. - if (_instanceType == RECEIVER) { - return _swappable; - } else { - return true; - } - } - bool receive() { if (_instanceType != RECEIVER) { throw std::logic_error( @@ -500,55 +381,63 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender. */ bool send(VersionNumber newVersionNumber) { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable."); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the receiver might get into - // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); - } - // Before sending the value, we have to update the associated - // time-stamp. - ((*_buffers)[_currentIndex]).timeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - ((*_buffers)[_currentIndex]).versionNumber = newVersionNumber; - std::size_t nextIndex; - bool foundEmptyBuffer; - if (_emptyBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = true; - _fullBufferQueue->push(_currentIndex); + return sendInternal(newVersionNumber, true); + } + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * If this process variable has a version-number source, a new version + * number is retrieved from this source and used for the value being sent to + * the receiver. Otherwise, a version number of zero is used. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool sendDestructively() { + VersionNumber newVersionNumber; + if (_versionNumberSource) { + newVersionNumber = _versionNumberSource->nextVersionNumber(); } else { - // We can still send, but we will lose older data. - if (_fullBufferQueue->pop(nextIndex)) { - foundEmptyBuffer = false; - _fullBufferQueue->push(_currentIndex); - } else { - // It is possible, that we did not find an empty buffer but before - // we could get a full buffer, the receiver processed all full - // buffers. In this case, the queue of empty buffers cannot be empty - // any longer because we have at least four buffers. - if (!_emptyBufferQueue->pop(nextIndex)) { - // This should never happen and is just an assertion for extra - // safety. - throw std::runtime_error( - "Assertion that empty-buffer queue has at least one element failed."); - } - foundEmptyBuffer = true; - _fullBufferQueue->push(_currentIndex); - } + newVersionNumber = 0; } - _lastSentIndex = _currentIndex; - _currentIndex = nextIndex; - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); + return sendDestructively(newVersionNumber); + } + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool sendDestructively(VersionNumber newVersionNumber) { + if (!_maySendDestructively) { + throw std::runtime_error( + "This process variable must not be sent destructively because the corresponding flag has not been set."); } - return foundEmptyBuffer; + return sendInternal(newVersionNumber, false); } const std::type_info& getValueType() const { @@ -610,12 +499,10 @@ namespace ChimeraTK { std::size_t _vectorSize; /** - * Flag indicating the swapping behavior. For a receiver, it indicates - * whether swapping is allowed. For a sender, it indicates whether - * swapping is allowed on the corresponding receiver. For a stand-alone - * instance, it does not have any meaning. + * Flag indicating whether the {@code sendDestructively} methods may be used + * on this process array. */ - bool _swappable; + bool _maySendDestructively; /** * Buffers that hold the actual values. @@ -672,6 +559,68 @@ namespace ChimeraTK { */ boost::shared_ptr _sendNotificationListener; + /** + * Internal implementation of the various {@code send} methods. All these + * methods basically do the same and only differ in whether the data in the + * sent array is copied to the buffer used as the new current buffer and + * which version number is used. + */ + bool sendInternal(VersionNumber newVersionNumber, bool shouldCopy) { + if (_instanceType != SENDER) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable."); + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the receiver might get into + // trouble when it suddenly experiences a vector of the wrong size. + if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + } + // Before sending the value, we have to update the associated + // time-stamp. + TimeStamp newTimeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : + TimeStamp::currentTime(); + ((*_buffers)[_currentIndex]).timeStamp = newTimeStamp; + ((*_buffers)[_currentIndex]).versionNumber = newVersionNumber; + std::size_t nextIndex; + bool foundEmptyBuffer; + if (_emptyBufferQueue->pop(nextIndex)) { + foundEmptyBuffer = true; + } else { + // We can still send, but we will lose older data. + if (_fullBufferQueue->pop(nextIndex)) { + foundEmptyBuffer = false; + } else { + // It is possible, that we did not find an empty buffer but before + // we could get a full buffer, the receiver processed all full + // buffers. In this case, the queue of empty buffers cannot be empty + // any longer because we have at least four buffers. + if (!_emptyBufferQueue->pop(nextIndex)) { + // This should never happen and is just an assertion for extra + // safety. + throw std::runtime_error( + "Assertion that empty-buffer queue has at least one element failed."); + } + foundEmptyBuffer = true; + } + } + if (shouldCopy) { + *((*_buffers)[nextIndex].value) = *((*_buffers)[_currentIndex].value); + (*_buffers)[nextIndex].timeStamp = newTimeStamp; + (*_buffers)[nextIndex].versionNumber = newVersionNumber; + } + _fullBufferQueue->push(_currentIndex); + _lastSentIndex = _currentIndex; + _currentIndex = nextIndex; + if (_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); + } + return foundEmptyBuffer; + } + }; /** @@ -714,18 +663,17 @@ namespace ChimeraTK { * thread. This means that the sender or the receiver have to be protected * with a mutex if more than one thread wants to access either of them. * - * If the swappable flag is true (the default), the - * receiver supports full read-write access (including swapping). If it is - * false, only read-only access is supported. On the other hand, - * setting this flag to false has the advantage that the sender's - * {@link ProcessArray::getLastSent()} method can be used. - * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This * number specifies, how many times {@link ProcessArray::send()} can be called * in a row without losing data when {@link ProcessArray::receive()} is not * called in between. * + * If the maySendDestructively flag is true (it is + * false by default), the {@link sendDestructively()} method may + * be used to transfer values without copying but losing them on the sender + * side. + * * The specified time-stamp source is used for determining the current time * when sending a value. The receiver will be updated with this time stamp * when receiving the value. If no time-stamp source is specified, the current @@ -743,7 +691,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const std::string & name = "", T initialValue = 0, - bool swappable = true, std::size_t numberOfBuffers = 2, + std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr versionNumberSource = VersionNumberSource::SharedPtr(), @@ -766,18 +714,17 @@ namespace ChimeraTK { * thread. This means that the sender or the receiver have to be protected * with a mutex if more than one thread wants to access either of them. * - * If the swappable flag is true (the default), the - * receiver supports full read-write access (including swapping). If it is - * false, only read-only access is supported. On the other hand, - * setting this flag to false has the advantage that the sender's - * {@link ProcessArray::getLastSent()} method can be used. - * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This * number specifies, how many times {@link ProcessArray::send()} can be called * in a row without losing data when {@link ProcessArray::receive()} is not * called in between. * + * If the maySendDestructively flag is true (it is + * false by default), the {@link sendDestructively()} method may + * be used to transfer values without copying but losing them on the sender + * side. + * * The specified time-stamp source is used for determining the current time * when sending a value. The receiver will be updated with this time stamp * when receiving the value. If no time-stamp source is specified, the current @@ -797,7 +744,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( const std::vector& initialValue, const std::string & name = "", - bool swappable = true, std::size_t numberOfBuffers = 2, + std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr versionNumberSource = VersionNumberSource::SharedPtr(), @@ -822,17 +769,18 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const std::string & name, T initialValue, - bool swappable, std::size_t numberOfBuffers, + std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener) { typename boost::shared_ptr > receiver = boost::make_shared< ProcessArray >(ProcessArray::RECEIVER, name, - std::vector(size, initialValue), numberOfBuffers, swappable, + std::vector(size, initialValue), numberOfBuffers, versionNumberSource); typename ProcessArray::SharedPtr sender = boost::make_shared< - ProcessArray >(ProcessArray::SENDER, swappable, timeStampSource, - versionNumberSource, sendNotificationListener, receiver); + ProcessArray >(ProcessArray::SENDER, maySendDestructively, + timeStampSource, versionNumberSource, sendNotificationListener, + receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); } @@ -841,16 +789,17 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( const std::vector& initialValue, const std::string & name, - bool swappable, std::size_t numberOfBuffers, + std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener) { typename boost::shared_ptr > receiver = boost::make_shared< ProcessArray >(ProcessArray::RECEIVER, name, initialValue, - numberOfBuffers, swappable, versionNumberSource); + numberOfBuffers, versionNumberSource); typename ProcessArray::SharedPtr sender = boost::make_shared< - ProcessArray >(ProcessArray::SENDER, swappable, timeStampSource, - versionNumberSource, sendNotificationListener, receiver); + ProcessArray >(ProcessArray::SENDER, maySendDestructively, + timeStampSource, versionNumberSource, sendNotificationListener, + receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); } diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h index d066f18..6b8c874 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h @@ -19,10 +19,10 @@ namespace ChimeraTK{ return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } std::vector const & get() const{ - return boost::static_pointer_cast< ProcessArray >(_impl)->getConst(); + return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } std::vector const & getConst() const{ - return boost::static_pointer_cast< ProcessArray >(_impl)->getConst(); + return boost::static_pointer_cast< ProcessArray >(_impl)->get(); } UserType& operator[](unsigned int element) { diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index f522458..fd5c429 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -121,7 +121,7 @@ namespace ChimeraTK { BOOST_CHECK(receiver->isReceiver()); BOOST_CHECK(!receiver->isSender()); senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "test", - SOME_NUMBER, false, 5); + SOME_NUMBER, 5); sender = senderReceiver.first; receiver = senderReceiver.second; BOOST_CHECK(sender->getName() == "test"); @@ -131,13 +131,13 @@ namespace ChimeraTK { } BOOST_CHECK(sender->get().size() == N_ELEMENTS); BOOST_CHECK(receiver->getName() == "test"); - for (typename std::vector::const_iterator i = - receiver->getConst().begin(); i != receiver->getConst().end(); ++i) { + for (typename std::vector::const_iterator i = receiver->get().begin(); + i != receiver->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - BOOST_CHECK(receiver->getConst().size() == N_ELEMENTS); + BOOST_CHECK(receiver->get().size() == N_ELEMENTS); senderReceiver = createSynchronizedProcessArray(referenceVector, "test", - false, 5); + 5, false); sender = senderReceiver.first; receiver = senderReceiver.second; BOOST_CHECK(sender->getName() == "test"); @@ -146,9 +146,9 @@ namespace ChimeraTK { std::equal(sender->get().begin(), sender->get().end(), referenceVector.begin())); BOOST_CHECK(receiver->getName() == "test"); - BOOST_CHECK(receiver->getConst().size() == 4); + BOOST_CHECK(receiver->get().size() == 4); BOOST_CHECK( - std::equal(receiver->getConst().begin(), receiver->getConst().end(), + std::equal(receiver->get().begin(), receiver->get().end(), referenceVector.begin())); } @@ -183,17 +183,6 @@ namespace ChimeraTK { (*receiver) = (*array1); (*sender) = v; (*receiver) = v; - // If the synchronized array is configured to not allow swapping on the - // receiver, trying to assign a value on the receiver should result in an - // exception, but it should still work for the sender. - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", 0, - false); - sender = senderReceiver.first; - receiver = senderReceiver.second; - (*sender) = (*array1); - BOOST_CHECK_THROW((*receiver) = (*array1), std::logic_error); - (*sender) = v; - BOOST_CHECK_THROW((*receiver) = v, std::logic_error); } template @@ -201,7 +190,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr simpleArray = createSimpleProcessArray(N_ELEMENTS, "", SOME_NUMBER); typename std::vector & v = simpleArray->get(); - typename std::vector const & cv = simpleArray->getConst(); + typename std::vector const & cv = simpleArray->get(); for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } @@ -218,9 +207,6 @@ namespace ChimeraTK { i != cv2.end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - // For a simple array, getLastSent() is not supported, so it should throw - // an exception. - BOOST_CHECK_THROW(simpleArray->getLastSent(), std::logic_error); // Next we run the tests for a synchronized array that supports swapping. We // only test that the getter methods do not throw an exception, as we can // assume that the returned reference is correct if it was correct for the @@ -231,38 +217,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; sender->get(); - sender->getConst(); receiver->get(); - receiver->getConst(); - // If swapping is allowed, getLastSent() should throw an exception for both - // the sender and the receiver. - BOOST_CHECK_THROW(sender->getLastSent(), std::logic_error); - BOOST_CHECK_THROW(receiver->getLastSent(), std::logic_error); - // Finally, we run the tests for a synchronized array that does not allow - // swapping. - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", - SOME_NUMBER, false); - sender = senderReceiver.first; - receiver = senderReceiver.second; - sender->get(); - sender->getConst(); - BOOST_CHECK_THROW(receiver->get(), std::logic_error); - receiver->getConst(); - sender->getLastSent(); - BOOST_CHECK_THROW(receiver->getLastSent(), std::logic_error); - // Now we send and then modify the array. The getLastSent() method should - // still return the old values, while get() should return the new ones. - sender->send(); - sender->set(std::vector(N_ELEMENTS, SOME_NUMBER + 1)); - typename std::vector & v2 = sender->get(); - for (typename std::vector::iterator i = v2.begin(); i != v2.end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 1); - } - typename std::vector const & cv3 = sender->getLastSent(); - for (typename std::vector::const_iterator i = cv3.begin(); - i != cv3.end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } } template @@ -296,17 +251,6 @@ namespace ChimeraTK { receiver->set(*array1); sender->set(v); receiver->set(v); - // If the synchronized array is configured to not allow swapping on the - // receiver, trying to assign a value on the receiver should result in an - // exception, but it should still work for the sender. - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", 0, - false); - sender = senderReceiver.first; - receiver = senderReceiver.second; - sender->set(*array1); - BOOST_CHECK_THROW(receiver->set(*array1), std::logic_error); - sender->set(v); - BOOST_CHECK_THROW(receiver->set(v), std::logic_error); } template @@ -333,15 +277,6 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr receiver = senderReceiver.second; sender->swap(v); receiver->swap(v); - // If the synchronized array is configured to not allow swapping on the - // receiver, trying to swap a value on the receiver should result in an - // exception, but it should still work for the sender. - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", 0, - false); - sender = senderReceiver.first; - receiver = senderReceiver.second; - sender->swap(v); - BOOST_CHECK_THROW(receiver->swap(v), std::logic_error); } template @@ -350,7 +285,7 @@ namespace ChimeraTK { boost::make_shared()); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, true, 2, + createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, false, TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr(), sendNotificationListener); typename ProcessArray::SharedPtr sender = senderReceiver.first; @@ -370,7 +305,7 @@ namespace ChimeraTK { boost::make_shared()); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, true, 2, + createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, false, timeStampSource); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; @@ -389,15 +324,15 @@ namespace ChimeraTK { void ProcessArrayTest::testSynchronization() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS); + createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, true); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; // If we send two values consecutively, they both should be received because // the number of buffers is two. sender->get().assign(N_ELEMENTS, SOME_NUMBER); - BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->sendDestructively()); sender->get().assign(N_ELEMENTS, SOME_NUMBER + 1); - BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->sendDestructively()); BOOST_CHECK(receiver->receive()); for (typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { @@ -413,11 +348,11 @@ namespace ChimeraTK { // Now we try to send three values consecutively. This should result in the // first value being dropped. sender->get().assign(N_ELEMENTS, SOME_NUMBER + 2); - BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->sendDestructively()); sender->get().assign(N_ELEMENTS, SOME_NUMBER + 3); - BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->sendDestructively()); sender->get().assign(N_ELEMENTS, SOME_NUMBER + 4); - BOOST_CHECK(!sender->send()); + BOOST_CHECK(!sender->sendDestructively()); BOOST_CHECK(receiver->receive()); for (typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { @@ -430,6 +365,26 @@ namespace ChimeraTK { } // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->receive()); + // When we send non-destructively, the value should also be preserved on the + // sender side. + sender->get().assign(N_ELEMENTS, SOME_NUMBER + 5); + BOOST_CHECK(sender->send()); + BOOST_CHECK(receiver->receive()); + for (typename std::vector::iterator i = receiver->get().begin(); + i != receiver->get().end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 5); + } + for (typename std::vector::iterator i = sender->get().begin(); + i != sender->get().end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 5); + } + // Calling sendDestructively() on a sender that has not the corresponding + // flag set should result in an exception. + senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, false); + sender = senderReceiver.first; + receiver = senderReceiver.second; + BOOST_CHECK_THROW(sender->sendDestructively(), std::runtime_error); } template @@ -438,18 +393,18 @@ namespace ChimeraTK { VersionNumberSource>(); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, true, 2, + createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, true, TimeStampSource::SharedPtr(), versionNumberSource); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; // Initially, the version number should be zero. BOOST_CHECK(sender->getVersionNumber() == 0); BOOST_CHECK(receiver->getVersionNumber() == 0); - // After sending and receiving a value, the version number on the receiver - // should be greater. + // After sending destructively and receiving a value, the version number on + // the receiver should be greater. VersionNumber initialVersionNumber = receiver->getVersionNumber(); sender->get()[0] = 1; - BOOST_CHECK(sender->send()); + BOOST_CHECK(sender->sendDestructively()); BOOST_CHECK(receiver->receive()); VersionNumber versionNumber = receiver->getVersionNumber(); BOOST_CHECK(versionNumber > initialVersionNumber); @@ -457,17 +412,26 @@ namespace ChimeraTK { // When we send again specifying the same version number, there should be no // update of the receiver. sender->get()[0] = 2; - BOOST_CHECK(sender->send(versionNumber)); + BOOST_CHECK(sender->sendDestructively(versionNumber)); BOOST_CHECK(!receiver->receive()); BOOST_CHECK(versionNumber == receiver->getVersionNumber()); BOOST_CHECK(receiver->get()[0] == 1); // When we explicitly use a greater version number, the receiver should be // updated again. sender->get()[0] = 3; - BOOST_CHECK(sender->send(versionNumberSource->nextVersionNumber())); + BOOST_CHECK(sender->sendDestructively(versionNumberSource->nextVersionNumber())); BOOST_CHECK(receiver->receive()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->get()[0] == 3); + // When we send non-destructively, the version number on the sender and the + // receiver should match after sending and receiving. + versionNumber = receiver->getVersionNumber(); + sender->get()[0] = 4; + BOOST_CHECK(sender->send()); + BOOST_CHECK(receiver->receive()); + BOOST_CHECK(receiver->getVersionNumber() > versionNumber); + BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); + BOOST_CHECK(receiver->get()[0] == 4); } } //namespace ChimeraTK From 15d5aa3da4b15287e05915982ebf230588bf9b2c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 14 Sep 2016 18:00:27 +0200 Subject: [PATCH 103/786] made the script a bit more generic to potentially also work with non-project-template dependencies --- cmake/add_dependency.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index 1cd1ca0..c547715 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -29,8 +29,9 @@ MACRO(add_dependency dependency_project_name required_version components) FIND_PACKAGE(${dependency_project_name} ${required_version} COMPONENTS ${components}) - include_directories(SYSTEM ${${dependency_project_name}_INCLUDE_DIRS}) + include_directories(SYSTEM ${${dependency_project_name}_INCLUDE_DIRS} ${${dependency_project_name}_INCLUDE_DIR}) link_directories(${${dependency_project_name}_LIBRARY_DIRS}) + link_directories(${${dependency_project_name}_LIBRARY_DIR}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}") ENDMACRO(add_dependency) From 5fd95c63473fc31bc70217443f9ece515e5b1d7f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 14 Sep 2016 18:00:55 +0200 Subject: [PATCH 104/786] added option to include the zqm library (to be tested) --- cmake/Modules/FindDOOCS.cmake | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 9955675..a59396e 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -3,7 +3,7 @@ # cmake module for finding DOOCS # # By default, only the client API is included. If the component "server" is specified, also the -# server library will be used. +# server library will be used. If the component "zmq" is specified, the DOOCSdzmq library will be used as well. # # returns: # DOOCS_FOUND : true or false, depending on whether the package was found @@ -32,18 +32,26 @@ SET(DOOCS_FOUND 0) -if("${DOOCS_FIND_COMPONENTS}" STREQUAL "server") +FIND_PATH(DOOCS_DIR libDOOCSapi.so + ${CMAKE_CURRENT_LIST_DIR} + /export/doocs/lib +) +set(DOOCS_LIBRARIES DOOCSapi nsl dl pthread m rt ldap) + +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") FIND_PATH(DOOCS_DIR libEqServer.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) - set(DOOCS_LIBRARIES EqServer DOOCSapi nsl dl pthread m rt ldap) -else() - FIND_PATH(DOOCS_DIR libDOOCSapi.so + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} EqServer) +endif() + +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") + FIND_PATH(DOOCS_DIR libDOOCSdzmq.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) - set(DOOCS_LIBRARIES DOOCSapi nsl dl pthread m rt ldap) + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSdzmq) endif() # now set the required variables based on the determined DOOCS_DIR @@ -55,11 +63,11 @@ set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") # extract DOOCS version from librar so symlink. Note: This is platform dependent and only works # if DOOCS was installed from the Debian pagackes. Find a better version detection scheme! -execute_process(COMMAND bash -c "readelf -d ${DOOCS_DIR}/libDOOCSapi.so | grep SONAME | sed -e 's/^.*Library soname: \\[libDOOCSapi\\.so\\.//' -e 's/\\]$//'" - OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) +execute_process(COMMAND bash -c "readlink ${DOOCS_DIR}/libDOOCSapi.so | sed -e 's/^.*libDOOCSapi.so.//' -e 's/-.*$//'" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) # use a macro provided by CMake to check if all the listed arguments are valid and set DOOCS_FOUND accordingly include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS REQUIRED_VARS DOOCS_DIR - VERSION_VAR DOOCS_VERSION ) + VERSION_VAR DOOCS_VERSION ) + From d061bd4d124643520f053d363ea46d4bf896c129 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 14 Sep 2016 18:16:43 +0200 Subject: [PATCH 105/786] fixed multiple components not properly being passed on to find_package --- cmake/add_dependency.cmake | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index c547715..ed3dbf4 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -26,13 +26,17 @@ # ####################################################################################################################### -MACRO(add_dependency dependency_project_name required_version components) - +FUNCTION(add_dependency dependency_project_name required_version) + # collect additional arguments and put into the list of components + SET(components "") + foreach(arg IN LISTS ARGN) + SET(components ${components} ${arg}) + endforeach() FIND_PACKAGE(${dependency_project_name} ${required_version} COMPONENTS ${components}) include_directories(SYSTEM ${${dependency_project_name}_INCLUDE_DIRS} ${${dependency_project_name}_INCLUDE_DIR}) link_directories(${${dependency_project_name}_LIBRARY_DIRS}) link_directories(${${dependency_project_name}_LIBRARY_DIR}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}") -ENDMACRO(add_dependency) +ENDFUNCTION(add_dependency) From e88d10c0fd572bad444166016e8d3ae307ad0e3d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 14 Sep 2016 18:20:49 +0200 Subject: [PATCH 106/786] fixed CXX_FLAGS etc. not properly set after last commit --- cmake/add_dependency.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index ed3dbf4..e292318 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -36,7 +36,9 @@ FUNCTION(add_dependency dependency_project_name required_version) include_directories(SYSTEM ${${dependency_project_name}_INCLUDE_DIRS} ${${dependency_project_name}_INCLUDE_DIR}) link_directories(${${dependency_project_name}_LIBRARY_DIRS}) link_directories(${${dependency_project_name}_LIBRARY_DIR}) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) + SET(${dependency_project_name}_LIBRARIES ${${dependency_project_name}_LIBRARIES} PARENT_SCOPE) + SET(${dependency_project_name}_LINK_FLAGS ${${dependency_project_name}_LINK_FLAGS} PARENT_SCOPE) ENDFUNCTION(add_dependency) From 8206728c737e2ccda6f04fd753b6ce0a1234c3df Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 16 Sep 2016 11:08:24 +0200 Subject: [PATCH 107/786] fixed that the script did not fail when one of the components was not found --- cmake/Modules/FindDOOCS.cmake | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index a59396e..e38c25f 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -39,7 +39,7 @@ FIND_PATH(DOOCS_DIR libDOOCSapi.so set(DOOCS_LIBRARIES DOOCSapi nsl dl pthread m rt ldap) if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") - FIND_PATH(DOOCS_DIR libEqServer.so + FIND_PATH(DOOCS_DIR_SERVER libEqServer.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) @@ -47,7 +47,7 @@ if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") endif() if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") - FIND_PATH(DOOCS_DIR libDOOCSdzmq.so + FIND_PATH(DOOCS_DIR_ZMQ libDOOCSdzmq.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) @@ -67,7 +67,11 @@ execute_process(COMMAND bash -c "readlink ${DOOCS_DIR}/libDOOCSapi.so | sed -e ' # use a macro provided by CMake to check if all the listed arguments are valid and set DOOCS_FOUND accordingly include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS - REQUIRED_VARS DOOCS_DIR - VERSION_VAR DOOCS_VERSION ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS REQUIRED_VARS DOOCS_DIR VERSION_VAR DOOCS_VERSION ) +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") + FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS REQUIRED_VARS DOOCS_DIR_SERVER VERSION_VAR DOOCS_VERSION ) +endif() +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") + FIND_PACKAGE_HANDLE_STANDARD_ARGS(DOOCS REQUIRED_VARS DOOCS_DIR_ZMQ VERSION_VAR DOOCS_VERSION ) +endif() From 0604fe67ece7679b0360e7c16adfdbf435461002 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 20 Sep 2016 13:43:36 +0200 Subject: [PATCH 108/786] added script to enable the C++ 11 support --- cmake/enable_cxx11_support.cmake | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 cmake/enable_cxx11_support.cmake diff --git a/cmake/enable_cxx11_support.cmake b/cmake/enable_cxx11_support.cmake new file mode 100644 index 0000000..848fac3 --- /dev/null +++ b/cmake/enable_cxx11_support.cmake @@ -0,0 +1,37 @@ +####################################################################################################################### +# enable_cxx11_support.cmake +# +# Enable C++-11 support by selecting the appropriate compiler flag. The flag will be appended to all of the following +# cmake variables: +# - CMAKE_CXX_FLAGS +# - ${PROJECT_NAME}_CXX_FLAGS +# - ${PROJECT_NAME}_CMAKE_CXX_FLAGS +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) +CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) +if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++11") + set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++11") +elseif(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++0x") + set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++0x") +else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") +endif() From aae2a1dd95c777e06ad12e526de04719f97f6cca Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 20 Sep 2016 17:34:56 +0200 Subject: [PATCH 109/786] changed order of libraries so that the dependencies come after the dependents. --- cmake/Modules/FindDOOCS.cmake | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index e38c25f..9fc5ecf 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -32,28 +32,28 @@ SET(DOOCS_FOUND 0) -FIND_PATH(DOOCS_DIR libDOOCSapi.so - ${CMAKE_CURRENT_LIST_DIR} - /export/doocs/lib -) -set(DOOCS_LIBRARIES DOOCSapi nsl dl pthread m rt ldap) - -if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") - FIND_PATH(DOOCS_DIR_SERVER libEqServer.so +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") + FIND_PATH(DOOCS_DIR_ZMQ libDOOCSdzmq.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) - set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} EqServer) + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSdzmq) endif() -if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") - FIND_PATH(DOOCS_DIR_ZMQ libDOOCSdzmq.so +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") + FIND_PATH(DOOCS_DIR_SERVER libEqServer.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) - set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSdzmq) + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} EqServer) endif() +FIND_PATH(DOOCS_DIR libDOOCSapi.so + ${CMAKE_CURRENT_LIST_DIR} + /export/doocs/lib +) +set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSapi nsl dl pthread m rt ldap) + # now set the required variables based on the determined DOOCS_DIR set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) From 1ca73180efb1a432e4e4a6d7fcd89d3c376903cf Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 20 Sep 2016 18:45:25 +0200 Subject: [PATCH 110/786] need to find DOOCS_DIR first, since this is the variable which might be specified at the cmake command line --- cmake/Modules/FindDOOCS.cmake | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 9fc5ecf..9a7a592 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -32,6 +32,11 @@ SET(DOOCS_FOUND 0) +FIND_PATH(DOOCS_DIR libDOOCSapi.so + ${CMAKE_CURRENT_LIST_DIR} + /export/doocs/lib +) + if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") FIND_PATH(DOOCS_DIR_ZMQ libDOOCSdzmq.so ${CMAKE_CURRENT_LIST_DIR} @@ -48,10 +53,6 @@ if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} EqServer) endif() -FIND_PATH(DOOCS_DIR libDOOCSapi.so - ${CMAKE_CURRENT_LIST_DIR} - /export/doocs/lib -) set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSapi nsl dl pthread m rt ldap) # now set the required variables based on the determined DOOCS_DIR From 56373d9ab7c139630177cc362079e03c6aaf38e5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 20 Sep 2016 18:48:25 +0200 Subject: [PATCH 111/786] look for the other DOOCS components in the same directory as the client --- cmake/Modules/FindDOOCS.cmake | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 9a7a592..308f442 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -32,23 +32,21 @@ SET(DOOCS_FOUND 0) + FIND_PATH(DOOCS_DIR libDOOCSapi.so ${CMAKE_CURRENT_LIST_DIR} /export/doocs/lib ) - if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") FIND_PATH(DOOCS_DIR_ZMQ libDOOCSdzmq.so - ${CMAKE_CURRENT_LIST_DIR} - /export/doocs/lib + ${DOOCS_DIR} ) set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSdzmq) endif() if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") FIND_PATH(DOOCS_DIR_SERVER libEqServer.so - ${CMAKE_CURRENT_LIST_DIR} - /export/doocs/lib + ${DOOCS_DIR} ) set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} EqServer) endif() From 88cb8918e334ad74b7c292c01ca7a6fa1bee7d9c Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 23 Sep 2016 11:25:20 +0200 Subject: [PATCH 112/786] Changed ProcessArray to derrive from NDRegisterAccessor. Compiles and runs, but does not use the data buffer, so the accesors won't work. --- CMakeLists.txt | 2 +- .../ControlSystemAdapter/ProcessArray.h | 23 +++++++++++-------- .../ControlSystemAdapter/ProcessVariable.h | 1 - 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eaad848..78180be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) message("Boost unit_test_framework not found, disabling testing") ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) -FIND_PACKAGE(mtca4u-deviceaccess 00.17 REQUIRED) +FIND_PACKAGE(mtca4u-deviceaccess 00.18 REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${mtca4u-deviceaccess_CXX_FLAGS}") include_directories(SYSTEM ${mtca4u-deviceaccess_INCLUDE_DIRS}) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 38c0cfb..bd2e732 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -15,7 +15,8 @@ #include #include -#include "ProcessVariable.h" +//#include "ProcessVariable.h" +#include #include "ProcessVariableListener.h" #include "TimeStampSource.h" @@ -28,7 +29,7 @@ namespace ChimeraTK { * should only be used from a single thread. */ template - class ProcessArray: public ProcessVariable { + class ProcessArray: public mtca4u::NDRegisterAccessor { public: @@ -69,10 +70,11 @@ namespace ChimeraTK { */ ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue) : - ProcessVariable(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _swappable(true), _buffers( - boost::make_shared >(1)), _currentIndex(0), _lastSentIndex( - 0) { + mtca4u::NDRegisterAccessor(name), + _instanceType(instanceType), _vectorSize(initialValue.size()), + _swappable(true), + _buffers(boost::make_shared >(1)), + _currentIndex(0), _lastSentIndex(0) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != STAND_ALONE) { @@ -102,7 +104,7 @@ namespace ChimeraTK { ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue, std::size_t numberOfBuffers, bool swappable) : - ProcessVariable(name), _instanceType(instanceType), _vectorSize( + mtca4u::NDRegisterAccessor(name), _instanceType(instanceType), _vectorSize( initialValue.size()), _swappable(swappable), _buffers( boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( boost::make_shared >( @@ -170,8 +172,9 @@ namespace ChimeraTK { boost::shared_ptr timeStampSource, boost::shared_ptr sendNotificationListener, boost::shared_ptr receiver) : - ProcessVariable(receiver->getName()), _instanceType(instanceType), _vectorSize( - receiver->_vectorSize), _swappable(swappable), _buffers( + mtca4u::NDRegisterAccessor(receiver->getName()), + _instanceType(instanceType), _vectorSize(receiver->_vectorSize), + _swappable(swappable), _buffers( receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( receiver), _timeStampSource(timeStampSource), _sendNotificationListener( @@ -467,7 +470,7 @@ namespace ChimeraTK { } virtual std::vector > getHardwareAccessingElements(){ - return { boost::enable_shared_from_this::shared_from_this() }; + return { boost::enable_shared_from_this::shared_from_this() }; } virtual void replaceTransferElement(boost::shared_ptr){ diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h index 3f5e897..9180eab 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h @@ -2,7 +2,6 @@ #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H #include -#include "TimeStamp.h" namespace ChimeraTK{ typedef mtca4u::TransferElement ProcessVariable; From a405932077fb9ef6478dd46946091df00e36f397 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 26 Sep 2016 11:02:03 +0200 Subject: [PATCH 113/786] removed scoped pointer in array implementation. C++11 supports vector::swap --- .../ControlSystemAdapter/ProcessArray.h | 31 +++++++------------ tests/src/testProcessArray.cpp | 5 ++- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 2b18895..3928d7e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -87,11 +87,8 @@ namespace ChimeraTK { "This constructor may only be used for a stand-alone process scalar."); } // We have to initialize the buffer by creating a vector of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). - boost::scoped_ptr > v(new std::vector(initialValue)); - (*_buffers)[0].value.swap(v); + // appropriate size. + (*_buffers)[0].value=initialValue; } /** @@ -135,14 +132,10 @@ namespace ChimeraTK { if (numberOfBuffers > (std::numeric_limits::max() - 2)) { throw std::invalid_argument("The number of buffers is too large."); } - // We have to initialize the buffers by creating vectors of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). + // We have to initialize the buffers by copying in the initial vectors. for (typename std::vector::iterator i = _buffers->begin(); i != _buffers->end(); ++i) { - boost::scoped_ptr > v(new std::vector(initialValue)); - i->value.swap(v); + i->value=initialValue; } // The buffer with the index 0 is assigned to the receiver and the // buffer with the index 1 is assigned to the sender. All buffers have @@ -250,8 +243,8 @@ namespace ChimeraTK { * and must point to a vector that has the same size as the vector it is * swapped with. */ - void swap(boost::scoped_ptr > & otherVector) { - if (otherVector->size() != get().size()) { + void swap(std::vector & otherVector) { + if (otherVector.size() != get().size()) { throw std::runtime_error("Vector sizes do not match"); } (*_buffers)[_currentIndex].value.swap(otherVector); @@ -266,7 +259,7 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector & get() { - return *(((*_buffers)[_currentIndex]).value); + return ((*_buffers)[_currentIndex]).value; } /** @@ -279,7 +272,7 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector const & get() const { - return *(((*_buffers)[_currentIndex]).value); + return ((*_buffers)[_currentIndex]).value; } virtual bool isReadable() const { @@ -333,7 +326,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); } @@ -479,7 +472,7 @@ namespace ChimeraTK { struct Buffer { TimeStamp timeStamp; - boost::scoped_ptr > value; + std::vector value; VersionNumber versionNumber; /** @@ -595,7 +588,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); } @@ -629,7 +622,7 @@ namespace ChimeraTK { } } if (shouldCopy) { - *((*_buffers)[nextIndex].value) = *((*_buffers)[_currentIndex].value); + (*_buffers)[nextIndex].value = (*_buffers)[_currentIndex].value; (*_buffers)[nextIndex].timeStamp = newTimeStamp; (*_buffers)[nextIndex].versionNumber = newVersionNumber; } diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index b9b42f9..3f186d8 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -257,14 +257,13 @@ namespace ChimeraTK { void ProcessArrayTest::testSwap() { typename ProcessArray::SharedPtr simpleArray = createSimpleProcessArray(N_ELEMENTS); - typename boost::scoped_ptr > v( - new std::vector(N_ELEMENTS, SOME_NUMBER)); + typename std::vector v(N_ELEMENTS, SOME_NUMBER); simpleArray->swap(v); for (typename std::vector::iterator i = simpleArray->get().begin(); i != simpleArray->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - for (typename std::vector::iterator i = v->begin(); i != v->end(); ++i) { + for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == 0); } // For the synchronized variant we only test that the swap method does not From 374656f604e792049fa3349286f3cd9cf99768f2 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 26 Sep 2016 11:02:03 +0200 Subject: [PATCH 114/786] removed scoped pointer in array implementation. C++11 supports vector::swap --- .../ControlSystemAdapter/ProcessArray.h | 31 +++++++------------ tests/src/testProcessArray.cpp | 5 ++- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 7a2cb16..c7e6692 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -86,11 +86,8 @@ namespace ChimeraTK { "This constructor may only be used for a stand-alone process scalar."); } // We have to initialize the buffer by creating a vector of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). - boost::scoped_ptr > v(new std::vector(initialValue)); - (*_buffers)[0].value.swap(v); + // appropriate size. + (*_buffers)[0].value=initialValue; } /** @@ -134,14 +131,10 @@ namespace ChimeraTK { if (numberOfBuffers > (std::numeric_limits::max() - 2)) { throw std::invalid_argument("The number of buffers is too large."); } - // We have to initialize the buffers by creating vectors of the - // appropriate size. We have to use swap because scoped_ptr does not - // allow assignment (it is compatible with C++ 98 which does not have - // move semantics). + // We have to initialize the buffers by copying in the initial vectors. for (typename std::vector::iterator i = _buffers->begin(); i != _buffers->end(); ++i) { - boost::scoped_ptr > v(new std::vector(initialValue)); - i->value.swap(v); + i->value=initialValue; } // The buffer with the index 0 is assigned to the receiver and the // buffer with the index 1 is assigned to the sender. All buffers have @@ -248,8 +241,8 @@ namespace ChimeraTK { * and must point to a vector that has the same size as the vector it is * swapped with. */ - void swap(boost::scoped_ptr > & otherVector) { - if (otherVector->size() != get().size()) { + void swap(std::vector & otherVector) { + if (otherVector.size() != get().size()) { throw std::runtime_error("Vector sizes do not match"); } (*_buffers)[_currentIndex].value.swap(otherVector); @@ -264,7 +257,7 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector & get() { - return *(((*_buffers)[_currentIndex]).value); + return ((*_buffers)[_currentIndex]).value; } /** @@ -277,7 +270,7 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector const & get() const { - return *(((*_buffers)[_currentIndex]).value); + return ((*_buffers)[_currentIndex]).value; } bool isReceiver() const { @@ -323,7 +316,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); } @@ -457,7 +450,7 @@ namespace ChimeraTK { struct Buffer { TimeStamp timeStamp; - boost::scoped_ptr > value; + std::vector value; VersionNumber versionNumber; /** @@ -573,7 +566,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value->size() != _vectorSize) { + if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); } @@ -608,7 +601,7 @@ namespace ChimeraTK { } } if (shouldCopy) { - *((*_buffers)[nextIndex].value) = *((*_buffers)[_currentIndex].value); + (*_buffers)[nextIndex].value = (*_buffers)[_currentIndex].value; (*_buffers)[nextIndex].timeStamp = newTimeStamp; (*_buffers)[nextIndex].versionNumber = newVersionNumber; } diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index fd5c429..bd578a7 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -257,14 +257,13 @@ namespace ChimeraTK { void ProcessArrayTest::testSwap() { typename ProcessArray::SharedPtr simpleArray = createSimpleProcessArray(N_ELEMENTS); - typename boost::scoped_ptr > v( - new std::vector(N_ELEMENTS, SOME_NUMBER)); + typename std::vector v(N_ELEMENTS, SOME_NUMBER); simpleArray->swap(v); for (typename std::vector::iterator i = simpleArray->get().begin(); i != simpleArray->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - for (typename std::vector::iterator i = v->begin(); i != v->end(); ++i) { + for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == 0); } // For the synchronized variant we only test that the swap method does not From bd3688c721d54798b9f12e7c530f22ff0754eabe Mon Sep 17 00:00:00 2001 From: Filip Makowski Date: Thu, 13 Oct 2016 15:46:05 +0200 Subject: [PATCH 115/786] Added new `dapi` component in FindDOOCS.cmake module. --- cmake/Modules/FindDOOCS.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 308f442..79bdfd0 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -44,6 +44,13 @@ if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";zmq;") set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSdzmq) endif() +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";dapi;") + FIND_PATH(DOOCS_DIR_DAPI libDOOCSdapi.so + ${DOOCS_DIR} + ) + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSdapi) +endif() + if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") FIND_PATH(DOOCS_DIR_SERVER libEqServer.so ${DOOCS_DIR} From c8bd3e541524d42e5b9860f3622e3ecf02aa808d Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 3 Nov 2016 09:53:39 +0100 Subject: [PATCH 116/786] added script to enable coverage report --- cmake/enable_code_coverage_report.cmake | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 cmake/enable_code_coverage_report.cmake diff --git a/cmake/enable_code_coverage_report.cmake b/cmake/enable_code_coverage_report.cmake new file mode 100644 index 0000000..44c270d --- /dev/null +++ b/cmake/enable_code_coverage_report.cmake @@ -0,0 +1,40 @@ +####################################################################################################################### +# enable_code_coverage_report.cmake +# +# Enable possibility to generate a code coverage report when compiling in 'Debug' mode. +# Configure with the option -DCMAKE_BUILD_TYPE=Debug. +# It require working tests which can be called with 'make test'. The coverage is created +# over these tests. +# +# You need lcov installed to use the 'make coverage' command. +# +# This script appends to the variable CMAKE_CXX_FLAGS_DEBUG, +# and adds the target 'coverage', only available in 'Debug' mode. +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 --coverage") + +#The make coverage command is only available in debug mode +IF(CMAKE_BUILD_TYPE STREQUAL "Debug") + configure_file(cmake/make_coverage.sh.in + ${PROJECT_BINARY_DIR}/make_coverage.sh @ONLY) + add_custom_target(coverage + ./make_coverage.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating test coverage documentation" VERBATIM + ) +ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") + From ee1e311124c65e7cf2a8caabff604092f5c1a3e5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 4 Nov 2016 14:53:40 +0100 Subject: [PATCH 117/786] added device acces libraries to the list of libraries needed to link when using this project --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78180be..700f006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include #Only the reference header is needed. The other variables are for dependencies of this library, #but there are none. set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIRS}") -set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY}") +set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}") set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS}") set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11") set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags") From 7a5420ecd97a0116e14680450cfa2fe63493fd4c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 4 Nov 2016 16:31:02 +0100 Subject: [PATCH 118/786] Fixed not using the buffer of the NDRegisterAccessor base class. Now the NDRegisterAccessor interface should be properly implemented. Some bloat might be removed, though, since it is already implemented in the base class. --- .../ControlSystemAdapter/ProcessArray.h | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 3928d7e..2a71290 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -86,9 +86,9 @@ namespace ChimeraTK { throw std::invalid_argument( "This constructor may only be used for a stand-alone process scalar."); } - // We have to initialize the buffer by creating a vector of the - // appropriate size. - (*_buffers)[0].value=initialValue; + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; } /** @@ -110,6 +110,9 @@ namespace ChimeraTK { boost::make_shared >( numberOfBuffers)), _currentIndex(0), _lastSentIndex(0), _versionNumberSource( versionNumberSource) { + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != RECEIVER) { @@ -174,6 +177,9 @@ namespace ChimeraTK { receiver), _timeStampSource(timeStampSource), _versionNumberSource( versionNumberSource), _sendNotificationListener( sendNotificationListener) { + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != SENDER) { @@ -247,7 +253,7 @@ namespace ChimeraTK { if (otherVector.size() != get().size()) { throw std::runtime_error("Vector sizes do not match"); } - (*_buffers)[_currentIndex].value.swap(otherVector); + get().swap(otherVector); } /** @@ -259,7 +265,7 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector & get() { - return ((*_buffers)[_currentIndex]).value; + return mtca4u::NDRegisterAccessor::buffer_2D[0]; } /** @@ -272,7 +278,7 @@ namespace ChimeraTK { * invalid reference results in undefined behavior. */ std::vector const & get() const { - return ((*_buffers)[_currentIndex]).value; + return mtca4u::NDRegisterAccessor::buffer_2D[0]; } virtual bool isReadable() const { @@ -339,6 +345,7 @@ namespace ChimeraTK { || ((*_buffers)[nextIndex]).versionNumber > getVersionNumber()) { _emptyBufferQueue->push(_currentIndex); _currentIndex = nextIndex; + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( ((*_buffers)[_currentIndex]).value ); return true; } else { _emptyBufferQueue->push(nextIndex); @@ -588,7 +595,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { + if (mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); } @@ -622,10 +629,13 @@ namespace ChimeraTK { } } if (shouldCopy) { - (*_buffers)[nextIndex].value = (*_buffers)[_currentIndex].value; + (*_buffers)[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; (*_buffers)[nextIndex].timeStamp = newTimeStamp; (*_buffers)[nextIndex].versionNumber = newVersionNumber; } + else { + (*_buffers)[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + } _lastSentIndex = _currentIndex; _currentIndex = nextIndex; if (_sendNotificationListener) { From 8a9977d1c5d25dfb806fbda8ec72995cfd04ba85 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 7 Nov 2016 14:00:35 +0100 Subject: [PATCH 119/786] bug fix: the buffer was filled after sending the index to the queue, which obviously destroys the memory barrier. --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 2a71290..0a01be9 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -607,6 +607,12 @@ namespace ChimeraTK { TimeStamp::currentTime(); ((*_buffers)[_currentIndex]).timeStamp = newTimeStamp; ((*_buffers)[_currentIndex]).versionNumber = newVersionNumber; + if (shouldCopy) { + (*_buffers)[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + } + else { + (*_buffers)[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + } std::size_t nextIndex; if (_emptyBufferQueue->pop(nextIndex)) { _fullBufferQueue->push(_currentIndex); @@ -629,13 +635,9 @@ namespace ChimeraTK { } } if (shouldCopy) { - (*_buffers)[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; (*_buffers)[nextIndex].timeStamp = newTimeStamp; (*_buffers)[nextIndex].versionNumber = newVersionNumber; } - else { - (*_buffers)[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); - } _lastSentIndex = _currentIndex; _currentIndex = nextIndex; if (_sendNotificationListener) { From 6ca507bddb05a85b7172928f9b202bf4a5c8c355 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 7 Nov 2016 18:04:22 +0100 Subject: [PATCH 120/786] - Removed the ProcessScalar entirely (has been superseeded by the ProcessArray) - Removed the Process*Accessor classes entirely (use DeviceAccess::*RegisterAccessor classes instead) --- .../ControlSystemPVManager.h | 24 +- .../ControlSystemAdapter/DevicePVManager.h | 56 -- .../ControlSystemAdapter/PVManager.h | 159 ------ .../ProcessArrayAccessor.h | 70 --- .../ControlSystemAdapter/ProcessScalar.h | 538 ------------------ .../ProcessScalarAccessor.h | 75 --- .../ProcessVariableAccessor.h | 140 ----- .../Testing/IndependentTestCore.h | 20 +- tests/src/fullStubTest.cpp | 28 +- ...estControlSystemSynchronizationUtility.cpp | 164 +++--- .../src/testDeviceSynchronizationUtility.cpp | 175 +++--- tests/src/testPVAccessors.cpp | 262 --------- tests/src/testPVManager.cpp | 269 +++++---- tests/src/testProcessScalar.cpp | 302 ---------- tests/src/testReferenceTestCoreWithTread.cpp | 12 +- 15 files changed, 322 insertions(+), 1972 deletions(-) delete mode 100644 include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h delete mode 100644 include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h delete mode 100644 include/ChimeraTK/ControlSystemAdapter/ProcessScalarAccessor.h delete mode 100644 include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h delete mode 100644 tests/src/testPVAccessors.cpp delete mode 100644 tests/src/testProcessScalar.cpp diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 8c95389..524a1a0 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -52,20 +52,6 @@ namespace ChimeraTK { */ typedef boost::shared_ptr SharedPtr; - /** - * Returns a reference to a process scalar that has been created earlier - * using the - * {@link DevicePVManager::createProcessScalar(SynchronizationDirection, const std::string&, T, std::size_t)} - * method. - * Returns a pointer to null if there is no process variable or - * array with the specified name. Throws a bad_cast exception if there is a - * process variable or array with the specified name but its type does not - * match. - */ - template - typename ProcessScalar::SharedPtr getProcessScalar( - const std::string& processVariableName) const; - /** * Returns a reference to a process array that has been created earlier * using the @@ -84,7 +70,6 @@ namespace ChimeraTK { /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link DevicePVManager::createProcessScalar(SynchronizationDirection, const std::string&, T, std::size_t)}, * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)}, * or * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} @@ -95,8 +80,7 @@ namespace ChimeraTK { * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid * non-working links: * - * The {@link getProcessScalar(const std::string&)} and - * {@link getProcessArray(const std:.string&)} methods should be preferred + * The {@link getProcessArray(const std:.string&)} methods should be preferred */ /** The \c getProcessScalar(const std::string&) and * \c getProcessArray(const std:.string&) methods should be preferred @@ -137,12 +121,6 @@ namespace ChimeraTK { }; - template - typename ProcessScalar::SharedPtr ControlSystemPVManager::getProcessScalar( - const std::string& processVariableName) const { - return _pvManager->getProcessScalar(processVariableName).first; - } - template typename ProcessArray::SharedPtr ControlSystemPVManager::getProcessArray( const std::string& processVariableName) const { diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 5f5e2c6..57d5e76 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -57,27 +57,6 @@ namespace ChimeraTK { */ typedef boost::shared_ptr SharedPtr; - /** - * Creates a new process scalar and registers it with the PV manager. - * Creating a process scalar with a name that is already used for a - * different process scalar or array is an error and causes an - * \c std::invalid_argument exception to be thrown. - * - * The number of buffers (the minimum and default value is one) is the max. - * number of values that can be queued in the transfer queue. Specifying a - * larger number make loss of data less likely but increases the memory - * footprint. - * - * Two process variables are created: one for the control system and one for - * the device library. The one that is returned is the one that should be - * used by the device library. - */ - template - typename ProcessScalar::SharedPtr createProcessScalar( - SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, T initialValue = 0, - std::size_t numberOfBuffers = 1); - /** * Creates a new process array and registers it with the PV manager. * Creating a process array with a name that is already used for a different @@ -136,18 +115,6 @@ namespace ChimeraTK { const std::vector& initialValue, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); - /** - * Returns a reference to a process scalar that has been created earlier - * using the - * {@link createProcessScalar(SynchronizationDirection, const std::string&, T, std::size_t)} - * method. Returns a pointer to null if there is no process - * scalar or array with the specified name. Throws a bad_cast exception if - * there is a process scalar or array with the specified name but its type - * does not match. - */ - template typename ProcessScalar::SharedPtr getProcessScalar( - const std::string& processVariableName) const; - /** * Returns a reference to a process array that has been created earlier * using the @@ -255,23 +222,6 @@ namespace ChimeraTK { }; - template - typename ProcessScalar::SharedPtr DevicePVManager::createProcessScalar( - SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, T initialValue, - std::size_t numberOfBuffers) { - switch (synchronizationDirection) { - case controlSystemToDevice: - return _pvManager->createProcessScalarControlSystemToDevice( - processVariableName, initialValue, numberOfBuffers).second; - case deviceToControlSystem: - return _pvManager->createProcessScalarDeviceToControlSystem( - processVariableName, initialValue, numberOfBuffers).second; - default: - throw std::invalid_argument("invalid SynchronizationDirection"); - } - } - template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, @@ -311,12 +261,6 @@ namespace ChimeraTK { } } - template - typename ProcessScalar::SharedPtr DevicePVManager::getProcessScalar( - const std::string& processVariableName) const { - return _pvManager->getProcessScalar(processVariableName).second; - } - template typename ProcessArray::SharedPtr DevicePVManager::getProcessArray( const std::string& processVariableName) const { diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 13364f7..3ae065f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -11,7 +11,6 @@ #include #include "ProcessArray.h" -#include "ProcessScalar.h" #include "PVManagerDecl.h" @@ -60,52 +59,6 @@ namespace ChimeraTK { */ typedef boost::unordered_map ProcessVariableMap; - /** - * Creates a new process scalar for transferring data from the device - * library to the control system and registers it with the PV manager. - * Creating a process scalar with a name that is already used for a - * different process scalar or array is an error and causes an - * \c std::invalid_argument exception to be thrown. - * - * The number of buffers (the minimum and default value is one) is the max. - * number of values that can be queued in the transfer queue. Specifying a - * larger number make loss of data less likely but increases the memory - * footprint. - * - * Two process variables are created: one for the control system and one for - * the device library. The pair that is returned has a reference to the - * instance intended for the control system as its first and a reference to - * the instance intended for the device library as its second member. - */ - template - std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> createProcessScalarDeviceToControlSystem( - const std::string& processVariableName, T initialValue = 0, - std::size_t numberOfBuffers = 1); - - /** - * Creates a new process scalar for transferring data from the control - * system to the device library and registers it with the PV manager. - * Creating a process scalar with a name that is already used for a - * different process scalar or array is an error and causes an - * \c std::invalid_argument exception to be thrown. - * - * The number of buffers (the minimum and default value is one) is the max. - * number of values that can be queued in the transfer queue. Specifying a - * larger number make loss of data less likely but increases the memory - * footprint. - * - * Two process variables are created: one for the control system and one for - * the device library. The pair that is returned has a reference to the - * instance intended for the control system as its first and a reference to - * the instance intended for the device library as its second member. - */ - template - std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> createProcessScalarControlSystemToDevice( - const std::string& processVariableName, T initialValue = 0, - std::size_t numberOfBuffers = 1); - /** * Creates a new process array for transferring data from the device library * to the control system and registers it with the PV manager. @@ -172,24 +125,6 @@ namespace ChimeraTK { const std::vector& initialValue, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); - /** - * Returns a reference to a process scalar that has been created earlier - * using one of the createProcessScalar... methods. - * Returns a pointer to null if there is no process scalar or - * array with the specified name. Throws a bad_cast exception if there is a - * process scalar or array with the specified name but its type does not - * match. - * - * A pair of two process variables is returned: The first member of the pair - * is a reference to the instance intended for the control system and the - * second member of the pair is a reference to the instance intended for the - * device library. - */ - template - std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> getProcessScalar( - const std::string& processVariableName) const; - /** * Returns a reference to a process array that has been created earlier * using one of the createProcessArray... methods. Returns a @@ -494,79 +429,6 @@ namespace ChimeraTK { std::pair, boost::shared_ptr > createPVManager(); - template - std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> PVManager::createProcessScalarDeviceToControlSystem( - const std::string& processVariableName, T initialValue, - std::size_t numberOfBuffers) { - if (_processVariables.find(processVariableName) - != _processVariables.end()) { - throw std::invalid_argument( - "Process variable with name " + processVariableName - + " already exists."); - } - - boost::shared_ptr timeStampSource = boost::make_shared< - TimeStampSourceImpl>(shared_from_this()); - boost::shared_ptr sendNotificationListener = - boost::make_shared( - shared_from_this()); - - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> processVariables = - createSynchronizedProcessScalar(processVariableName, initialValue, - numberOfBuffers, timeStampSource, _versionNumberSource, - sendNotificationListener); - - _processVariables.insert( - std::make_pair(processVariableName, - std::make_pair(processVariables.second, processVariables.first))); - - // Increase notification queue size by one to make space for this process - // variable. We can use the unsafe variant because calls to this method have - // to be synchronized anyway. - _controlSystemNotificationQueue.reserve_unsafe(1); - - return std::make_pair(processVariables.second, processVariables.first); - } - - template - std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> PVManager::createProcessScalarControlSystemToDevice( - const std::string& processVariableName, T initialValue, - std::size_t numberOfBuffers) { - if (_processVariables.find(processVariableName) - != _processVariables.end()) { - throw std::invalid_argument( - "Process variable with name " + processVariableName - + " already exists."); - } - - // We do not use a time-stamp source for process variables that are - // synchronized from the control system to the device. - boost::shared_ptr timeStampSource; - boost::shared_ptr sendNotificationListener = - boost::make_shared( - shared_from_this()); - - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> processVariables = - createSynchronizedProcessScalar(processVariableName, initialValue, - numberOfBuffers, timeStampSource, _versionNumberSource, - sendNotificationListener); - - _processVariables.insert( - std::make_pair(processVariableName, - std::make_pair(processVariables.first, processVariables.second))); - - // Increase notification queue size by one to make space for this process - // variable. We can use the unsafe variant because calls to this method have - // to be synchronized anyway. - _deviceNotificationQueue.reserve_unsafe(1); - - return std::make_pair(processVariables.first, processVariables.second); - } - template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( @@ -642,27 +504,6 @@ namespace ChimeraTK { return std::make_pair(processVariables.first, processVariables.second); } - template - std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> PVManager::getProcessScalar( - const std::string& processVariableName) const { - ProcessVariableSharedPtrPair processVariable = getProcessVariable( - processVariableName); - if (processVariable.first && processVariable.second) { - typename ProcessScalar::SharedPtr csPV = boost::dynamic_pointer_cast< - ProcessScalar, ProcessVariable>(processVariable.first); - typename ProcessScalar::SharedPtr devPV = boost::dynamic_pointer_cast< - ProcessScalar, ProcessVariable>(processVariable.second); - if (!csPV || !devPV) { - throw std::bad_cast(); - } - return std::make_pair(csPV, devPV); - } else { - return std::make_pair(typename ProcessScalar::SharedPtr(), - typename ProcessScalar::SharedPtr()); - } - } - template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::getProcessArray( diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h deleted file mode 100644 index 6b8c874..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArrayAccessor.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_ACCESSOR_H -#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_ACCESSOR_H - -#include "ProcessArray.h" -#include "ProcessVariableAccessor.h" - -namespace ChimeraTK{ - template - class ProcessArrayAccessor: public ProcessVariableAccessor{ - - public: - ProcessArrayAccessor(typename ProcessArray::SharedPtr const & processArray = - typename ProcessArray::SharedPtr()) - : ProcessVariableAccessor(processArray){ - // _processArray( boost::static_pointer_cast< ProcessArray >( _impl ) ){ - } - - std::vector & get(){ - return boost::static_pointer_cast< ProcessArray >(_impl)->get(); - } - std::vector const & get() const{ - return boost::static_pointer_cast< ProcessArray >(_impl)->get(); - } - std::vector const & getConst() const{ - return boost::static_pointer_cast< ProcessArray >(_impl)->get(); - } - - UserType& operator[](unsigned int element) { - return get()[element]; - } - - /** Access data with std::vector-like iterators */ - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; - typedef typename std::vector::reverse_iterator reverse_iterator; - typedef typename std::vector::const_reverse_iterator const_reverse_iterator; - - iterator begin() { return get().begin(); } - const_iterator begin() const { return getConst().cbegin(); } - const_iterator cbegin() const { return getConst().cbegin(); } - iterator end() { return get().end(); } - const_iterator end() const { return getConst().cend(); } - const_iterator cend() const { return getConst().cend(); } - reverse_iterator rbegin() { return get().rbegin(); } - const_reverse_iterator rbegin() const { return getConst().crbegin(); } - const_reverse_iterator crbegin() const { return getConst().crbegin(); } - reverse_iterator rend() { return get().rend(); } - const_reverse_iterator rend() const { return getConst().crend(); } - const_reverse_iterator crend() const { return getConst().crend(); } - - size_t size() const{ return get().size(); } - - /* Swap content of (cooked) buffer with std::vector */ - void swap(std::vector &x) { - get().swap(x); - } - - UserType* data() { - return get().data(); - } - UserType const * data() const{ - return get().data(); - } - - - }; - -} //namespace ChimeraTK - -#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_ACCESSOR_H diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h b/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h deleted file mode 100644 index 61a90ad..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessScalar.h +++ /dev/null @@ -1,538 +0,0 @@ -#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_H -#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_H - -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ProcessVariable.h" -#include "ProcessVariableListener.h" -#include "TimeStampSource.h" -#include "VersionNumberSource.h" - -namespace ChimeraTK { - - /** - * The scalar implementation of a ProcessVariable. This implementation is used for all - * three use cases (sender, receiver, and stand-alone). - * - * This class is not thread-safe and should only be used from a single thread. - */ - template - class ProcessScalar: public ProcessVariable { - - public: - /** - * Type of the instance. This defines the behavior (send or receive - * possible, modifications allowed, etc.). This enum type is private so - * that only friend functions can construct instances of this class. The - * constructors themselves have to be public so that boost::make_shared - * can be used. - */ - enum InstanceType { - /** - * Instance acts on is own. - */ - STAND_ALONE, - - /** - * Instance acts as the sender in a sender / receiver pair. - */ - SENDER, - - /** - * Instance acts as the receiver in a sender / receiver pair. - */ - RECEIVER - }; - - /** - * Type alias for a shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Creates a process scalar that works independently. This means that the - * instance is not synchronized with any other instance and thus the send - * and receive operations are not supported. However, all other operations - * can be used like on any process variable. - */ - ProcessScalar(InstanceType instanceType, const std::string& name, - T initialValue) : - ProcessVariable(name), _instanceType(instanceType), _value( - initialValue), _versionNumber(0) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != STAND_ALONE) { - throw std::invalid_argument( - "This constructor may only be used for a stand-alone process scalar."); - } - } - - /** - * Creates a process scalar that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. - */ - ProcessScalar(InstanceType instanceType, const std::string& name, - T initialValue, std::size_t numberOfBuffers, - VersionNumberSource::SharedPtr versionNumberSource) : - ProcessVariable(name), _instanceType(instanceType), _value( - initialValue), _versionNumber(0), _bufferQueue( - boost::make_shared >( - numberOfBuffers)), _versionNumberSource(versionNumberSource) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument( - "This constructor may only be used for a receiver process scalar."); - } - if (numberOfBuffers < 1) { - throw std::invalid_argument( - "The number of buffers must be at least one."); - } - } - - /** - * Creates a process scalar that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the swappable option is true, the swap operation is - * allowed on the receiver. The swappable flag must be the - * same for the sender and receiver. Allowing swapping has the side effect - * that a value cannot be read after having been sent. - * - * The optional send-notification listener is notified every time the - * sender's {@link ProcessScalar::send()} method is called. It can be - * used to queue a request for the receiver's - * {@link ProcessScalar::receive()} method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - ProcessScalar(InstanceType instanceType, - TimeStampSource::SharedPtr timeStampSource, - VersionNumberSource::SharedPtr versionNumberSource, - ProcessVariableListener::SharedPtr sendNotificationListener, - ProcessScalar::SharedPtr receiver) : - ProcessVariable(receiver->getName()), _instanceType(instanceType), _timeStamp( - receiver->_timeStamp), _value(receiver->_value), _versionNumber(0), _bufferQueue( - receiver->_bufferQueue), _receiver(receiver), _timeStampSource( - timeStampSource), _versionNumberSource(versionNumberSource), _sendNotificationListener( - sendNotificationListener) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument( - "This constructor may only be used for a sender process scalar."); - } - if (!receiver) { - throw std::invalid_argument( - "The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - } - - /** - * Assign the content of another process variable of type T to this one. - * It only assigns the variable content, but not the callback functions. - * This operator behaves like set(). - * - * FIXME: Should we remove this? It might not behave as - * expected, and the base class is intentionally non-copyable. - */ - ProcessScalar & operator=(ProcessScalar const & other) { - set(other); - return (*this); - } - - /** - * Assign the content of the template type. This operator behaves like - * set(). - */ - ProcessScalar & operator=(T const & t) { - set(t); - return *this; - } - - /** - * Automatic conversion operator which returns a \b copy of this process - * variable's value. As no reference is returned, this cannot be used for - * assignment. - */ - operator T() const { - return get(); - } - - /** - * Set the value of this process variable to the specified one. This does - * not trigger the on-set callback function, however it notifies the control - * system that this process variable's value has changed. - */ - void set(T const & t) { - _value = t; - } - - /** - * Sets and sends the value of this process variable only if the version - * number of the new value is greater than the version number of the current - * value. - * - * This is a convenience method that is provided for when the value of a - * process variable is updated with a value calculated from the value of a - * different process variable. Calling this method is equivalent to the - * following code: - * - *
-     * if (newVersionNumber > getVersionNumber()) {
-     *   set(t);
-     *   send(newVersionNumber);
-     * }
-     * 
- * - * By using this method, one can easily avoid overwriting a more recent - * value with an older value, which could otherwise happen due to updates - * being handled asynchronously. - */ - void setAndWriteIfNewVersionGreater(T const & t, VersionNumber newVersionNumber) { - if (newVersionNumber > getVersionNumber()) { - set(t); - write(newVersionNumber); - } - } - - // FIXME: this belongs to ProcessScalar, not the Impl. Moved here to remove the impl - // inheritence for creating the pimpl pattern, where impl and not are typedefed. - const std::type_info& getValueType() const { - return typeid(T); - } - - bool isArray() const { - return false; - } - - /** - * Set the value of this process variable to the one of the other process - * variable. This does not trigger the on-set callback function, however it - * notifies the control system that this process variable's value has - * changed. - */ - void set(ProcessScalar const & other) { - set(other.get()); - } - - /** - * Returns a \b copy of this process variable's value. As no reference is - * returned, this cannot be used for assignment. - */ - T get() const { - return _value; - } - - // ----------------------------------------------- - // Implementations of the derrived functions. They are documented in ProcessVariable - - virtual bool isReadable() const { - return _instanceType == RECEIVER; - } - - virtual bool isWriteable() const { - return _instanceType == SENDER; - } - - virtual bool isReadOnly() const { - return !isWriteable(); - } - - // deprecated - TimeStamp getTimeStamp() const { - return _timeStamp; - } - - /** - * Returns the version number that is associated with the current value. - * This is the version number that was received with the last - * {@link receive()} operation or sent with the last {@link send()} - * operation (whichever happened later). - * - * The version number is used to resolve conflicting updates. When an update - * is received using the {@link receive()} method, it is only used if its - * value has a version number that is greater than the version number of the - * current value. Initially, each process variable has a version number of - * zero. - * - * When this process variable has not been initialized with a version number - * source, its version number always stays at zero and the version-number - * logic is disabled. - */ - VersionNumber getVersionNumber() const { - return _versionNumber; - } - - virtual bool readNonBlocking() { - if (_instanceType != RECEIVER) { - throw std::logic_error( - "Receive operation is only allowed for a receiver process variable."); - } - Buffer nextBuffer; - if (_bufferQueue->pop(nextBuffer)) { - if (!_versionNumberSource - || nextBuffer.versionNumber > getVersionNumber()) { - _timeStamp = nextBuffer.timeStamp; - _value = nextBuffer.value; - _versionNumber = nextBuffer.versionNumber; - return true; - } else { - return false; - } - } else { - return false; - } - } - - virtual void read(){ - throw std::logic_error("Blocking read is not supported by process scalar."); - - } - - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * If this process variable has a version-number source, a new version - * number is retrieved from this source and used for the value being sent to - * the receiver. Otherwise, a version number of zero is used. - * - * Throws an exception if this process variable is not a sender. - */ - virtual void write() { - VersionNumber newVersionNumber; - if (_versionNumberSource) { - newVersionNumber = _versionNumberSource->nextVersionNumber(); - } else { - newVersionNumber = 0; - } - write(newVersionNumber); - } - - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * The specified version number is passed to the receiver. If the receiver - * has a value with a version number greater than or equal to the specified - * version number, it silently discards this update. - * - * Throws an exception if this process variable is not a sender. - */ - void write(VersionNumber newVersionNumber) { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable."); - } - // Before sending the value, we have to update our time stamp. - _timeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - Buffer nextBuffer; - nextBuffer.timeStamp = _timeStamp; - nextBuffer.value = _value; - nextBuffer.versionNumber = newVersionNumber; - _versionNumber = newVersionNumber; - if (!_bufferQueue->bounded_push(nextBuffer)) { - // sending failed because the queue is full. We have to pop a buffer to make space. - - // We are not interested in the old value, but we have to provide - // a reference. - Buffer oldBuffer; - _bufferQueue->pop(oldBuffer); - // Now we can be sure that the push is successful. - _bufferQueue->bounded_push(nextBuffer); - } - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - } - - virtual bool isSameRegister(const boost::shared_ptr& e) const{ - // only true if the very instance of the transfer element is the same - return e.get() == this; - } - - virtual std::vector > getHardwareAccessingElements(){ - return { boost::enable_shared_from_this::shared_from_this() }; - } - - virtual void replaceTransferElement(boost::shared_ptr){ - // You can't replace anything here. Just do nothing. - } - - private: - /** - * Type for the individual buffers. Each buffer stores a value and a time - * stamp. - */ - struct Buffer { - TimeStamp timeStamp; - T value; - VersionNumber versionNumber; - }; - - /** - * Type this instance is representing. - */ - InstanceType _instanceType; - - /** - * Time stamp of the current value. - */ - TimeStamp _timeStamp; - - /** - * Current value. - */ - T _value; - - /** - * Version number associated with the value. - */ - VersionNumber _versionNumber; - - /** - * Queue holding the values that have been sent but not received yet. - * We do not use an spsc_queue for this queue, because might want to take - * elements from the sending thread, so there are two threads which might - * consume elements and thus an spsc_queue is not safe. - */ - boost::shared_ptr > _bufferQueue; - - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr > _receiver; - - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; - - /** - * Version number source used to update the version number when setting a - * new value. - */ - boost::shared_ptr _versionNumberSource; - - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; - - }; - - /** - * Creates a simple process scalar. A simple process scalar just works on its - * own and does not implement a synchronization mechanism. Apart from this, - * it is fully functional, so that you get and set values. - */ - template - typename ProcessScalar::SharedPtr createSimpleProcessScalar( - const std::string & name = "", T initialValue = 0); - - /** - * Creates a synchronized process scalar. A synchronized process scalar works - * as a pair of two process scalars, where one process scalar acts as a sender - * and the other one acts as a receiver. - * - * Changes that have been made to the sender can be sent to the receiver - * through the {@link ProcessScalar::send()} method. The receiver can be - * updated with these changes by calling its {@link ProcessScalar::receive()} - * method. - * - * The synchronization is implemented in a thread-safe manner, so that the - * sender and the receiver can safely be used by different threads without - * a mutex. However, both the sender and receiver each only support a single - * thread. This means that the sender or the receiver have to be protected - * with a mutex if more than one thread wants to access either of them. - * - * The number of buffers specifies how many buffers are allocated for the - * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::send()} can be called - * in a row without losing data when {@link ProcessArray::receive()} is not - * called in between. - * - * The specified time-stamp source is used for determining the current time - * when sending a value. The receiver will be updated with this time stamp - * when receiving the value. If no time-stamp source is specified, the current - * system-time when the value is sent is used. - * - * The optional send-notification listener is notified every time the sender's - * {@link ProcessScalar::send()} method is called. It can be used to queue a - * request for the receiver's {@link ProcessScalar::receive()} method to be - * called. The process variable passed to the listener is the receiver and - * not the sender. - */ - template - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar( - const std::string & name = "", T initialValue = 0, - std::size_t numberOfBuffers = 1, - TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - VersionNumberSource::SharedPtr versionNumberSource = - VersionNumberSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = - ProcessVariableListener::SharedPtr()); - - /** - * A process variable which is not a sender/receiver pair but a single instance. - * You can neither send nor receive it. - * FIXME: This sounds pretty useless. Should we remove it? - */ - template - typename ProcessScalar::SharedPtr createSimpleProcessScalar( - const std::string & name, T initialValue) { - return boost::make_shared >(ProcessScalar::STAND_ALONE, - name, initialValue); - } - - template - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> createSynchronizedProcessScalar( - const std::string & name, T initialValue, std::size_t numberOfBuffers, - TimeStampSource::SharedPtr timeStampSource, - VersionNumberSource::SharedPtr versionNumberSource, - ProcessVariableListener::SharedPtr sendNotificationListener) { - boost::shared_ptr > receiver = boost::make_shared< - ProcessScalar >(ProcessScalar::RECEIVER, name, initialValue, - numberOfBuffers, versionNumberSource); - typename ProcessScalar::SharedPtr sender = boost::make_shared< - ProcessScalar >(ProcessScalar::SENDER, timeStampSource, - versionNumberSource, sendNotificationListener, receiver); - return std::pair::SharedPtr, - typename ProcessScalar::SharedPtr>(sender, receiver); - } - -} // namespace ChimeraTK - -#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_H diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessScalarAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessScalarAccessor.h deleted file mode 100644 index 0b4ab78..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessScalarAccessor.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_ACCESSOR_H -#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_ACCESSOR_H - -#include "ProcessScalar.h" -#include "ProcessVariableAccessor.h" - -namespace ChimeraTK { - - template - class ProcessScalarAccessor: public ProcessVariableAccessor{ - - // protected: - // typename ProcessScalar::SharedPtr & _processScalar; - - public: - ProcessScalarAccessor(typename ProcessScalar::SharedPtr const & processScalar = - typename ProcessScalar::SharedPtr()) - : ProcessVariableAccessor(processScalar){ - // _processScalar( boost::static_pointer_cast< ProcessScalar >( _impl ) ){ - } - - /** - * Assign the content of the template type. This operator behaves like - * set(T). - */ - ProcessScalarAccessor & operator=(T const & t) { - boost::static_pointer_cast< ProcessScalar >(_impl)->set(t); - return *this; - } - - /** - * Set the value of this process variable to the one of the other process - * variable. - */ - void set(ProcessScalar const & other){ - boost::static_pointer_cast< ProcessScalar >(_impl)->set(other); - } - - /** - * Set the value of this process variable to the one of the other process - * variable accessor - */ - void set(ProcessScalarAccessor const & other){ - set(other.get()); - } - - /** - * Set the value of this process variable to the specified one. - */ - void set(T const & t){ - boost::static_pointer_cast< ProcessScalar >(_impl)->set(t); - } - - /** - * Automatic conversion operator which returns a \b copy of this process - * variable's value. As no reference is returned, this cannot be used for - * assignment. - */ - operator T() const{ - return boost::static_pointer_cast< ProcessScalar >(_impl)->get(); - } - - /** - * Returns a \b copy of this process variable's value. As no reference is - * returned, this cannot be used for assignment. - */ - T get() const{ - return boost::static_pointer_cast< ProcessScalar >(_impl)->get(); - } - - }; - -} - -#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_SCALAR_ACCESSOR_H diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h deleted file mode 100644 index 9d8c40f..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableAccessor.h +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_ACCESSOR_H -#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_ACCESSOR_H - -#include "ProcessVariable.h" - -namespace ChimeraTK { - - class ProcessVariableAccessor{ - public: - /** - * Returns the name that identifies the process variable. - */ - const std::string& getName() const{ - return _impl->getName(); - } - - /** - * Returns the \c std::type_info for the value type of this process - * variable or array. This can be used to determine the type of a process - * variable at runtime. - */ - const std::type_info& getValueType() const{ - return _impl->getValueType(); - } - - /** - * Returns true if the implementation is an instance of - * {@link ProcessArray}\ and false if this object is an - * instance of {@link ProcessScalar}\. - */ - bool isArray() const{ - return _impl->isArray(); - } - - /** - * Returns true if this object represents the receiver in a - * sender / receiver pair, false otherwise. - */ - virtual bool isReadable() const{ - return _impl->isReadable(); - } - - /** - * Returns true if this object represents the sender in a - * sender / receiver pair, false otherwise. - */ - bool isWriteable() const{ - return _impl->isWriteable(); - } - - /** - * Returns the time stamp associated with the current value of the process - * variable. Typically, this is the time when the value was updated. - */ - TimeStamp getTimeStamp() const{ - return _impl->getTimeStamp(); - } - - /** - * Receives a new value from the sender if this process variable is a - * receiver. Returns true if a new value has been received and - * false if there is no new value available. - * - * Throws an exception if this process variable is not a receiver. - */ - bool readNonBlocking(){ - return _impl->readNonBlocking(); - } - - /** - * Sends the current value to the receiver if this process variable is a - * sender. Returns true if an empty buffer was available and - * false if no empty buffer was available and thus a previously - * sent value has been dropped in order to send the current value. - * - * Throws an exception if this process variable is not a sender. - */ - void write(){ - _impl->write(); - } - - /** - * Replaces the implementation that is used under the hood. Use this function instead of - * a assignment operator. The assignment operator was turned off to avoid confusion with - * assigning the content of the other process variable, which is accessed by set. - */ - void replace(ProcessVariableAccessor const & other){ - replaceImpl( other._impl ); - } - - private: - - /** The replace impl to avoid code duplication. Requires impl to be valid. - */ - void replaceImpl(ProcessVariable::SharedPtr const & otherImpl){ - // current safety check - // FIXME: Do we want this in future when the accessors childen - // should work with all kinds of impl? Just one impl? - if ( (_impl->isArray() != otherImpl->isArray() ) || - (_impl->getValueType() != otherImpl->getValueType() ) ){ - throw std::runtime_error("ProcessVariableAccessors can only be replaced by accessors of the same type"); - } - _impl = otherImpl; - } - - public: - /** - * Replace function which direktly takes a ProcessVariable (sharedPtr). Can be used - * to initalise an accessor which has been created with the default constuctor of the child - * (empty shared ptr). - */ - void replace(ProcessVariable::SharedPtr const & impl){ - if (_impl){ // my current impl exists. replace with safety check - replaceImpl( impl ); - }else{ // I don't have an impl yet, just assign it. - _impl = impl; - } - } - - protected: - ProcessVariable::SharedPtr _impl; - - /** - */ - ProcessVariableAccessor(ProcessVariable::SharedPtr const & impl) - : _impl(impl){ - } - - private: - /** - * The assignment operator is intentionally turned off. Also derrieved classes cannot - * access it. It intentionally is not implemented. Use replace() instead to replace the impl, - * and set() to replace the content. - */ - ProcessVariableAccessor & operator=(ProcessVariableAccessor const & other); - }; - -} //namespace ChimeraTK - -#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_ACCESSOR_H diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h index 5792a1d..bdc8736 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h @@ -18,8 +18,8 @@ template struct TypedPVHolder{ - typename ChimeraTK::ProcessScalar::SharedPtr toDeviceScalar; - typename ChimeraTK::ProcessScalar::SharedPtr fromDeviceScalar; + typename ChimeraTK::ProcessArray::SharedPtr toDeviceScalar; + typename ChimeraTK::ProcessArray::SharedPtr fromDeviceScalar; typename ChimeraTK::ProcessArray::SharedPtr toDeviceArray; typename ChimeraTK::ProcessArray::SharedPtr fromDeviceArray; /** The "data type constant" is a value that depends on the data type. It is intended as @@ -30,30 +30,30 @@ struct TypedPVHolder{ * \li -sizeof(type) for signed integer types * \li 1./sizeof(type) for floating point types */ - typename ChimeraTK::ProcessScalar::SharedPtr dataTypeConstant; + typename ChimeraTK::ProcessArray::SharedPtr dataTypeConstant; typename ChimeraTK::ProcessArray::SharedPtr constantArray; TypedPVHolder(boost::shared_ptr const & processVariableManager, std::string typeNamePrefix): - toDeviceScalar( processVariableManager->createProcessScalar(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_SCALAR") ), - fromDeviceScalar( processVariableManager->createProcessScalar(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_SCALAR") ), + toDeviceScalar( processVariableManager->createProcessArray(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_SCALAR",1) ), + fromDeviceScalar( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_SCALAR",1) ), toDeviceArray( processVariableManager->createProcessArray(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_ARRAY", 10) ), fromDeviceArray( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_ARRAY", 10) ), - dataTypeConstant( processVariableManager->createProcessScalar(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/DATA_TYPE_CONSTANT") ), + dataTypeConstant( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/DATA_TYPE_CONSTANT",1) ), constantArray( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/CONSTANT_ARRAY",10) ){ if (std::numeric_limits::is_integer){ if (std::numeric_limits::is_signed){ // signed int - (*dataTypeConstant) = static_cast(-sizeof(DataType)); + dataTypeConstant->accessData(0) = static_cast(-sizeof(DataType)); }else{ // unsigned int - (*dataTypeConstant) = sizeof(DataType); + dataTypeConstant->accessData(0) = sizeof(DataType); } }else{ // floating point - (*dataTypeConstant) = 1./sizeof(DataType); + dataTypeConstant->accessData(0) = 1./sizeof(DataType); } for (size_t i = 0; i < constantArray->get().size(); ++i){ - constantArray->get()[i] = (*dataTypeConstant)*i*i; + constantArray->get()[i] = dataTypeConstant->accessData(0)*i*i; } } diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index cef92ff..e2ecfd2 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -35,23 +35,23 @@ struct TestCoreFixture{ template void typedWriteScalarTest(std::string typeNamePrefix){ - auto toDeviceScalar = csManager->getProcessScalar(typeNamePrefix+"/TO_DEVICE_SCALAR"); - auto fromDeviceScalar = csManager->getProcessScalar(typeNamePrefix+"/FROM_DEVICE_SCALAR"); + auto toDeviceScalar = csManager->getProcessArray(typeNamePrefix+"/TO_DEVICE_SCALAR"); + auto fromDeviceScalar = csManager->getProcessArray(typeNamePrefix+"/FROM_DEVICE_SCALAR"); - UserType previousReadValue = *fromDeviceScalar; + UserType previousReadValue = fromDeviceScalar->accessData(0); - *toDeviceScalar = previousReadValue+13; + toDeviceScalar->accessData(0) = previousReadValue+13; csSyncUtil.sendAll(); IndependentTestCore::runMainLoopOnce(); csSyncUtil.receiveAll(); - BOOST_CHECK( *fromDeviceScalar == previousReadValue+13 ); + BOOST_CHECK( fromDeviceScalar->accessData(0) == previousReadValue+13 ); } template void typedReadArrayTest(std::string typeNamePrefix){ - UserType typeConstant = csManager->getProcessScalar(typeNamePrefix+"/DATA_TYPE_CONSTANT")->get(); + UserType typeConstant = csManager->getProcessArray(typeNamePrefix+"/DATA_TYPE_CONSTANT")->accessData(0); auto inputArray = csManager->getProcessArray(typeNamePrefix+"/CONSTANT_ARRAY"); BOOST_REQUIRE(inputArray); @@ -96,14 +96,14 @@ BOOST_AUTO_TEST_SUITE( FullStubTestSuite ) BOOST_FIXTURE_TEST_CASE( test_read_scalar, TestCoreFixture){ // just after creation of the fixture the constants should be available to the control system - BOOST_CHECK( csManager->getProcessScalar("CHAR/DATA_TYPE_CONSTANT")->get() == -1 ); - BOOST_CHECK( csManager->getProcessScalar("UCHAR/DATA_TYPE_CONSTANT")->get() == 1 ); - BOOST_CHECK( csManager->getProcessScalar("SHORT/DATA_TYPE_CONSTANT")->get() == -2 ); - BOOST_CHECK( csManager->getProcessScalar("USHORT/DATA_TYPE_CONSTANT")->get() == 2 ); - BOOST_CHECK( csManager->getProcessScalar("INT/DATA_TYPE_CONSTANT")->get() == -4 ); - BOOST_CHECK( csManager->getProcessScalar("UINT/DATA_TYPE_CONSTANT")->get() == 4 ); - BOOST_CHECK( csManager->getProcessScalar("FLOAT/DATA_TYPE_CONSTANT")->get() == 1./4 ); - BOOST_CHECK( csManager->getProcessScalar("DOUBLE/DATA_TYPE_CONSTANT")->get() == 1./8 ); + BOOST_CHECK( csManager->getProcessArray("CHAR/DATA_TYPE_CONSTANT")->get()[0] == -1 ); + BOOST_CHECK( csManager->getProcessArray("UCHAR/DATA_TYPE_CONSTANT")->get()[0] == 1 ); + BOOST_CHECK( csManager->getProcessArray("SHORT/DATA_TYPE_CONSTANT")->get()[0] == -2 ); + BOOST_CHECK( csManager->getProcessArray("USHORT/DATA_TYPE_CONSTANT")->get()[0] == 2 ); + BOOST_CHECK( csManager->getProcessArray("INT/DATA_TYPE_CONSTANT")->get()[0] == -4 ); + BOOST_CHECK( csManager->getProcessArray("UINT/DATA_TYPE_CONSTANT")->get()[0] == 4 ); + BOOST_CHECK( csManager->getProcessArray("FLOAT/DATA_TYPE_CONSTANT")->get()[0] == 1./4 ); + BOOST_CHECK( csManager->getProcessArray("DOUBLE/DATA_TYPE_CONSTANT")->get()[0] == 1./8 ); } BOOST_FIXTURE_TEST_CASE( test_write_scalar, TestCoreFixture){ diff --git a/tests/src/testControlSystemSynchronizationUtility.cpp b/tests/src/testControlSystemSynchronizationUtility.cpp index 5bc9f62..ff27c8a 100644 --- a/tests/src/testControlSystemSynchronizationUtility.cpp +++ b/tests/src/testControlSystemSynchronizationUtility.cpp @@ -28,25 +28,25 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntOut = - devManager->createProcessScalar(controlSystemToDevice, - "intOut"); - ProcessScalar::SharedPtr csIntOut = csManager->getProcessScalar< + ProcessArray::SharedPtr devIntOut = + devManager->createProcessArray(controlSystemToDevice, + "intOut",1); + ProcessArray::SharedPtr csIntOut = csManager->getProcessArray< int32_t>("intOut"); - ProcessScalar::SharedPtr devFloatOut = - devManager->createProcessScalar(controlSystemToDevice, - "floatOut"); - ProcessScalar::SharedPtr csFloatOut = csManager->getProcessScalar< + ProcessArray::SharedPtr devFloatOut = + devManager->createProcessArray(controlSystemToDevice, + "floatOut",1); + ProcessArray::SharedPtr csFloatOut = csManager->getProcessArray< float>("floatOut"); - ProcessScalar::SharedPtr devIntIn = - devManager->createProcessScalar(deviceToControlSystem, - "intIn"); - ProcessScalar::SharedPtr csIntIn = csManager->getProcessScalar< + ProcessArray::SharedPtr devIntIn = + devManager->createProcessArray(deviceToControlSystem, + "intIn",1); + ProcessArray::SharedPtr csIntIn = csManager->getProcessArray< int32_t>("intIn"); - ProcessScalar::SharedPtr devFloatIn = - devManager->createProcessScalar(deviceToControlSystem, - "floatIn"); - ProcessScalar::SharedPtr csFloatIn = csManager->getProcessScalar< + ProcessArray::SharedPtr devFloatIn = + devManager->createProcessArray(deviceToControlSystem, + "floatIn",1); + ProcessArray::SharedPtr csFloatIn = csManager->getProcessArray< float>("floatIn"); ControlSystemSynchronizationUtility syncUtil(csManager); @@ -66,98 +66,98 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) // Next we test it with a single element. ProcessVariable::SharedPtr singletonRawArray[1]; singletonRawArray[0] = csIntIn; - *devIntIn = 42; + devIntIn->accessData(0) = 42; devIntIn->write(); syncUtil.receive(singletonRawArray, singletonRawArray + 1); - BOOST_CHECK(*csIntIn == 42); + BOOST_CHECK(csIntIn->accessData(0) == 42); singletonRawArray[0] = csIntOut; - *csIntOut = 43; + csIntOut->accessData(0) = 43; syncUtil.send(singletonRawArray, singletonRawArray + 1); devIntOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 43); + BOOST_CHECK(devIntOut->accessData(0) == 43); std::vector singletonVector(1); singletonVector[0] = csIntIn; - *devIntIn = 44; + devIntIn->accessData(0) = 44; devIntIn->write(); syncUtil.receive(singletonVector); - BOOST_CHECK(*csIntIn == 44); + BOOST_CHECK(csIntIn->accessData(0) == 44); singletonVector[0] = csIntOut; - *csIntOut = 45; + csIntOut->accessData(0) = 45; syncUtil.send(singletonVector); devIntOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 45); + BOOST_CHECK(devIntOut->accessData(0) == 45); std::list singletonList; singletonList.push_back(csIntIn); - *devIntIn = 46; + devIntIn->accessData(0) = 46; devIntIn->write(); syncUtil.receive(singletonList); - BOOST_CHECK(*csIntIn == 46); + BOOST_CHECK(csIntIn->accessData(0) == 46); singletonList.clear(); singletonList.push_back(csIntOut); - *csIntOut = 47; + csIntOut->accessData(0) = 47; syncUtil.send(singletonList); devIntOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 47); + BOOST_CHECK(devIntOut->accessData(0) == 47); // Finally, we test with two elements. ProcessVariable::SharedPtr doubleRawArray[2]; doubleRawArray[0] = csIntIn; doubleRawArray[1] = csFloatIn; - *devIntIn = 48; - *devFloatIn = 49.0f; + devIntIn->accessData(0) = 48; + devFloatIn->accessData(0) = 49.0f; devIntIn->write(); devFloatIn->write(); syncUtil.receive(doubleRawArray, doubleRawArray + 2); - BOOST_CHECK(*csIntIn == 48); - BOOST_CHECK(*csFloatIn == 49.0f); + BOOST_CHECK(csIntIn->accessData(0) == 48); + BOOST_CHECK(csFloatIn->accessData(0) == 49.0f); doubleRawArray[0] = csIntOut; doubleRawArray[1] = csFloatOut; - *csIntOut = 49; - *csFloatOut = 50.0f; + csIntOut->accessData(0) = 49; + csFloatOut->accessData(0) = 50.0f; syncUtil.send(doubleRawArray, doubleRawArray + 2); devIntOut->readNonBlocking(); devFloatOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 49); - BOOST_CHECK(*devFloatOut == 50.0f); + BOOST_CHECK(devIntOut->accessData(0) == 49); + BOOST_CHECK(devFloatOut->accessData(0) == 50.0f); std::vector doubleVector(2); doubleVector[0] = csIntIn; doubleVector[1] = csFloatIn; - *devIntIn = 51; - *devFloatIn = 52.0f; + devIntIn->accessData(0) = 51; + devFloatIn->accessData(0) = 52.0f; devIntIn->write(); devFloatIn->write(); syncUtil.receive(doubleVector); - BOOST_CHECK(*csIntIn == 51); - BOOST_CHECK(*csFloatIn == 52.0f); + BOOST_CHECK(csIntIn->accessData(0) == 51); + BOOST_CHECK(csFloatIn->accessData(0) == 52.0f); doubleVector[0] = csIntOut; doubleVector[1] = csFloatOut; - *csIntOut = 53; - *csFloatOut = 54.0f; + csIntOut->accessData(0) = 53; + csFloatOut->accessData(0) = 54.0f; syncUtil.send(doubleVector); devIntOut->readNonBlocking(); devFloatOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 53); - BOOST_CHECK(*devFloatOut == 54.0f); + BOOST_CHECK(devIntOut->accessData(0) == 53); + BOOST_CHECK(devFloatOut->accessData(0) == 54.0f); std::list doubleList; doubleList.push_back(csIntIn); doubleList.push_back(csFloatIn); - *devIntIn = 55; - *devFloatIn = 56.0f; + devIntIn->accessData(0) = 55; + devFloatIn->accessData(0) = 56.0f; devIntIn->write(); devFloatIn->write(); syncUtil.receive(doubleList); - BOOST_CHECK(*csIntIn == 55); - BOOST_CHECK(*csFloatIn == 56.0f); + BOOST_CHECK(csIntIn->accessData(0) == 55); + BOOST_CHECK(csFloatIn->accessData(0) == 56.0f); doubleList.clear(); doubleList.push_back(csIntOut); doubleList.push_back(csFloatOut); - *csIntOut = 57; - *csFloatOut = 58.0f; + csIntOut->accessData(0) = 57; + csFloatOut->accessData(0) = 58.0f; syncUtil.send(doubleList); devIntOut->readNonBlocking(); devFloatOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 57); - BOOST_CHECK(*devFloatOut == 58.0f); + BOOST_CHECK(devIntOut->accessData(0) == 57); + BOOST_CHECK(devFloatOut->accessData(0) == 58.0f); } BOOST_AUTO_TEST_CASE( testReceiveAll ) { @@ -166,18 +166,18 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntIn = - devManager->createProcessScalar(deviceToControlSystem, - "intIn"); - ProcessScalar::SharedPtr csIntIn = csManager->getProcessScalar< + ProcessArray::SharedPtr devIntIn = + devManager->createProcessArray(deviceToControlSystem, + "intIn",1); + ProcessArray::SharedPtr csIntIn = csManager->getProcessArray< int32_t>("intIn"); ControlSystemSynchronizationUtility syncUtil(csManager); - *devIntIn = 5; + devIntIn->accessData(0) = 5; devIntIn->write(); syncUtil.receiveAll(); - BOOST_CHECK(*csIntIn == 5); + BOOST_CHECK(csIntIn->accessData(0) == 5); } BOOST_AUTO_TEST_CASE( testSendAll ) { @@ -186,31 +186,31 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntOut = - devManager->createProcessScalar(controlSystemToDevice, - "intOut"); - ProcessScalar::SharedPtr csIntOut = csManager->getProcessScalar< + ProcessArray::SharedPtr devIntOut = + devManager->createProcessArray(controlSystemToDevice, + "intOut",1); + ProcessArray::SharedPtr csIntOut = csManager->getProcessArray< int32_t>("intOut"); - ProcessScalar::SharedPtr devFloatOut = - devManager->createProcessScalar(controlSystemToDevice, - "floatOut"); - ProcessScalar::SharedPtr csFloatOut = csManager->getProcessScalar< + ProcessArray::SharedPtr devFloatOut = + devManager->createProcessArray(controlSystemToDevice, + "floatOut",1); + ProcessArray::SharedPtr csFloatOut = csManager->getProcessArray< float>("floatOut"); - ProcessScalar::SharedPtr devIntIn = - devManager->createProcessScalar(deviceToControlSystem, - "intIn"); - ProcessScalar::SharedPtr csIntIn = csManager->getProcessScalar< + ProcessArray::SharedPtr devIntIn = + devManager->createProcessArray(deviceToControlSystem, + "intIn",1); + ProcessArray::SharedPtr csIntIn = csManager->getProcessArray< int32_t>("intIn"); ControlSystemSynchronizationUtility syncUtil(csManager); - *csIntOut = 15; - *csFloatOut = 16.0f; + csIntOut->accessData(0) = 15; + csFloatOut->accessData(0) = 16.0f; syncUtil.sendAll(); devIntOut->readNonBlocking(); devFloatOut->readNonBlocking(); - BOOST_CHECK(*devIntOut == 15); - BOOST_CHECK(*devFloatOut == 16.0f); + BOOST_CHECK(devIntOut->accessData(0) == 15); + BOOST_CHECK(devFloatOut->accessData(0) == 16.0f); } BOOST_AUTO_TEST_CASE( testReceiveNotificationListener ) { @@ -219,15 +219,15 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntIn = - devManager->createProcessScalar(deviceToControlSystem, - "intIn"); - ProcessScalar::SharedPtr csIntIn = csManager->getProcessScalar< + ProcessArray::SharedPtr devIntIn = + devManager->createProcessArray(deviceToControlSystem, + "intIn",1); + ProcessArray::SharedPtr csIntIn = csManager->getProcessArray< int32_t>("intIn"); - ProcessScalar::SharedPtr devFloatIn = - devManager->createProcessScalar(deviceToControlSystem, - "floatIn"); - ProcessScalar::SharedPtr csFloatIn = csManager->getProcessScalar< + ProcessArray::SharedPtr devFloatIn = + devManager->createProcessArray(deviceToControlSystem, + "floatIn",1); + ProcessArray::SharedPtr csFloatIn = csManager->getProcessArray< float>("floatIn"); ControlSystemSynchronizationUtility syncUtil(csManager); diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index 5295cd0..2d9640d 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -28,26 +28,18 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntOut = - devManager->createProcessScalar(controlSystemToDevice, - "intOut"); - ProcessScalar::SharedPtr csIntOut = csManager->getProcessScalar< - int32_t>("intOut"); - ProcessScalar::SharedPtr devFloatOut = - devManager->createProcessScalar(controlSystemToDevice, - "floatOut"); - ProcessScalar::SharedPtr csFloatOut = csManager->getProcessScalar< - float>("floatOut"); - ProcessScalar::SharedPtr devIntIn = - devManager->createProcessScalar(deviceToControlSystem, - "intIn"); - ProcessScalar::SharedPtr csIntIn = csManager->getProcessScalar< - int32_t>("intIn"); - ProcessScalar::SharedPtr devFloatIn = - devManager->createProcessScalar(deviceToControlSystem, - "floatIn"); - ProcessScalar::SharedPtr csFloatIn = csManager->getProcessScalar< - float>("floatIn"); + ProcessArray::SharedPtr devIntOut = + devManager->createProcessArray(controlSystemToDevice, "intOut", 1); + ProcessArray::SharedPtr csIntOut = csManager->getProcessArray("intOut"); + ProcessArray::SharedPtr devFloatOut = + devManager->createProcessArray(controlSystemToDevice, "floatOut", 1); + ProcessArray::SharedPtr csFloatOut = csManager->getProcessArray("floatOut"); + ProcessArray::SharedPtr devIntIn = + devManager->createProcessArray(deviceToControlSystem, "intIn", 1); + ProcessArray::SharedPtr csIntIn = csManager->getProcessArray("intIn"); + ProcessArray::SharedPtr devFloatIn = + devManager->createProcessArray(deviceToControlSystem, "floatIn", 1); + ProcessArray::SharedPtr csFloatIn = csManager->getProcessArray("floatIn"); DeviceSynchronizationUtility syncUtil(devManager); @@ -66,98 +58,98 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) // Next we test it with a single element. ProcessVariable::SharedPtr singletonRawArray[1]; singletonRawArray[0] = devIntOut; - *csIntOut = 42; + csIntOut->accessData(0) = 42; csIntOut->write(); syncUtil.receive(singletonRawArray, singletonRawArray + 1); - BOOST_CHECK(*devIntOut == 42); + BOOST_CHECK(devIntOut->accessData(0) == 42); singletonRawArray[0] = devIntIn; - *devIntIn = 43; + devIntIn->accessData(0) = 43; syncUtil.send(singletonRawArray, singletonRawArray + 1); csIntIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 43); + BOOST_CHECK(csIntIn->accessData(0) == 43); std::vector singletonVector(1); singletonVector[0] = devIntOut; - *csIntOut = 44; + csIntOut->accessData(0) = 44; csIntOut->write(); syncUtil.receive(singletonVector); - BOOST_CHECK(*devIntOut == 44); + BOOST_CHECK(devIntOut->accessData(0) == 44); singletonVector[0] = devIntIn; - *devIntIn = 45; + devIntIn->accessData(0) = 45; syncUtil.send(singletonVector); csIntIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 45); + BOOST_CHECK(csIntIn->accessData(0) == 45); std::list singletonList; singletonList.push_back(devIntOut); - *csIntOut = 46; + csIntOut->accessData(0) = 46; csIntOut->write(); syncUtil.receive(singletonList); - BOOST_CHECK(*devIntOut == 46); + BOOST_CHECK(devIntOut->accessData(0) == 46); singletonList.clear(); singletonList.push_back(devIntIn); - *devIntIn = 47; + devIntIn->accessData(0) = 47; syncUtil.send(singletonList); csIntIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 47); + BOOST_CHECK(csIntIn->accessData(0) == 47); // Finally, we test with two elements. ProcessVariable::SharedPtr doubleRawArray[2]; doubleRawArray[0] = devIntOut; doubleRawArray[1] = devFloatOut; - *csIntOut = 48; - *csFloatOut = 49.0f; + csIntOut->accessData(0) = 48; + csFloatOut->accessData(0) = 49.0f; csIntOut->write(); csFloatOut->write(); syncUtil.receive(doubleRawArray, doubleRawArray + 2); - BOOST_CHECK(*devIntOut == 48); - BOOST_CHECK(*devFloatOut == 49.0f); + BOOST_CHECK(devIntOut->accessData(0) == 48); + BOOST_CHECK(devFloatOut->accessData(0) == 49.0f); doubleRawArray[0] = devIntIn; doubleRawArray[1] = devFloatIn; - *devIntIn = 49; - *devFloatIn = 50.0f; + devIntIn->accessData(0) = 49; + devFloatIn->accessData(0) = 50.0f; syncUtil.send(doubleRawArray, doubleRawArray + 2); csIntIn->readNonBlocking(); csFloatIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 49); - BOOST_CHECK(*csFloatIn == 50.0f); + BOOST_CHECK(csIntIn->accessData(0) == 49); + BOOST_CHECK(csFloatIn->accessData(0) == 50.0f); std::vector doubleVector(2); doubleVector[0] = devIntOut; doubleVector[1] = devFloatOut; - *csIntOut = 51; - *csFloatOut = 52.0f; + csIntOut->accessData(0) = 51; + csFloatOut->accessData(0) = 52.0f; csIntOut->write(); csFloatOut->write(); syncUtil.receive(doubleVector); - BOOST_CHECK(*devIntOut == 51); - BOOST_CHECK(*devFloatOut == 52.0f); + BOOST_CHECK(devIntOut->accessData(0) == 51); + BOOST_CHECK(devFloatOut->accessData(0) == 52.0f); doubleVector[0] = devIntIn; doubleVector[1] = devFloatIn; - *devIntIn = 53; - *devFloatIn = 54.0f; + devIntIn->accessData(0) = 53; + devFloatIn->accessData(0) = 54.0f; syncUtil.send(doubleVector); csIntIn->readNonBlocking(); csFloatIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 53); - BOOST_CHECK(*csFloatIn == 54.0f); + BOOST_CHECK(csIntIn->accessData(0) == 53); + BOOST_CHECK(csFloatIn->accessData(0) == 54.0f); std::list doubleList; doubleList.push_back(devIntOut); doubleList.push_back(devFloatOut); - *csIntOut = 55; - *csFloatOut = 56.0f; + csIntOut->accessData(0) = 55; + csFloatOut->accessData(0) = 56.0f; csIntOut->write(); csFloatOut->write(); syncUtil.receive(doubleList); - BOOST_CHECK(*devIntOut == 55); - BOOST_CHECK(*devFloatOut == 56.0f); + BOOST_CHECK(devIntOut->accessData(0) == 55); + BOOST_CHECK(devFloatOut->accessData(0) == 56.0f); doubleList.clear(); doubleList.push_back(devIntIn); doubleList.push_back(devFloatIn); - *devIntIn = 57; - *devFloatIn = 58.0f; + devIntIn->accessData(0) = 57; + devFloatIn->accessData(0) = 58.0f; syncUtil.send(doubleList); csIntIn->readNonBlocking(); csFloatIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 57); - BOOST_CHECK(*csFloatIn == 58.0f); + BOOST_CHECK(csIntIn->accessData(0) == 57); + BOOST_CHECK(csFloatIn->accessData(0) == 58.0f); } BOOST_AUTO_TEST_CASE( testReceiveAll ) { @@ -166,18 +158,16 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntOut = - devManager->createProcessScalar(controlSystemToDevice, - "intOut"); - ProcessScalar::SharedPtr csIntOut = csManager->getProcessScalar< - int32_t>("intOut"); + ProcessArray::SharedPtr devIntOut = + devManager->createProcessArray(controlSystemToDevice,"intOut",1); + ProcessArray::SharedPtr csIntOut = csManager->getProcessArray("intOut"); DeviceSynchronizationUtility syncUtil(devManager); - *csIntOut = 5; + csIntOut->accessData(0) = 5; csIntOut->write(); syncUtil.receiveAll(); - BOOST_CHECK(*devIntOut == 5); + BOOST_CHECK(devIntOut->accessData(0) == 5); } BOOST_AUTO_TEST_CASE( testSendAll ) { @@ -186,31 +176,25 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntIn = - devManager->createProcessScalar(deviceToControlSystem, - "intIn"); - ProcessScalar::SharedPtr csIntIn = csManager->getProcessScalar< - int32_t>("intIn"); - ProcessScalar::SharedPtr devFloatIn = - devManager->createProcessScalar(deviceToControlSystem, - "floatIn"); - ProcessScalar::SharedPtr csFloatIn = csManager->getProcessScalar< - float>("floatIn"); - ProcessScalar::SharedPtr devIntOut = - devManager->createProcessScalar(controlSystemToDevice, - "intOut"); - ProcessScalar::SharedPtr csIntOut = csManager->getProcessScalar< - int32_t>("intOut"); + ProcessArray::SharedPtr devIntIn = + devManager->createProcessArray(deviceToControlSystem, "intIn", 1); + ProcessArray::SharedPtr csIntIn = csManager->getProcessArray("intIn"); + ProcessArray::SharedPtr devFloatIn = + devManager->createProcessArray(deviceToControlSystem, "floatIn", 1); + ProcessArray::SharedPtr csFloatIn = csManager->getProcessArray("floatIn"); + ProcessArray::SharedPtr devIntOut = + devManager->createProcessArray(controlSystemToDevice, "intOut", 1); + ProcessArray::SharedPtr csIntOut = csManager->getProcessArray("intOut"); DeviceSynchronizationUtility syncUtil(devManager); - *devIntIn = 15; - *devFloatIn = 16.0f; + devIntIn->accessData(0) = 15; + devFloatIn->accessData(0) = 16.0f; syncUtil.sendAll(); csIntIn->readNonBlocking(); csFloatIn->readNonBlocking(); - BOOST_CHECK(*csIntIn == 15); - BOOST_CHECK(*csFloatIn == 16.0f); + BOOST_CHECK(csIntIn->accessData(0) == 15); + BOOST_CHECK(csFloatIn->accessData(0) == 16.0f); } BOOST_AUTO_TEST_CASE( testReceiveNotificationListener ) { @@ -219,40 +203,31 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) boost::shared_ptr csManager = pvManagers.first; boost::shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr devIntOut = - devManager->createProcessScalar(controlSystemToDevice, - "intOut"); - ProcessScalar::SharedPtr csIntOut = csManager->getProcessScalar< - int32_t>("intOut"); - ProcessScalar::SharedPtr devFloatOut = - devManager->createProcessScalar(controlSystemToDevice, - "floatOut"); - ProcessScalar::SharedPtr csFloatOut = csManager->getProcessScalar< - float>("floatOut"); + ProcessArray::SharedPtr devIntOut = + devManager->createProcessArray(controlSystemToDevice, "intOut", 1); + ProcessArray::SharedPtr csIntOut = csManager->getProcessArray("intOut"); + ProcessArray::SharedPtr devFloatOut = + devManager->createProcessArray(controlSystemToDevice, "floatOut", 1); + ProcessArray::SharedPtr csFloatOut = csManager->getProcessArray("floatOut"); DeviceSynchronizationUtility syncUtil(devManager); boost::shared_ptr receiveNotificationListener = boost::make_shared(); - syncUtil.addReceiveNotificationListener("intOut", - receiveNotificationListener); + syncUtil.addReceiveNotificationListener("intOut", receiveNotificationListener); csIntOut->write(); csFloatOut->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 1); - BOOST_CHECK( - receiveNotificationListener->lastProcessVariable->getName() - == "intOut"); + BOOST_CHECK(receiveNotificationListener->lastProcessVariable->getName() == "intOut"); csIntOut->write(); std::vector pvList(1); pvList[0] = devIntOut; syncUtil.receive(pvList); BOOST_CHECK(receiveNotificationListener->count == 2); - BOOST_CHECK( - receiveNotificationListener->lastProcessVariable->getName() - == "intOut"); + BOOST_CHECK(receiveNotificationListener->lastProcessVariable->getName() == "intOut"); syncUtil.removeReceiveNotificationListener("intOut"); csIntOut->write(); diff --git a/tests/src/testPVAccessors.cpp b/tests/src/testPVAccessors.cpp deleted file mode 100644 index 2877eaa..0000000 --- a/tests/src/testPVAccessors.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#define BOOST_TEST_MODULE PvAcessorsTest -// Only after defining the name include the unit test header. -#include - -#include "ProcessScalarAccessor.h" -#include "ProcessArrayAccessor.h" -#include "ManualTimeStampSource.h" - -using namespace ChimeraTK; - -BOOST_AUTO_TEST_SUITE( PvAccessorsTestSuite ) - -BOOST_AUTO_TEST_CASE( testScalarCreation ){ - // checking that the default constructor is working - ProcessScalarAccessor scalarIntAccessor; - - // There are two ways to create. Just run them both: - auto firstInt = createSimpleProcessScalar("first",42); - scalarIntAccessor.replace( firstInt ); - - BOOST_CHECK( scalarIntAccessor.get() == 42 ); - - auto secondInt = createSimpleProcessScalar("second",43); - ProcessScalarAccessor secondIntAccessor(secondInt); - - BOOST_CHECK( secondIntAccessor.get() == 43 ); - - scalarIntAccessor.replace( secondIntAccessor ); - - BOOST_CHECK( scalarIntAccessor.get() == 43 ); - // both must point to the same impl (shared prt pointing to the same instance - - scalarIntAccessor.set(44); - BOOST_CHECK( secondIntAccessor.get() == 44 ); - - auto intArray = createSimpleProcessArray(10, "intArray"); - BOOST_CHECK_THROW( secondIntAccessor.replace(intArray), std::runtime_error) ; - - auto scalarFloat = createSimpleProcessScalar("scalarFloat"); - BOOST_CHECK_THROW( secondIntAccessor.replace(scalarFloat), std::runtime_error) ; - - // test copy construction (automatically created, but it's a feature we want - ProcessScalarAccessor thirdIntAccessor( secondIntAccessor ); - secondIntAccessor.set( 46 ); - BOOST_CHECK( thirdIntAccessor.get() == 46 ); -} - -BOOST_AUTO_TEST_CASE( testPVAccessor ){ - // testing the base class. We are mainly testing sending and receiving, so - // we need a pair - auto myTimeStampSource = boost::make_shared(); - TimeStamp testTimeStamp; - testTimeStamp.seconds = 123; - myTimeStampSource->setTimeStamp(testTimeStamp); - - auto senderReceiverPair = createSynchronizedProcessScalar("anInt", - 0 /*start value*/, 1 /* nBuffers */, myTimeStampSource ); - - ProcessScalarAccessor scalarSender(senderReceiverPair.first); - ProcessScalarAccessor scalarReceiver(senderReceiverPair.second); - - ProcessVariableAccessor & sender = scalarSender; - ProcessVariableAccessor & receiver = scalarReceiver; - - BOOST_CHECK( sender.getName() == "anInt" ); - BOOST_CHECK( sender.getValueType() == typeid(int) ); - BOOST_CHECK( sender.isArray() == false ); - - BOOST_CHECK( sender.isReadable() == false ); - BOOST_CHECK( sender.isWriteable() == true ); - BOOST_CHECK( receiver.isReadable() == true ); - BOOST_CHECK( receiver.isWriteable() == false ); - - BOOST_CHECK( !(sender.getTimeStamp() == testTimeStamp) ); - BOOST_CHECK( scalarReceiver != 2 ); - scalarSender.set(2); - sender.write(); - BOOST_CHECK( sender.getTimeStamp() == testTimeStamp ); - receiver.readNonBlocking(); - BOOST_CHECK( receiver.getTimeStamp() == testTimeStamp ); - BOOST_CHECK( scalarReceiver == 2 ); - -} - -BOOST_AUTO_TEST_CASE( testScalarOperations ){ - // test getters, setters and operators - auto scalarInt = createSimpleProcessScalar("scalarInt",42); - ProcessScalarAccessor scalarIntAccessor(scalarInt); - - // implicit conversion - int i; - i = scalarIntAccessor; - BOOST_CHECK( i == 42 ); - - // assigment - i = 43; - scalarIntAccessor = i; - // we test with the underlying impl - BOOST_CHECK( scalarInt->get() == 43 ); - // now we can also test the get function - BOOST_CHECK( scalarIntAccessor.get() == 43 ); - - auto anotherScalarInt = createSimpleProcessScalar("anotherScalarInt",44); - scalarIntAccessor.set(*anotherScalarInt); - BOOST_CHECK( scalarIntAccessor.get() == 44 ); - scalarIntAccessor.set( 45 ); - BOOST_CHECK( scalarIntAccessor.get() == 45 ); - - ProcessScalarAccessor anotherScalarIntAccessor(anotherScalarInt); - anotherScalarIntAccessor.set(46); - scalarIntAccessor.set( anotherScalarIntAccessor ); - BOOST_CHECK( scalarIntAccessor.get() == 46 ); -} - -BOOST_AUTO_TEST_CASE( testArrayCreation ){ - // checking that the default constructor is working - ProcessArrayAccessor arrayIntAccessor; - - // There are two ways to create. Just run them both: - auto firstIntArray = createSimpleProcessArray(10,"first", 42); - arrayIntAccessor.replace( firstIntArray ); - - BOOST_CHECK( arrayIntAccessor.get().size() == 10 ); - BOOST_CHECK( arrayIntAccessor.get()[0] == 42 ); - - auto secondIntArray = createSimpleProcessArray(11,"second",43); - ProcessArrayAccessor secondIntArrayAccessor(secondIntArray); - - BOOST_CHECK( secondIntArrayAccessor.get().size() == 11 ); - BOOST_CHECK( secondIntArrayAccessor.get()[0] == 43 ); - - arrayIntAccessor.replace( secondIntArrayAccessor ); - - BOOST_CHECK( arrayIntAccessor.get().size() == 11 ); - BOOST_CHECK( arrayIntAccessor.get()[0] == 43 ); - // both must point to the same impl (shared prt pointing to the same instance - - for (auto & i : arrayIntAccessor.get()){ - i = 44; - } - BOOST_CHECK( secondIntArrayAccessor.get()[0] == 44 ); - - auto scalarInt = createSimpleProcessScalar("scalarInt"); - BOOST_CHECK_THROW( secondIntArrayAccessor.replace(scalarInt), std::runtime_error) ; - - auto arrayFloat = createSimpleProcessArray(10, "arrayFloat"); - BOOST_CHECK_THROW( secondIntArrayAccessor.replace(arrayFloat), std::runtime_error) ; - - // test copy construction (automatically created, but it's a feature we want - ProcessArrayAccessor thirdIntArrayAccessor( secondIntArrayAccessor ); - for (auto & i : secondIntArrayAccessor.get()){ - i = 46; - } - BOOST_CHECK( thirdIntArrayAccessor.get()[0] == 46 ); -} - -BOOST_AUTO_TEST_CASE( testArrayOperations ){ - auto firstIntArray = createSimpleProcessArray(10,"first", 42); - ProcessArrayAccessor arrayIntAccessor( firstIntArray ); - - // get the underlying vector to prepare it - int i = 45; - for ( auto & c : arrayIntAccessor.get() ){ - c = i++; - } - - // test the [] operator - for (int j = 0; static_cast(j) < arrayIntAccessor.size(); ++j){ - BOOST_CHECK( arrayIntAccessor[j] == 45+j ); - } - - // test the accessors. Start with modifiying the array. - // NOTE: We are not really testing the accessors themselves, but that we get the - // right one, so that we don't accidentally return the reverst iterator instead of - // the forward one. - for ( auto & c : arrayIntAccessor ){ - c++; - } - // Check with the [] operator, wich we already verified - for (int j = 0; static_cast(j) < arrayIntAccessor.size(); ++j){ - BOOST_CHECK( arrayIntAccessor[j] == 46+j ); - } - - // now recheck with the const iterator. Use a const object to check that it's really const. - ProcessArrayAccessor const & constArrayIntAccessor = arrayIntAccessor; - i = 46; - for (auto it = constArrayIntAccessor.cbegin(); it < constArrayIntAccessor.cend(); ++it ){ - BOOST_CHECK( (*it) == i++ ); - } - - // also check the const begin and end iterators - i = 46; - for (auto it = constArrayIntAccessor.begin(); it < constArrayIntAccessor.end(); ++it ){ - BOOST_CHECK( (*it) == i++ ); - } - - // also try the const get methods - i = 46; - for ( auto & c : constArrayIntAccessor.get() ){ - BOOST_CHECK( c == i++ ); - } - i = 46; - for ( auto & c : constArrayIntAccessor.getConst() ){ - BOOST_CHECK( c == i++ ); - } - - // Now test the reverse iterators - // Modify with the writeable one - i =78; - for (auto it = arrayIntAccessor.rbegin(); it < arrayIntAccessor.rend(); ++it){ - (*it) = i++; - } - - // check with normal, already testes iterators - i = 87; - for (auto const & c : arrayIntAccessor){ - BOOST_CHECK( c == i-- ); - } - - // check with const reverse iterators - i = 78; - for (auto it = constArrayIntAccessor.rbegin(); it < constArrayIntAccessor.rend(); ++it){ - BOOST_CHECK( (*it) == i++ ); - } - - i = 78; - for (auto it = constArrayIntAccessor.crbegin(); it < constArrayIntAccessor.crend(); ++it){ - BOOST_CHECK( (*it) == i++ ); - } - - // test swapping with a vector - std::vector v(10,42); - arrayIntAccessor.swap(v); - for (auto & c : constArrayIntAccessor ){ - BOOST_CHECK( c == 42 ); - } - i= 87; - for (auto const & c : v){ - BOOST_CHECK( c == i-- ); - } - - // set using the direct pointer - int * ptr = arrayIntAccessor.data(); - for (size_t j = 0; j < arrayIntAccessor.size(); ++j){ - ptr[j] = 13 + j; - } - - // check with iterator (already verified) - i = 13; - for (auto & c : constArrayIntAccessor ){ - BOOST_CHECK( c == i++ ); - } - - int const * cptr = constArrayIntAccessor.data(); - for (size_t j = 0; j < constArrayIntAccessor.size(); ++j){ - BOOST_CHECK( cptr[j] == static_cast(13 + j) ); - } - - -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 2661b27..36d7b11 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -51,25 +51,25 @@ template static void testCreateProcessVariables(const string& name, shared_ptr devManager, shared_ptr csManager) { - shared_ptr > createdPV = devManager->createProcessScalar( - deviceToControlSystem, name + "In"); + shared_ptr > createdPV = devManager->createProcessArray( + deviceToControlSystem, name + "In", 1); BOOST_CHECK(createdPV->getName() == name + "In"); - shared_ptr > devPV = devManager->getProcessScalar( + shared_ptr > devPV = devManager->getProcessArray( name + "In"); BOOST_CHECK(devPV->getName() == name + "In"); - shared_ptr > csPV = csManager->getProcessScalar( + shared_ptr > csPV = csManager->getProcessArray( name + "In"); BOOST_CHECK(csPV->getName() == name + "In"); - createdPV = devManager->createProcessScalar(controlSystemToDevice, - name + "Out"); + createdPV = devManager->createProcessArray(controlSystemToDevice, + name + "Out", 1); BOOST_CHECK(createdPV->getName() == name + "Out"); - devPV = devManager->getProcessScalar(name + "Out"); + devPV = devManager->getProcessArray(name + "Out"); BOOST_CHECK(devPV->getName() == name + "Out"); - csPV = csManager->getProcessScalar(name + "Out"); + csPV = csManager->getProcessArray(name + "Out"); BOOST_CHECK(csPV->getName() == name + "Out"); - BOOST_CHECK_THROW( devManager->createProcessScalar( - static_cast(-1),name + "ShouldFail"), + BOOST_CHECK_THROW( devManager->createProcessArray( + static_cast(-1),name + "ShouldFail", 1), std::invalid_argument ); string arrayName = name + "Array"; @@ -129,15 +129,15 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(deviceToControlSystem, "double"); + devManager->createProcessArray(deviceToControlSystem, "double", 1); // We expect a bad_argument exception to be thrown because the specified // PV name is already used. BOOST_CHECK_THROW( - devManager->createProcessScalar(controlSystemToDevice, "double"), + devManager->createProcessArray(controlSystemToDevice, "double", 1), std::invalid_argument); BOOST_CHECK_THROW( - devManager->createProcessScalar(deviceToControlSystem, - "double"), std::invalid_argument); + devManager->createProcessArray(deviceToControlSystem, + "double", 1), std::invalid_argument); } BOOST_AUTO_TEST_CASE( testNonExistentPVName ) { @@ -147,8 +147,8 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - BOOST_CHECK(!devManager->getProcessScalar("foo")); - BOOST_CHECK(!csManager->getProcessScalar("foo")); + BOOST_CHECK(!devManager->getProcessArray("foo")); + BOOST_CHECK(!csManager->getProcessArray("foo")); } BOOST_AUTO_TEST_CASE( testInvalidCast ) { @@ -158,27 +158,19 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(deviceToControlSystem, "double"); + devManager->createProcessArray(deviceToControlSystem, "double", 1); devManager->createProcessArray(controlSystemToDevice, "floatArray", 10); // We expect a bad_cast exception to be thrown because the specified // PV name points to a PV of a different type. - BOOST_CHECK_THROW(devManager->getProcessScalar("double"), - std::bad_cast); - BOOST_CHECK_THROW(csManager->getProcessScalar("double"), + BOOST_CHECK_THROW(devManager->getProcessArray("double"), std::bad_cast); - BOOST_CHECK_THROW(devManager->getProcessArray("double"), - std::bad_cast); - BOOST_CHECK_THROW(csManager->getProcessArray("double"), + BOOST_CHECK_THROW(csManager->getProcessArray("double"), std::bad_cast); BOOST_CHECK_THROW(devManager->getProcessArray("floatArray"), std::bad_cast); BOOST_CHECK_THROW(csManager->getProcessArray("floatArray"), std::bad_cast); - BOOST_CHECK_THROW(devManager->getProcessScalar("floatArray"), - std::bad_cast); - BOOST_CHECK_THROW(csManager->getProcessScalar("floatArray"), - std::bad_cast); } template @@ -189,22 +181,25 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) T pv = *i; const std::string& name = pv->getName(); if (name == "double") { - BOOST_CHECK(pv->isArray() == false); BOOST_CHECK(pv->getValueType() == typeid(double)); - boost::dynamic_pointer_cast, - typename T::element_type>(pv); + auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); + BOOST_CHECK(pvc->isArray() == true); + BOOST_CHECK(pvc->getNumberOfSamples() == 1); + BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundDouble = true; } else if (name == "int32") { - BOOST_CHECK(pv->isArray() == false); BOOST_CHECK(pv->getValueType() == typeid(int32_t)); - boost::dynamic_pointer_cast, - typename T::element_type>(pv); + auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); + BOOST_CHECK(pvc->isArray() == true); + BOOST_CHECK(pvc->getNumberOfSamples() == 1); + BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundInt32 = true; } else if (name == "floatArray") { - BOOST_CHECK(pv->isArray() == true); BOOST_CHECK(pv->getValueType() == typeid(float)); - boost::dynamic_pointer_cast, - typename T::element_type>(pv); + auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); + BOOST_CHECK(pvc->isArray() == true); + BOOST_CHECK(pvc->getNumberOfSamples() == 10); + BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundFloatArray = true; } else { BOOST_FAIL( @@ -222,22 +217,26 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) T pv = *i; const std::string& name = pv->getName(); if (name == "double") { - BOOST_CHECK(pv->isArray() == false); BOOST_CHECK(pv->getValueType() == typeid(double)); - boost::dynamic_pointer_cast, - typename T::element_type>(pv); + auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); + BOOST_CHECK(pvc->isArray() == true); + BOOST_CHECK(pvc->getNumberOfSamples() == 1); + BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundDouble = true; } else if (name == "int32") { - BOOST_CHECK(pv->isArray() == false); BOOST_CHECK(pv->getValueType() == typeid(int32_t)); - boost::dynamic_pointer_cast, - typename T::element_type>(pv); + auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); + BOOST_CHECK(pvc->isArray() == true); + BOOST_CHECK(pvc->getNumberOfSamples() == 1); + BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundInt32 = true; } else if (name == "floatArray") { BOOST_CHECK(pv->isArray() == true); - BOOST_CHECK(pv->getValueType() == typeid(float)); - boost::dynamic_pointer_cast, - typename T::element_type>(pv); + boost::dynamic_pointer_cast, typename T::element_type>(pv); + auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); + BOOST_CHECK(pvc->isArray() == true); + BOOST_CHECK(pvc->getNumberOfSamples() == 10); + BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundFloatArray = true; } else { BOOST_FAIL( @@ -254,8 +253,8 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(controlSystemToDevice, "double"); - devManager->createProcessScalar(deviceToControlSystem, "int32"); + devManager->createProcessArray(controlSystemToDevice, "double", 1); + devManager->createProcessArray(deviceToControlSystem, "int32", 1); devManager->createProcessArray(deviceToControlSystem, "floatArray", 10); @@ -271,24 +270,24 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr pvManager; void operator()() { - ProcessScalar::SharedPtr int32In = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32In = pvManager->getProcessArray< int32_t>("int32In"); - ProcessScalar::SharedPtr int32Out = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32Out = pvManager->getProcessArray< int32_t>("int32Out"); ProcessArray::SharedPtr floatArrayIn = pvManager->getProcessArray< float>("floatArrayIn"); ProcessArray::SharedPtr floatArrayOut = pvManager->getProcessArray< float>("floatArrayOut"); - ProcessScalar::SharedPtr stopDeviceThread = - pvManager->getProcessScalar("stopDeviceThread"); + ProcessArray::SharedPtr stopDeviceThread = + pvManager->getProcessArray("stopDeviceThread"); - int32In->set(0); - int32Out->set(0); + int32In->accessData(0) = 0; + int32Out->accessData(0) = 0; floatArrayIn->set(vector(10, 0.0)); floatArrayOut->set(vector(10, 0.0)); - stopDeviceThread->set(0); + stopDeviceThread->accessData(0) = 0; - while (stopDeviceThread->get() == 0) { + while (stopDeviceThread->accessData(0) == 0) { *int32In = *int32Out; *floatArrayIn = *floatArrayOut; int32In->write(); @@ -308,14 +307,14 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(deviceToControlSystem, "int32In"); - devManager->createProcessScalar(controlSystemToDevice, "int32Out"); + devManager->createProcessArray(deviceToControlSystem, "int32In", 1); + devManager->createProcessArray(controlSystemToDevice, "int32Out", 1); devManager->createProcessArray(deviceToControlSystem, "floatArrayIn", 10); devManager->createProcessArray(controlSystemToDevice, "floatArrayOut", 10); - devManager->createProcessScalar(controlSystemToDevice, - "stopDeviceThread"); + devManager->createProcessArray(controlSystemToDevice, + "stopDeviceThread", 1); TestDeviceCallable callable; callable.pvManager = devManager; @@ -329,16 +328,16 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_AUTO_TEST_CASE( testSynchronization ) { shared_ptr pvManager = initTestDeviceLib(); - ProcessScalar::SharedPtr int32In = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32In = pvManager->getProcessArray< int32_t>("int32In"); - ProcessScalar::SharedPtr int32Out = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32Out = pvManager->getProcessArray< int32_t>("int32Out"); ProcessArray::SharedPtr floatArrayIn = pvManager->getProcessArray< float>("floatArrayIn"); ProcessArray::SharedPtr floatArrayOut = pvManager->getProcessArray< float>("floatArrayOut"); - ProcessScalar::SharedPtr stopDeviceThread = - pvManager->getProcessScalar("stopDeviceThread"); + ProcessArray::SharedPtr stopDeviceThread = + pvManager->getProcessArray("stopDeviceThread"); list inboundProcessVariables; inboundProcessVariables.push_back(int32In); @@ -349,7 +348,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) outboundProcessVariables.push_back(floatArrayOut); outboundProcessVariables.push_back(stopDeviceThread); - *int32Out = 55; + int32Out->accessData(0) = 55; floatArrayOut->get().at(0) = 1.0f; floatArrayOut->get().at(1) = 2.0f; floatArrayOut->get().at(2) = -1.3f; @@ -360,12 +359,12 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) boost::this_thread::sleep_for(boost::chrono::milliseconds(15)); receiveAll(inboundProcessVariables); - BOOST_CHECK(*int32In == 55); + BOOST_CHECK(int32In->accessData(0) == 55); BOOST_CHECK(floatArrayIn->get().at(0) == 1.0f); BOOST_CHECK(floatArrayIn->get().at(1) == 2.0f); BOOST_CHECK(floatArrayIn->get().at(2) == -1.3f); - *int32Out = -300; + int32Out->accessData(0) = -300; floatArrayOut->get().at(0) = 15.0f; floatArrayOut->get().at(1) = -7.2f; floatArrayOut->get().at(9) = 120.0f; @@ -376,12 +375,12 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) boost::this_thread::sleep_for(boost::chrono::milliseconds(15)); receiveAll(inboundProcessVariables); - BOOST_CHECK(*int32In == -300); + BOOST_CHECK(int32In->accessData(0) == -300); BOOST_CHECK(floatArrayIn->get().at(0) == 15.0f); BOOST_CHECK(floatArrayIn->get().at(1) == -7.2f); BOOST_CHECK(floatArrayIn->get().at(9) == 120.0f); - *stopDeviceThread = 1; + stopDeviceThread->accessData(0) = 1; stopDeviceThread->write(); } @@ -389,32 +388,32 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr pvManager; void operator()() { - ProcessScalar::SharedPtr int32In = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32In = pvManager->getProcessArray< int32_t>("int32In"); - ProcessScalar::SharedPtr doubleIn = pvManager->getProcessScalar< + ProcessArray::SharedPtr doubleIn = pvManager->getProcessArray< double>("doubleIn"); - ProcessScalar::SharedPtr stopDeviceThread = - pvManager->getProcessScalar("stopDeviceThread"); + ProcessArray::SharedPtr stopDeviceThread = + pvManager->getProcessArray("stopDeviceThread"); int i = 0; - while (stopDeviceThread->get() == 0) { + while (stopDeviceThread->accessData(0) == 0) { if (i == 0) { // Flood notifications for first process variable before sending a // notification for a second one. This tests the mechanism that // prevents the queue from being filled with notifications for a // single process variable. while (i < 50000) { - int32In->set(i); + int32In->accessData(0) = i; ++i; int32In->write(); } - doubleIn->set(2.0); + doubleIn->accessData(0) = 2.0; doubleIn->write(); } else { // After the flood test, we send notifications in regular intervals // to ensure that the reset mechanism works (multiple notifications // can be sent if they are collected). - int32In->set(55); + int32In->accessData(0) = 55; int32In->write(); } boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); @@ -430,10 +429,10 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(deviceToControlSystem, "int32In"); - devManager->createProcessScalar(deviceToControlSystem, "doubleIn"); - devManager->createProcessScalar(controlSystemToDevice, - "stopDeviceThread"); + devManager->createProcessArray(deviceToControlSystem, "int32In", 1); + devManager->createProcessArray(deviceToControlSystem, "doubleIn", 1); + devManager->createProcessArray(controlSystemToDevice, + "stopDeviceThread", 1); TestDeviceCallable2 callable; callable.pvManager = devManager; @@ -447,12 +446,12 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_AUTO_TEST_CASE( testNotificationToControlSystem ) { shared_ptr pvManager = initTestDeviceLib2(); - ProcessScalar::SharedPtr int32In = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32In = pvManager->getProcessArray< int32_t>("int32In"); - ProcessScalar::SharedPtr doubleIn = pvManager->getProcessScalar< + ProcessArray::SharedPtr doubleIn = pvManager->getProcessArray< double>("doubleIn"); - ProcessScalar::SharedPtr stopDeviceThread = - pvManager->getProcessScalar("stopDeviceThread"); + ProcessArray::SharedPtr stopDeviceThread = + pvManager->getProcessArray("stopDeviceThread"); int int32NotificationCount = 0; int doubleNotificationCount = 0; @@ -478,7 +477,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_CHECK(int32NotificationCount >= 5); BOOST_CHECK(doubleNotificationCount >= 1); - *stopDeviceThread = 1; + stopDeviceThread->accessData(0) = 1; stopDeviceThread->write(); } @@ -486,12 +485,12 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr pvManager; void operator()() { - ProcessScalar::SharedPtr int32In = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32In = pvManager->getProcessArray< int32_t>("int32Out"); - ProcessScalar::SharedPtr doubleIn = pvManager->getProcessScalar< + ProcessArray::SharedPtr doubleIn = pvManager->getProcessArray< double>("doubleOut"); - ProcessScalar::SharedPtr stopControlSystemThread = - pvManager->getProcessScalar("stopControlSystemThread"); + ProcessArray::SharedPtr stopControlSystemThread = + pvManager->getProcessArray("stopControlSystemThread"); int int32NotificationCount = 0; int doubleNotificationCount = 0; @@ -517,7 +516,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_CHECK(int32NotificationCount >= 5); BOOST_CHECK(doubleNotificationCount >= 1); - *stopControlSystemThread = 1; + stopControlSystemThread->accessData(0) = 1; stopControlSystemThread->write(); } }; @@ -529,10 +528,10 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(controlSystemToDevice, "int32Out"); - devManager->createProcessScalar(controlSystemToDevice, "doubleOut"); - devManager->createProcessScalar(deviceToControlSystem, - "stopControlSystemThread"); + devManager->createProcessArray(controlSystemToDevice, "int32Out", 1); + devManager->createProcessArray(controlSystemToDevice, "doubleOut", 1); + devManager->createProcessArray(deviceToControlSystem, + "stopControlSystemThread", 1); TestDeviceCallable3 callable; callable.pvManager = devManager; @@ -546,32 +545,32 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_AUTO_TEST_CASE( testNotificationToDevice ) { shared_ptr pvManager = initTestDeviceLib3(); - ProcessScalar::SharedPtr int32Out = pvManager->getProcessScalar< + ProcessArray::SharedPtr int32Out = pvManager->getProcessArray< int32_t>("int32Out"); - ProcessScalar::SharedPtr doubleOut = pvManager->getProcessScalar< + ProcessArray::SharedPtr doubleOut = pvManager->getProcessArray< double>("doubleOut"); - ProcessScalar::SharedPtr stopControlSystemThread = - pvManager->getProcessScalar("stopControlSystemThread"); + ProcessArray::SharedPtr stopControlSystemThread = + pvManager->getProcessArray("stopControlSystemThread"); int i = 0; - while (stopControlSystemThread->get() == 0) { + while (stopControlSystemThread->accessData(0) == 0) { if (i == 0) { // Flood notifications for first process variable before sending a // notification for a second one. This tests the mechanism that // prevents the queue from being filled with notifications for a // single process variable. while (i < 50000) { - int32Out->set(i); + int32Out->accessData(0) = i; ++i; int32Out->write(); } - doubleOut->set(2.0); + doubleOut->accessData(0) = 2.0; doubleOut->write(); } else { // After the flood test, we send notifications in regular intervals // to ensure that the reset mechanism works (multiple notifications // can be sent if they are collected). - int32Out->set(55); + int32Out->accessData(0) = 55; int32Out->write(); } boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); @@ -583,14 +582,14 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr pvManager; void operator()() { - ProcessScalar::SharedPtr intA = pvManager->getProcessScalar< + ProcessArray::SharedPtr intA = pvManager->getProcessArray< uint32_t>("intA"); - ProcessScalar::SharedPtr intB = pvManager->getProcessScalar< + ProcessArray::SharedPtr intB = pvManager->getProcessArray< uint32_t>("intB"); - ProcessScalar::SharedPtr index0 = pvManager->getProcessScalar< + ProcessArray::SharedPtr index0 = pvManager->getProcessArray< uint32_t>("index0"); - ProcessScalar::SharedPtr stopDeviceThread = - pvManager->getProcessScalar("stopDeviceThread"); + ProcessArray::SharedPtr stopDeviceThread = + pvManager->getProcessArray("stopDeviceThread"); uint32_t nextIndexNumber = 0; @@ -598,26 +597,26 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_CHECK(pvManager->isAutomaticReferenceTimeStampMode()); pvManager->setReferenceTimeStamp(TimeStamp::currentTime(nextIndexNumber)); - index0->set(nextIndexNumber); + index0->accessData(0) = nextIndexNumber; ++nextIndexNumber; // Setting a reference time-stamp should implicitly disable the automatic // mode. BOOST_CHECK(!pvManager->isAutomaticReferenceTimeStampMode()); - intA->set(0); - intB->set(0); + intA->accessData(0) = 0; + intB->accessData(0) = 0; - while (stopDeviceThread->get() == 0) { + while (stopDeviceThread->accessData(0) == 0) { // On each iteration, we set the current time-stamp. This should ensure // that all PVs receive the same time-stamp. pvManager->setReferenceTimeStamp( TimeStamp::currentTime(nextIndexNumber)); - index0->set(nextIndexNumber); + index0->accessData(0) = nextIndexNumber; ++nextIndexNumber; - intA->set(intA->get() + 2); - intB->set(intB->get() + 2); + intA->accessData(0) = intA->accessData(0) + 2; + intB->accessData(0) = intB->accessData(0) + 2; index0->write(); intA->write(); @@ -635,11 +634,11 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - devManager->createProcessScalar(deviceToControlSystem, "intA"); - devManager->createProcessScalar(deviceToControlSystem, "intB"); - devManager->createProcessScalar(deviceToControlSystem, "index0"); - devManager->createProcessScalar(controlSystemToDevice, - "stopDeviceThread"); + devManager->createProcessArray(deviceToControlSystem, "intA", 1); + devManager->createProcessArray(deviceToControlSystem, "intB", 1); + devManager->createProcessArray(deviceToControlSystem, "index0", 1); + devManager->createProcessArray(controlSystemToDevice, + "stopDeviceThread", 1); TestDeviceCallable4 callable; callable.pvManager = devManager; @@ -653,24 +652,24 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) BOOST_AUTO_TEST_CASE( testTimeStamps ) { shared_ptr pvManager = initTestDeviceLib4(); - ProcessScalar::SharedPtr intA = pvManager->getProcessScalar< + ProcessArray::SharedPtr intA = pvManager->getProcessArray< uint32_t>("intA"); - ProcessScalar::SharedPtr intB = pvManager->getProcessScalar< + ProcessArray::SharedPtr intB = pvManager->getProcessArray< uint32_t>("intB"); - ProcessScalar::SharedPtr index0 = pvManager->getProcessScalar< + ProcessArray::SharedPtr index0 = pvManager->getProcessArray< uint32_t>("index0"); - ProcessScalar::SharedPtr stopDeviceThread = - pvManager->getProcessScalar("stopDeviceThread"); + ProcessArray::SharedPtr stopDeviceThread = + pvManager->getProcessArray("stopDeviceThread"); list inboundProcessVariables; inboundProcessVariables.push_back(intA); inboundProcessVariables.push_back(intB); inboundProcessVariables.push_back(index0); - while (*index0 < 150) { - uint32_t i0 = *index0; - uint32_t a = *intA; - uint32_t b = *intB; + while (index0->accessData(0) < 150) { + uint32_t i0 = index0->accessData(0); + uint32_t a = intA->accessData(0); + uint32_t b = intB->accessData(0); TimeStamp i0TS = index0->getTimeStamp(); TimeStamp aTS = intA->getTimeStamp(); TimeStamp bTS = intB->getTimeStamp(); @@ -696,7 +695,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) receiveAll(inboundProcessVariables); } - *stopDeviceThread = 1; + stopDeviceThread->accessData(0) = 1; stopDeviceThread->write(); } @@ -707,13 +706,13 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - ProcessScalar::SharedPtr intAdev = devManager->createProcessScalar< - int32_t>(deviceToControlSystem, "intA"); - ProcessScalar::SharedPtr intBdev = devManager->createProcessScalar< - int32_t>(deviceToControlSystem, "intB"); - ProcessScalar::SharedPtr intAcs = csManager->getProcessScalar< + ProcessArray::SharedPtr intAdev = devManager->createProcessArray< + int32_t>(deviceToControlSystem, "intA",1); + ProcessArray::SharedPtr intBdev = devManager->createProcessArray< + int32_t>(deviceToControlSystem, "intB",1); + ProcessArray::SharedPtr intAcs = csManager->getProcessArray< int32_t>("intA"); - ProcessScalar::SharedPtr intBcs = csManager->getProcessScalar< + ProcessArray::SharedPtr intBcs = csManager->getProcessArray< int32_t>("intB"); // By default, the PV manager should be in automatic time-stamp mode. diff --git a/tests/src/testProcessScalar.cpp b/tests/src/testProcessScalar.cpp deleted file mode 100644 index ede8d45..0000000 --- a/tests/src/testProcessScalar.cpp +++ /dev/null @@ -1,302 +0,0 @@ -#include - -#include -using namespace boost::unit_test_framework; - -#include "ProcessScalar.h" - -#include "CountingProcessVariableListener.h" -#include "CountingTimeStampSource.h" - -namespace ChimeraTK { - /** - * The test class for the ProcessScalar. - * It is templated to be tested with all data types. - */ - template - struct ProcessScalarTest { - static void testConstructors(); - static void testAssignment(); - static void testConversion(); - static void testGet(); - static void testSet(); - static void testSendNotification(); - static void testTimeStampSource(); - static void testSynchronization(); - static void testVersionNumbers(); - }; - - /** - * The boost test suite which executes the ProcessScalarTest. - */ - template - class ProcessScalarTestSuite: public test_suite { - public: - ProcessScalarTestSuite() : - test_suite("ProcessScalar test suite") { - add(BOOST_TEST_CASE(&ProcessScalarTest::testConstructors)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testAssignment)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testConversion)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testSet)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testSendNotification)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testTimeStampSource)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testSynchronization)); - add(BOOST_TEST_CASE(&ProcessScalarTest::testVersionNumbers)); - } - }; - - template - void ProcessScalarTest::testConstructors() { - typename ProcessScalar::SharedPtr simpleScalar = - createSimpleProcessScalar(); - // The name should be empty and the value should have been initialized with - // zero. - BOOST_CHECK(simpleScalar->getName() == ""); - BOOST_CHECK(simpleScalar->get() == 0); - BOOST_CHECK(!simpleScalar->isReadable()); - BOOST_CHECK(!simpleScalar->isWriteable()); - // Now we test the constructor with non-default parameters. - simpleScalar = createSimpleProcessScalar("test", 42); - BOOST_CHECK(simpleScalar->getName() == "test"); - BOOST_CHECK(simpleScalar->get() == 42); - - // Now we repeat the tests but for a sender / receiver pair. We do not test - // the notification listener and time-stamp source because there is no - // simple test for them and they are tested in a different method. - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> senderReceiver = - createSynchronizedProcessScalar(); - typename ProcessScalar::SharedPtr sender = senderReceiver.first; - typename ProcessScalar::SharedPtr receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == ""); - BOOST_CHECK(sender->get() == 0); - BOOST_CHECK(receiver->getName() == ""); - BOOST_CHECK(receiver->get() == 0); - BOOST_CHECK(!sender->isReadable()); - BOOST_CHECK(sender->isWriteable()); - BOOST_CHECK(receiver->isReadable()); - BOOST_CHECK(!receiver->isWriteable()); - senderReceiver = createSynchronizedProcessScalar("test", 42); - sender = senderReceiver.first; - receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == "test"); - BOOST_CHECK(sender->get() == 42); - BOOST_CHECK(receiver->getName() == "test"); - BOOST_CHECK(receiver->get() == 42); - } - - template - void ProcessScalarTest::testAssignment() { - // It is sufficient to run the test for the simple variant because the - // variants do not differ in their assignment operator and set method - // implementation. - typename ProcessScalar::SharedPtr scalar1 = - createSimpleProcessScalar(); - typename ProcessScalar::SharedPtr scalar2 = createSimpleProcessScalar( - "", 42); - // Test the assignment of another process scalar. - (*scalar1) = (*scalar2); - BOOST_CHECK(scalar1->get() == 42); - // Test the assignment of a raw number. - (*scalar1) = 23; - BOOST_CHECK(scalar1->get() == 23); - } - - template - void ProcessScalarTest::testConversion() { - // It is sufficient to run the test for the simple variant because the - // variants do not differ in their conversion operator implementation. - T number = 42; - typename ProcessScalar::SharedPtr simpleScalar = - createSimpleProcessScalar("", number); - T otherNumber = (*simpleScalar); - BOOST_CHECK(number == otherNumber); - } - - template - void ProcessScalarTest::testSet() { - // It is sufficient to run the test for the simple variant because the - // variants do not differ in their assignment operator and set method - // implementation. - typename ProcessScalar::SharedPtr scalar1 = - createSimpleProcessScalar(); - typename ProcessScalar::SharedPtr scalar2 = createSimpleProcessScalar( - "", 42); - // Test the assignment of another process scalar. - scalar1->set(*scalar2); - BOOST_CHECK(scalar1->get() == 42); - // Test the assignment of a raw number. - scalar1->set(23); - BOOST_CHECK(scalar1->get() == 23); - } - - template - void ProcessScalarTest::testSendNotification() { - boost::shared_ptr sendNotificationListener( - boost::make_shared()); - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> senderReceiver = - createSynchronizedProcessScalar("", 0, 1, - TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr(), - sendNotificationListener); - typename ProcessScalar::SharedPtr sender = senderReceiver.first; - typename ProcessScalar::SharedPtr receiver = senderReceiver.second; - BOOST_CHECK(sendNotificationListener->count == 0); - sender->write(); - BOOST_CHECK(sendNotificationListener->count == 1); - BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); - sender->write(); - BOOST_CHECK(sendNotificationListener->count == 2); - BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); - } - - template - void ProcessScalarTest::testTimeStampSource() { - TimeStampSource::SharedPtr timeStampSource( - boost::make_shared()); - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> senderReceiver = - createSynchronizedProcessScalar("", 0, 1, timeStampSource); - typename ProcessScalar::SharedPtr sender = senderReceiver.first; - typename ProcessScalar::SharedPtr receiver = senderReceiver.second; - sender->write(); - receiver->readNonBlocking(); - BOOST_CHECK(receiver->getTimeStamp().index0 == 0); - sender->write(); - receiver->readNonBlocking(); - BOOST_CHECK(receiver->getTimeStamp().index0 == 1); - sender->write(); - receiver->readNonBlocking(); - BOOST_CHECK(receiver->getTimeStamp().index0 == 2); - } - - template - void ProcessScalarTest::testSynchronization() { - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> senderReceiver = - createSynchronizedProcessScalar(); - typename ProcessScalar::SharedPtr sender = senderReceiver.first; - typename ProcessScalar::SharedPtr receiver = senderReceiver.second; - *sender = 42; - sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 42); - // No more values should be available. - BOOST_CHECK(!receiver->readNonBlocking()); - *sender = 43; - sender->write(); - *sender = 44; - // We send again, and the queue will overflow, so when reading we will lose the 43 and just see 44 - sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 44); - BOOST_CHECK(!receiver->readNonBlocking()); - // Now we create a sender/receiver pair with a slight larger queue and run - // the test again to check that we can transfer more data without losing - // values. - senderReceiver = createSynchronizedProcessScalar("", 0, 2); - sender = senderReceiver.first; - receiver = senderReceiver.second; - *sender = 42; - sender->write(); - *sender = 43; - sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 42); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 43); - // No more values should be available. - BOOST_CHECK(!receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 43); - *sender = 44; - sender->write(); - *sender = 45; - sender->write(); - *sender = 46; - // When sending this value, we should lose the value we sent first (44). - sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 45); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 46); - // No more values should be available. - BOOST_CHECK(!receiver->readNonBlocking()); - BOOST_CHECK(*receiver == 46); - } - - template - void ProcessScalarTest::testVersionNumbers() { - VersionNumberSource::SharedPtr versionNumberSource = boost::make_shared< - VersionNumberSource>(); - typename std::pair::SharedPtr, - typename ProcessScalar::SharedPtr> senderReceiver = - createSynchronizedProcessScalar("", 0, 1, - TimeStampSource::SharedPtr(), versionNumberSource); - typename ProcessScalar::SharedPtr sender = senderReceiver.first; - typename ProcessScalar::SharedPtr receiver = senderReceiver.second; - // Initially, the version number should be zero. - BOOST_CHECK(sender->getVersionNumber() == 0); - BOOST_CHECK(receiver->getVersionNumber() == 0); - // After sending a value, the version number of the sender should be greater - // than the version number of the receiver. After receiving that value, the - // version numbers should match. - *sender = 1; - sender->write(); - BOOST_CHECK(sender->getVersionNumber() > receiver->getVersionNumber()); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); - BOOST_CHECK(*receiver == 1); - // Sending another value should have the same effect. - *sender = 2; - sender->write(); - BOOST_CHECK(sender->getVersionNumber() > receiver->getVersionNumber()); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); - BOOST_CHECK(*receiver == 2); - // Sending a value with the same version number should not result in an - // update on the receiver side. - *sender = 3; - sender->write(sender->getVersionNumber()); - BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); - BOOST_CHECK(!receiver->readNonBlocking()); - BOOST_CHECK(sender->getVersionNumber() == receiver->getVersionNumber()); - BOOST_CHECK(*receiver == 2); - // Finally we send an update with a version number that we explicitly get - // from the version number source. - VersionNumber newVersionNumber = versionNumberSource->nextVersionNumber(); - *sender = 4; - sender->write(newVersionNumber); - BOOST_CHECK(sender->getVersionNumber() == newVersionNumber); - BOOST_CHECK(sender->getVersionNumber() > receiver->getVersionNumber()); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(receiver->getVersionNumber() == newVersionNumber); - BOOST_CHECK(sender->getVersionNumber() == newVersionNumber); - BOOST_CHECK(*receiver == 4); - } - -} //namespace ChimeraTK - -test_suite* -init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { - framework::master_test_suite().p_name.value = "ProcessScalar test suite"; - - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - framework::master_test_suite().add( - new ChimeraTK::ProcessScalarTestSuite); - - return NULL; -} - diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index abedf52..8cb609f 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -26,12 +26,12 @@ BOOST_AUTO_TEST_CASE( testInt32_t ){ ControlSystemSynchronizationUtility csSyncUtil(csManager); - auto toDeviceScalar = csManager->getProcessScalar("INT/TO_DEVICE_SCALAR"); - auto fromDeviceScalar = csManager->getProcessScalar("INT/FROM_DEVICE_SCALAR"); + auto toDeviceScalar = csManager->getProcessArray("INT/TO_DEVICE_SCALAR"); + auto fromDeviceScalar = csManager->getProcessArray("INT/FROM_DEVICE_SCALAR"); - int32_t previousReadValue = *fromDeviceScalar; + int32_t previousReadValue = fromDeviceScalar->accessData(0); - *toDeviceScalar = previousReadValue+13; + toDeviceScalar->accessData(0) = previousReadValue+13; csSyncUtil.sendAll(); @@ -40,13 +40,13 @@ BOOST_AUTO_TEST_CASE( testInt32_t ){ int i = 0; for ( ; i < 1000 ; ++i){ csSyncUtil.receiveAll(); - if ( *fromDeviceScalar == previousReadValue+13){ + if ( fromDeviceScalar->accessData(0) == previousReadValue+13){ break; } boost::this_thread::sleep_for( boost::chrono::milliseconds(100) ); } - BOOST_CHECK_MESSAGE( i< 1000, "Reading the correct value timed out." ); + BOOST_CHECK_MESSAGE( i < 1000, "Reading the correct value timed out." ); } From d8f488bcd7e51e5caa34cc76900a2e1755e15278 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 9 Nov 2016 12:46:15 +0100 Subject: [PATCH 121/786] added CXX_FLAGS from DeviceAccess to the compiler flags --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 700f006..e047cc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ endif(TESTING_IS_ENABLED) add_library(${PROJECT_NAME} SHARED ${library_sources} ) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION} SOVERSION ${${PROJECT_NAME}_SOVERSION}) -target_link_libraries(${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY}) +target_link_libraries(${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) #change the install prefix to the source directory in case the user has not specified a destination #i. e. CMAKE_INSTALL_PREFIX is not set manually @@ -118,7 +118,7 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIRS}") set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}") set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS}") -set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11") +set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11 ${mtca4u-deviceaccess_CXX_FLAGS}") set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags") include(${CMAKE_SOURCE_DIR}/cmake/create_cmake_config_files.cmake) From 5c7e9efe63e640f02937515c3edd6a0c76ba5a6f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 9 Nov 2016 13:20:57 +0100 Subject: [PATCH 122/786] updated Doxygen documentation --- .../ControlSystemSynchronizationUtility.h | 8 ++-- .../ControlSystemAdapter/DevicePVManager.h | 1 - .../DeviceSynchronizationUtility.h | 8 ++-- .../ControlSystemAdapter/ProcessArray.h | 40 +++++++++---------- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index e8c287b..88928f9 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -65,7 +65,7 @@ namespace ChimeraTK { * Run the receive operation on a list of process variables. This can be any * type of container that has begin() and end() * methods that return an appropriate iterator. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void receive(T processVariables); @@ -74,7 +74,7 @@ namespace ChimeraTK { * Run the receive operation on a list of process variables. The list is * specified in the form of a begin and end iterator. For example, this can * be an STL iterator or a raw pointer. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void receive(T processVariablesBegin, T processVariablesEnd); @@ -83,7 +83,7 @@ namespace ChimeraTK { * Run the send operation on a list of process variables. This can be any * type of container that has begin() and end() * methods that return an appropriate iterator. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void send(T processVariables); @@ -92,7 +92,7 @@ namespace ChimeraTK { * Run the send operation on a list of process variables. The list is * specified in the form of a begin and end iterator. For example, this can * be an STL iterator or a raw pointer. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void send(T processVariablesBegin, T processVariablesEnd); diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 57d5e76..75f4bec 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -132,7 +132,6 @@ namespace ChimeraTK { /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link createProcessScalar(SynchronizationDirection, const std::string&, T, std::size_t)}, * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)}, * or * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} diff --git a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h index 4ed683e..005c0d4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h @@ -65,7 +65,7 @@ namespace ChimeraTK { * Run the receive operation on a list of process variables. This can be any * type of container that has begin() and end() * methods that return an appropriate iterator. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void receive(T processVariables); @@ -74,7 +74,7 @@ namespace ChimeraTK { * Run the receive operation on a list of process variables. The list is * specified in the form of a begin and end iterator. For example, this can * be an STL iterator or a raw pointer. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void receive(T processVariablesBegin, T processVariablesEnd); @@ -83,7 +83,7 @@ namespace ChimeraTK { * Run the send operation on a list of process variables. This can be any * type of container that has begin() and end() * methods that return an appropriate iterator. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void send(T processVariables); @@ -92,7 +92,7 @@ namespace ChimeraTK { * Run the send operation on a list of process variables. The list is * specified in the form of a begin and end iterator. For example, this can * be an STL iterator or a raw pointer. The elements must be of type - * {@link ProcessVariable::SharedPtr}. + * ProcessVariable::SharedPtr. */ template void send(T processVariablesBegin, T processVariablesEnd); diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 0a01be9..4131d33 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -155,13 +155,13 @@ namespace ChimeraTK { * receiver. * * If the maySendDestructively flag is true, the - * {@link sendDestructively()} method may be used to transfer values without + * {@link ProcessArray::writeDestructively()} method may be used to transfer values without * copying but losing them on the sender side. * * The optional send-notification listener is notified every time the - * sender's {@link ProcessArray::send()} method is called. It can be + * sender's {@link ProcessArray::write()} method is called. It can be * used to queue a request for the receiver's - * {@link ProcessArray::receive()} method to be called. The process + * {@link ProcessArray::readNonBlocking()} method to be called. The process * variable passed to the listener is the receiver and not the sender. */ ProcessArray(InstanceType instanceType, bool maySendDestructively, @@ -303,13 +303,13 @@ namespace ChimeraTK { /** * Returns the version number that is associated with the current value. - * This is the version number that was received with the {@link receive()} - * operation that received the respective value or the last {@link send()} + * This is the version number that was received with the {@link readNonBlocking()} + * operation that received the respective value or the last {@link write()} * operation. If the last send operation was destructive, the version number * (like the current value) is undefined. * * The version number is used to resolve conflicting updates. When an update - * is received using the {@link receive()} method, it is only used if its + * is received using the {@link readNonBlocking()} method, it is only used if its * value has a version number that is greater than the version number of the * current value. Initially, each process variable has a version number of * zero. @@ -678,8 +678,8 @@ namespace ChimeraTK { * * The sender allows full read-write access. Changes that have been made to * the sender can be sent to the receiver through the - * {@link ProcessArray::send()} method. The receiver can be updated with these - * changes by calling its {@link ProcessArray::receive()} method. + * {@link ProcessArray::write()} method. The receiver can be updated with these + * changes by calling its {@link ProcessArray::readNonBlocking()} method. * * The synchronization is implemented in a thread-safe manner, so that the * sender and the receiver can safely be used by different threads without @@ -689,12 +689,12 @@ namespace ChimeraTK { * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::send()} can be called - * in a row without losing data when {@link ProcessArray::receive()} is not + * number specifies, how many times {@link ProcessArray::write()} can be called + * in a row without losing data when {@link ProcessArray::readNonBlocking()} is not * called in between. * * If the maySendDestructively flag is true (it is - * false by default), the {@link sendDestructively()} method may + * false by default), the {@link ProcessArray::writeDestructively()} method may * be used to transfer values without copying but losing them on the sender * side. * @@ -704,8 +704,8 @@ namespace ChimeraTK { * system-time when the value is sent is used. * * The optional send-notification listener is notified every time the sender's - * {@link ProcessArray::send()} method is called. It can be used to queue a - * request for the receiver's {@link ProcessArray::receive()} method to be + * {@link ProcessArray::write()} method is called. It can be used to queue a + * request for the receiver's {@link ProcessArray::readNonBlocking()} method to be * called. The process variable passed to the listener is the receiver and * not the sender. * @@ -729,8 +729,8 @@ namespace ChimeraTK { * * The sender allows full read-write access. Changes that have been made to * the sender can be sent to the receiver through the - * {@link ProcessArray::send()} method. The receiver can be updated with these - * changes by calling its {@link ProcessArray::receive()} method. + * {@link ProcessArray::write()} method. The receiver can be updated with these + * changes by calling its {@link ProcessArray::readNonBlocking()} method. * * The synchronization is implemented in a thread-safe manner, so that the * sender and the receiver can safely be used by different threads without @@ -740,12 +740,12 @@ namespace ChimeraTK { * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::send()} can be called - * in a row without losing data when {@link ProcessArray::receive()} is not + * number specifies, how many times {@link ProcessArray::write()} can be called + * in a row without losing data when {@link ProcessArray::readNonBlocking()} is not * called in between. * * If the maySendDestructively flag is true (it is - * false by default), the {@link sendDestructively()} method may + * false by default), the {@link ProcessArray::writeDestructively()} method may * be used to transfer values without copying but losing them on the sender * side. * @@ -755,8 +755,8 @@ namespace ChimeraTK { * system-time when the value is sent is used. * * The optional send-notification listener is notified every time the sender's - * {@link ProcessArray::send()} method is called. It can be used to queue a - * request for the receiver's {@link ProcessArray::receive()} method to be + * {@link ProcessArray::write()} method is called. It can be used to queue a + * request for the receiver's {@link ProcessArray::readNonBlocking()} method to be * called. The process variable passed to the listener is the receiver and * not the sender. * From 1541d20aa499dbef303fd98a5f4c428f0d61ed55 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 11 Nov 2016 10:43:23 +0100 Subject: [PATCH 123/786] Added ApplicationBase class as a basis for applications. This class has been split off the Application class of the ApplicationCore (hence the name). --- .../ControlSystemAdapter/ApplicationBase.h | 84 +++++++++++++++++++ src/ApplicationBase.cc | 70 ++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h create mode 100644 src/ApplicationBase.cc diff --git a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h new file mode 100644 index 0000000..dc8cfbe --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h @@ -0,0 +1,84 @@ +/* + * ApplicationBase.h + * + * Created on: Nov 11, 2016 + * Author: Martin Hierholzer + */ + +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_APPLICATION_BASE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_APPLICATION_BASE_H + +#include + +#include "DevicePVManager.h" + +namespace ChimeraTK { + + /** Base class for applications. Note: The class intended for use by the application programmers is part of the + * ApplicationCore library and called ChimeraTK::Application. ApplicationBase just contains the part which is + * necessary to write the middleware-specific code initialising the application. */ + class ApplicationBase { + + public: + + /** Constructor: the first instance will be created explicitly by the control system adapter code. Any second + * instance is not allowed, thus calling the constructor multiple times will throw an exception. + * Design note: We are not using a true singleton pattern, since Application is an abstract base class. The + * actual instance is created as a static variable. + * The application developer should derive his application from this class and implement the initialise() + * function only. */ + ApplicationBase(const std::string& name); + + /** The implementation of Application must call Application::shutdown() in its destructor. This destructor just + * checks if Application::shutdown() was properly called and throws an exception otherwise. */ + virtual ~ApplicationBase(); + + /** Initialise and run the application. This function must be implemented by the application programmer. */ + virtual void run() = 0; + + /** This will remove the global pointer to the instance and allows creating another instance + * afterwards. This is mostly useful for writing tests, as it allows to run several applications sequentially + * in the same executable. It is mandatory to call this function inside the descructor of the implemention + * of this class. */ + virtual void shutdown(); + + /** Set the process variable manager. This will be called by the control system adapter initialisation code. */ + void setPVManager(boost::shared_ptr const &processVariableManager); + + /** Obtain the process variable manager. */ + boost::shared_ptr getPVManager() { + return _processVariableManager; + } + + /** Obtain instance of the application. Will throw an exception if called before the instance has been + * created by the control system adapter. */ + static ApplicationBase& getInstance(); + + /** Return the name of the application */ + const std::string& getName() { + return applicationName; + } + + protected: + + /** The name of the application */ + std::string applicationName; + + /** Pointer to the process variable manager used to create variables exported to the control system */ + boost::shared_ptr _processVariableManager; + + /** Flag if shutdown() has been called. */ + bool hasBeenShutdown{false}; + + /** Pointer to the only instance of the Application */ + static ApplicationBase *instance; + + /** Mutex for thread-safety when setting the instance pointer */ + static std::mutex instance_mutex; + + }; + +} /* namespace ChimeraTK */ + +#endif /* CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_APPLICATION_BASE_H */ + diff --git a/src/ApplicationBase.cc b/src/ApplicationBase.cc new file mode 100644 index 0000000..83a78a4 --- /dev/null +++ b/src/ApplicationBase.cc @@ -0,0 +1,70 @@ +/* + * ApplicationBase.cc + * + * Created on: Nov 11, 2016 + * Author: Martin Hierholzer + */ + +#include "ApplicationBase.h" + +namespace ChimeraTK { + + /*********************************************************************************************************************/ + + ApplicationBase *ApplicationBase::instance = nullptr; + std::mutex ApplicationBase::instance_mutex; + + /*********************************************************************************************************************/ + + ApplicationBase::ApplicationBase(const std::string& name) + : applicationName(name) + { + std::lock_guard lock(instance_mutex); + if(instance != nullptr) { + throw std::runtime_error("Multiple instances of ChimeraTK::ApplicationBase cannot be created."); + } + instance = this; + } + + /*********************************************************************************************************************/ + + ApplicationBase::~ApplicationBase() { + if(!hasBeenShutdown) { + std::cerr << "*****************************************************************************" << std::endl; + std::cerr << " BUG found in application " << applicationName << "!" << std::endl; + std::cerr << " Its implementation of the class Application must have a destructor which" << std::endl; + std::cerr << " calls Application::shutdown()." << std::endl; + std::cerr << " Since the application was not shutdown properly, we are now about to crash." << std::endl; + std::cerr << " Please fix your application!" << std::endl; + std::cerr << "*****************************************************************************" << std::endl; + std::terminate(); + } + } + + /*********************************************************************************************************************/ + + void ApplicationBase::shutdown() { + + // finally clear the global instance pointer and mark this instance as shut down + std::lock_guard lock(instance_mutex); + instance = nullptr; + hasBeenShutdown = true; + + } + + /*********************************************************************************************************************/ + + void ApplicationBase::setPVManager(boost::shared_ptr const &processVariableManager) { + _processVariableManager = processVariableManager; + } + + /*********************************************************************************************************************/ + + ApplicationBase& ApplicationBase::getInstance() { + if(instance == nullptr) { + throw std::runtime_error("No instance of ChimeraTK::ApplicationBase created, but ApplicationBase::getInstance() called."); + } + return *instance; + } + +} /* namespace ChimeraTK */ From faeeee02ade5fd04e78f8a5b8862b40b0bfec14e Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 11 Nov 2016 13:20:53 +0100 Subject: [PATCH 124/786] Change in comment only: clarified how the ApplicationBase::run() function must be implemented. --- include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h index dc8cfbe..cb5fd99 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h +++ b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h @@ -33,7 +33,10 @@ namespace ChimeraTK { * checks if Application::shutdown() was properly called and throws an exception otherwise. */ virtual ~ApplicationBase(); - /** Initialise and run the application. This function must be implemented by the application programmer. */ + /** Initialise and run the application. This function must be implemented by the application programmer. The + * application must be initialised, then a new thread must be started, in which the actual application will + * run. This function has to return after the application is initialised and the thread is created, so then + * control system adpater can continue its operation. */ virtual void run() = 0; /** This will remove the global pointer to the instance and allows creating another instance From 1d3455ed41e8139a671898da98c2d57c51ce02b5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 14 Nov 2016 10:36:19 +0100 Subject: [PATCH 125/786] add ApplicationBase::initialise() function definition, which defines the ProcessVariables (to be implemented by the application developer) --- .../ControlSystemAdapter/ApplicationBase.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h index cb5fd99..b6e2e9c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h +++ b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h @@ -33,10 +33,15 @@ namespace ChimeraTK { * checks if Application::shutdown() was properly called and throws an exception otherwise. */ virtual ~ApplicationBase(); - /** Initialise and run the application. This function must be implemented by the application programmer. The - * application must be initialised, then a new thread must be started, in which the actual application will - * run. This function has to return after the application is initialised and the thread is created, so then - * control system adpater can continue its operation. */ + /** Initialise the application. This function must be implemented by the application programmer. In this + * function, the application must register all process variables in the PV manager. */ + virtual void initialise() = 0; + + /** Run the application. This function must be implemented by the application programmer. It will be called + * after initialise() and after all process variables have been created and, if applicable, initialised with + * values e.g. from a config file. The application must start one or more threads inside this function, which + * will execute the actual application. The function must then return, so the control system adpater can + * continue its operation. */ virtual void run() = 0; /** This will remove the global pointer to the instance and allows creating another instance From 98e8092f17ba1fd86492b1b562f191bd02e219d9 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 16 Nov 2016 09:59:19 +0100 Subject: [PATCH 126/786] fixed still using the ProcessScalar.h include file --- .../ControlSystemAdapter/Testing/IndependentTestCore.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h index bdc8736..7b94d4b 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include From 49d70498d0c22b1ac9c234406ffd5d6f8c825279 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 22 Nov 2016 11:03:22 +0100 Subject: [PATCH 127/786] added some "chrome trim" comments to better structure the source code --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 4131d33..981f520 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -647,6 +647,10 @@ namespace ChimeraTK { }; +/*********************************************************************************************************************/ +/*** Non-member functions below this line ****************************************************************************/ +/*********************************************************************************************************************/ + /** * Creates a simple process array. A simple process array just works on its * own and does not implement a synchronization mechanism. Apart from this, @@ -775,6 +779,10 @@ namespace ChimeraTK { ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr()); +/*********************************************************************************************************************/ +/*** Implementations below this line *********************************************************************************/ +/*********************************************************************************************************************/ + template typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string & name, T initialValue) { @@ -782,6 +790,8 @@ namespace ChimeraTK { name, std::vector(size, initialValue)); } +/*********************************************************************************************************************/ + template typename ProcessArray::SharedPtr createSimpleProcessArray( const std::vector& initialValue, const std::string & name) { @@ -789,6 +799,8 @@ namespace ChimeraTK { name, initialValue); } +/*********************************************************************************************************************/ + template typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( @@ -809,6 +821,8 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr>(sender, receiver); } +/*********************************************************************************************************************/ + template typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( From 76402ef2724b66bf2d317ac2036299c933ce0f46 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 22 Nov 2016 11:10:50 +0100 Subject: [PATCH 128/786] converted tabs into spaces --- .../ControlSystemAdapter/ProcessArray.h | 18 +++++----- .../ProcessVariableListener.h | 34 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 981f520..4ca204a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -76,10 +76,10 @@ namespace ChimeraTK { ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue) : mtca4u::NDRegisterAccessor(name), - _instanceType(instanceType), _vectorSize(initialValue.size()), - _maySendDestructively(true), - _buffers(boost::make_shared >(1)), - _currentIndex(0), _lastSentIndex(0) { + _instanceType(instanceType), _vectorSize(initialValue.size()), + _maySendDestructively(true), + _buffers(boost::make_shared >(1)), + _currentIndex(0), _lastSentIndex(0) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != STAND_ALONE) { @@ -102,7 +102,7 @@ namespace ChimeraTK { ProcessArray(InstanceType instanceType, const std::string& name, const std::vector& initialValue, std::size_t numberOfBuffers, VersionNumberSource::SharedPtr versionNumberSource) : - mtca4u::NDRegisterAccessor(name), _instanceType(instanceType), _vectorSize( + mtca4u::NDRegisterAccessor(name), _instanceType(instanceType), _vectorSize( initialValue.size()), _maySendDestructively(false), _buffers( boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( boost::make_shared >( @@ -169,9 +169,9 @@ namespace ChimeraTK { VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener, ProcessArray::SharedPtr receiver) : - mtca4u::NDRegisterAccessor(receiver->getName()), - _instanceType(instanceType), _vectorSize(receiver->_vectorSize), - _maySendDestructively(maySendDestructively), _buffers( + mtca4u::NDRegisterAccessor(receiver->getName()), + _instanceType(instanceType), _vectorSize(receiver->_vectorSize), + _maySendDestructively(maySendDestructively), _buffers( receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( receiver), _timeStampSource(timeStampSource), _versionNumberSource( @@ -631,7 +631,7 @@ namespace ChimeraTK { throw std::runtime_error( "Assertion that empty-buffer queue has at least one element failed."); } - _fullBufferQueue->push(_currentIndex); + _fullBufferQueue->push(_currentIndex); } } if (shouldCopy) { diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h index 486d842..2658855 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h @@ -14,25 +14,25 @@ namespace ChimeraTK { class ProcessVariableListener { public: - /** - * Shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Processes the notification. This method is called every time an event - * occurs. The passed process variable is the one for which the event has - * been triggered. - */ - virtual void notify(boost::shared_ptr processVariable) =0; + /** + * Shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + + /** + * Processes the notification. This method is called every time an event + * occurs. The passed process variable is the one for which the event has + * been triggered. + */ + virtual void notify(boost::shared_ptr processVariable) =0; protected: - /** - * Destructor. The destructor is protected so that class implementing this - * interface cannot be destroyed through pointers to this interface. - */ - virtual ~ProcessVariableListener() { - } + /** + * Destructor. The destructor is protected so that class implementing this + * interface cannot be destroyed through pointers to this interface. + */ + virtual ~ProcessVariableListener() { + } }; From 1c75db4c7535f989c93678658635b67452c3dcc0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 22 Nov 2016 16:49:18 +0100 Subject: [PATCH 129/786] Added support for engineering units and variable descriptions. This is an interface change which is only partially compatible! --- .../ControlSystemAdapter/DevicePVManager.h | 27 ++--- .../ControlSystemAdapter/PVManager.h | 32 ++--- .../ControlSystemAdapter/ProcessArray.h | 110 ++++++++++-------- .../Testing/IndependentTestCore.h | 21 ++-- .../src/testDeviceSynchronizationUtility.cpp | 2 +- tests/src/testPVManager.cpp | 53 +++++---- tests/src/testProcessArray.cpp | 26 +++-- 7 files changed, 153 insertions(+), 118 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 75f4bec..00f9196 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -81,8 +81,8 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, const std::string& processVariableName, std::size_t size, - T initialValue = 0, bool maySendDestructively = false, - std::size_t numberOfBuffers = 2); + const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + T initialValue = 0, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** * Creates a new process array and registers it with the PV manager. @@ -111,9 +111,9 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, - const std::vector& initialValue, bool maySendDestructively = false, - std::size_t numberOfBuffers = 2); + const std::string& processVariableName, const std::vector& initialValue, + const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** * Returns a reference to a process array that has been created earlier @@ -224,16 +224,17 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, std::size_t size, T initialValue, + const std::string& processVariableName, std::size_t size, + const std::string& unit, const std::string& description, T initialValue, bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { case controlSystemToDevice: return _pvManager->createProcessArrayControlSystemToDevice( - processVariableName, std::vector(size, initialValue), + processVariableName, std::vector(size, initialValue), unit, description, maySendDestructively, numberOfBuffers).second; case deviceToControlSystem: return _pvManager->createProcessArrayDeviceToControlSystem( - processVariableName, std::vector(size, initialValue), + processVariableName, std::vector(size, initialValue), unit, description, maySendDestructively, numberOfBuffers).second; default: throw std::invalid_argument("invalid SynchronizationDirection"); @@ -243,17 +244,17 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, - const std::vector& initialValue, bool maySendDestructively, - std::size_t numberOfBuffers) { + const std::string& processVariableName, const std::vector& initialValue, + const std::string& unit, const std::string& description, + bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { case controlSystemToDevice: return _pvManager->createProcessArrayControlSystemToDevice( - processVariableName, initialValue, maySendDestructively, + processVariableName, initialValue, unit, description, maySendDestructively, numberOfBuffers).second; case deviceToControlSystem: return _pvManager->createProcessArrayDeviceToControlSystem( - processVariableName, initialValue, maySendDestructively, + processVariableName, initialValue, unit, description, maySendDestructively, numberOfBuffers).second; default: throw std::invalid_argument("invalid SynchronizationDirection"); diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 3ae065f..c023fa2 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -87,10 +87,10 @@ namespace ChimeraTK { */ template std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( - const std::string& processVariableName, - const std::vector& initialValue, bool maySendDestructively = false, - std::size_t numberOfBuffers = 2); + typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( + const std::string& processVariableName, const std::vector& initialValue, + const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** * Creates a new process array for transferring data from the control system @@ -120,10 +120,10 @@ namespace ChimeraTK { */ template std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( - const std::string& processVariableName, - const std::vector& initialValue, bool maySendDestructively = false, - std::size_t numberOfBuffers = 2); + typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( + const std::string& processVariableName, const std::vector& initialValue, + const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** * Returns a reference to a process array that has been created earlier @@ -431,9 +431,9 @@ namespace ChimeraTK { template std::pair::SharedPtr, - typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( - const std::string& processVariableName, - const std::vector& initialValue, bool maySendDestructively, + typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( + const std::string& processVariableName, const std::vector& initialValue, + const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) != _processVariables.end()) { @@ -450,7 +450,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> processVariables = - createSynchronizedProcessArray(initialValue, processVariableName, + createSynchronizedProcessArray(initialValue, processVariableName, unit, description, numberOfBuffers, maySendDestructively, timeStampSource, _versionNumberSource, sendNotificationListener); @@ -468,9 +468,9 @@ namespace ChimeraTK { template std::pair::SharedPtr, - typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( - const std::string& processVariableName, - const std::vector& initialValue, bool maySendDestructively, + typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( + const std::string& processVariableName, const std::vector& initialValue, + const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) != _processVariables.end()) { @@ -488,7 +488,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> processVariables = - createSynchronizedProcessArray(initialValue, processVariableName, + createSynchronizedProcessArray(initialValue, processVariableName, unit, description, numberOfBuffers, maySendDestructively, timeStampSource, _versionNumberSource, sendNotificationListener); diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 4ca204a..36eeb61 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -34,7 +34,7 @@ namespace ChimeraTK { * source will always stay at zero. */ template - class ProcessArray: public mtca4u::NDRegisterAccessor { + class ProcessArray : public mtca4u::NDRegisterAccessor { public: @@ -73,13 +73,16 @@ namespace ChimeraTK { * and receive operations are not supported. However, all other operations * can be used like on any process variable. */ - ProcessArray(InstanceType instanceType, const std::string& name, - const std::vector& initialValue) : - mtca4u::NDRegisterAccessor(name), - _instanceType(instanceType), _vectorSize(initialValue.size()), - _maySendDestructively(true), - _buffers(boost::make_shared >(1)), - _currentIndex(0), _lastSentIndex(0) { + ProcessArray(InstanceType instanceType, const std::string &name, const std::string &unit, + const std::string &description, const std::vector &initialValue) + : mtca4u::NDRegisterAccessor(name, unit, description), + _instanceType(instanceType), + _vectorSize(initialValue.size()), + _maySendDestructively(true), + _buffers(boost::make_shared >(1)), + _currentIndex(0), + _lastSentIndex(0) + { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != STAND_ALONE) { @@ -99,17 +102,20 @@ namespace ChimeraTK { * This constructor creates the buffers and queues that are needed for the * send/receive process and are shared with the sender. */ - ProcessArray(InstanceType instanceType, const std::string& name, - const std::vector& initialValue, std::size_t numberOfBuffers, - VersionNumberSource::SharedPtr versionNumberSource) : - mtca4u::NDRegisterAccessor(name), _instanceType(instanceType), _vectorSize( - initialValue.size()), _maySendDestructively(false), _buffers( - boost::make_shared >(numberOfBuffers + 2)), _fullBufferQueue( - boost::make_shared >( - numberOfBuffers)), _emptyBufferQueue( - boost::make_shared >( - numberOfBuffers)), _currentIndex(0), _lastSentIndex(0), _versionNumberSource( - versionNumberSource) { + ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, + const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, + VersionNumberSource::SharedPtr versionNumberSource) + : mtca4u::NDRegisterAccessor(name, unit, description), + _instanceType(instanceType), + _vectorSize(initialValue.size()), + _maySendDestructively(false), + _buffers(boost::make_shared>(numberOfBuffers + 2)), + _fullBufferQueue(boost::make_shared>(numberOfBuffers)), + _emptyBufferQueue(boost::make_shared>(numberOfBuffers)), + _currentIndex(0), + _lastSentIndex(0), + _versionNumberSource(versionNumberSource) + { // allocate and initialise buffer of the base class mtca4u::NDRegisterAccessor::buffer_2D.resize(1); mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; @@ -168,15 +174,21 @@ namespace ChimeraTK { TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener, - ProcessArray::SharedPtr receiver) : - mtca4u::NDRegisterAccessor(receiver->getName()), - _instanceType(instanceType), _vectorSize(receiver->_vectorSize), - _maySendDestructively(maySendDestructively), _buffers( - receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue( - receiver->_emptyBufferQueue), _currentIndex(1), _lastSentIndex(1), _receiver( - receiver), _timeStampSource(timeStampSource), _versionNumberSource( - versionNumberSource), _sendNotificationListener( - sendNotificationListener) { + ProcessArray::SharedPtr receiver) + : mtca4u::NDRegisterAccessor(receiver->getName(), receiver->getUnit(), receiver->getDescription()), + _instanceType(instanceType), + _vectorSize(receiver->_vectorSize), + _maySendDestructively(maySendDestructively), + _buffers(receiver->_buffers), + _fullBufferQueue(receiver->_fullBufferQueue), + _emptyBufferQueue(receiver->_emptyBufferQueue), + _currentIndex(1), + _lastSentIndex(1), + _receiver(receiver), + _timeStampSource(timeStampSource), + _versionNumberSource(versionNumberSource), + _sendNotificationListener(sendNotificationListener) + { // allocate and initialise buffer of the base class mtca4u::NDRegisterAccessor::buffer_2D.resize(1); mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; @@ -470,6 +482,7 @@ namespace ChimeraTK { virtual void replaceTransferElement(boost::shared_ptr){ // You can't replace anything here. Just do nothing. } + private: /** @@ -659,8 +672,8 @@ namespace ChimeraTK { * The specified initial value is used for all the elements of the array. */ template - typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, - const std::string & name = "", T initialValue = 0); + typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string &name = "", + const std::string &unit = "", const std::string &description = "", T initialValue = 0); /** * Creates a simple process array. A simple process array just works on its @@ -672,8 +685,8 @@ namespace ChimeraTK { * values provided by this vector. */ template - typename ProcessArray::SharedPtr createSimpleProcessArray( - const std::vector& initialValue, const std::string & name = ""); + typename ProcessArray::SharedPtr createSimpleProcessArray(const std::vector& initialValue, + const std::string & name = "", const std::string &unit = "", const std::string &description = ""); /** * Creates a synchronized process array. A synchronized process array works @@ -716,10 +729,10 @@ namespace ChimeraTK { * The specified initial value is used for all the elements of the array. */ template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const std::string & name = "", T initialValue = 0, - std::size_t numberOfBuffers = 2, bool maySendDestructively = false, + std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( + std::size_t size, const std::string & name = "", const std::string &unit = "", + const std::string &description = "", T initialValue = 0, std::size_t numberOfBuffers = 2, + bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr versionNumberSource = VersionNumberSource::SharedPtr(), @@ -769,10 +782,9 @@ namespace ChimeraTK { * values provided by this vector. */ template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const std::string & name = "", - std::size_t numberOfBuffers = 2, bool maySendDestructively = false, + std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( + const std::vector& initialValue, const std::string & name = "", const std::string &unit = "", + const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr versionNumberSource = VersionNumberSource::SharedPtr(), @@ -784,19 +796,19 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, - const std::string & name, T initialValue) { + typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string & name, + const std::string &unit, const std::string &description, T initialValue) { return boost::make_shared >(ProcessArray::STAND_ALONE, - name, std::vector(size, initialValue)); + name, unit, description, std::vector(size, initialValue)); } /*********************************************************************************************************************/ template typename ProcessArray::SharedPtr createSimpleProcessArray( - const std::vector& initialValue, const std::string & name) { + const std::vector& initialValue, const std::string & name, const std::string &unit, const std::string &description) { return boost::make_shared >(ProcessArray::STAND_ALONE, - name, initialValue); + name, unit, description, initialValue); } /*********************************************************************************************************************/ @@ -804,13 +816,13 @@ namespace ChimeraTK { template typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const std::string & name, T initialValue, + std::size_t size, const std::string & name, const std::string &unit, const std::string &description, T initialValue, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener) { typename boost::shared_ptr > receiver = boost::make_shared< - ProcessArray >(ProcessArray::RECEIVER, name, + ProcessArray >(ProcessArray::RECEIVER, name, unit, description, std::vector(size, initialValue), numberOfBuffers, versionNumberSource); typename ProcessArray::SharedPtr sender = boost::make_shared< @@ -826,13 +838,13 @@ namespace ChimeraTK { template typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const std::string & name, + const std::vector& initialValue, const std::string & name, const std::string &unit, const std::string &description, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener) { typename boost::shared_ptr > receiver = boost::make_shared< - ProcessArray >(ProcessArray::RECEIVER, name, initialValue, + ProcessArray >(ProcessArray::RECEIVER, name, unit, description, initialValue, numberOfBuffers, versionNumberSource); typename ProcessArray::SharedPtr sender = boost::make_shared< ProcessArray >(ProcessArray::SENDER, maySendDestructively, diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h index 7b94d4b..ca59afe 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h @@ -33,13 +33,20 @@ struct TypedPVHolder{ typename ChimeraTK::ProcessArray::SharedPtr dataTypeConstant; typename ChimeraTK::ProcessArray::SharedPtr constantArray; - TypedPVHolder(boost::shared_ptr const & processVariableManager, std::string typeNamePrefix): - toDeviceScalar( processVariableManager->createProcessArray(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_SCALAR",1) ), - fromDeviceScalar( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_SCALAR",1) ), - toDeviceArray( processVariableManager->createProcessArray(ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_ARRAY", 10) ), - fromDeviceArray( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_ARRAY", 10) ), - dataTypeConstant( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/DATA_TYPE_CONSTANT",1) ), - constantArray( processVariableManager->createProcessArray(ChimeraTK::deviceToControlSystem, typeNamePrefix + "/CONSTANT_ARRAY",10) ){ + TypedPVHolder(boost::shared_ptr const & processVariableManager, std::string typeNamePrefix) + : toDeviceScalar( processVariableManager->createProcessArray( + ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_SCALAR",1) ), + fromDeviceScalar( processVariableManager->createProcessArray( + ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_SCALAR",1) ), + toDeviceArray( processVariableManager->createProcessArray( + ChimeraTK::controlSystemToDevice, typeNamePrefix + "/TO_DEVICE_ARRAY", 10) ), + fromDeviceArray( processVariableManager->createProcessArray( + ChimeraTK::deviceToControlSystem, typeNamePrefix + "/FROM_DEVICE_ARRAY", 10) ), + dataTypeConstant( processVariableManager->createProcessArray( + ChimeraTK::deviceToControlSystem, typeNamePrefix + "/DATA_TYPE_CONSTANT",1) ), + constantArray( processVariableManager->createProcessArray( + ChimeraTK::deviceToControlSystem, typeNamePrefix + "/CONSTANT_ARRAY",10) ) + { if (std::numeric_limits::is_integer){ if (std::numeric_limits::is_signed){ // signed int diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index 2d9640d..00ff582 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -159,7 +159,7 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) boost::shared_ptr devManager = pvManagers.second; ProcessArray::SharedPtr devIntOut = - devManager->createProcessArray(controlSystemToDevice,"intOut",1); + devManager->createProcessArray(controlSystemToDevice, "intOut", 1); ProcessArray::SharedPtr csIntOut = csManager->getProcessArray("intOut"); DeviceSynchronizationUtility syncUtil(devManager); diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 36d7b11..f5c0904 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -51,22 +51,40 @@ template static void testCreateProcessVariables(const string& name, shared_ptr devManager, shared_ptr csManager) { + shared_ptr > createdPV = devManager->createProcessArray( - deviceToControlSystem, name + "In", 1); + deviceToControlSystem, name + "In", 1, "kindOfAUnit","any description"); BOOST_CHECK(createdPV->getName() == name + "In"); + BOOST_CHECK(createdPV->getUnit() == "kindOfAUnit"); + BOOST_CHECK(createdPV->getDescription() == "any description"); + shared_ptr > devPV = devManager->getProcessArray( name + "In"); BOOST_CHECK(devPV->getName() == name + "In"); + BOOST_CHECK(devPV->getUnit() == "kindOfAUnit"); + BOOST_CHECK(devPV->getDescription() == "any description"); + shared_ptr > csPV = csManager->getProcessArray( name + "In"); BOOST_CHECK(csPV->getName() == name + "In"); + BOOST_CHECK(csPV->getUnit() == "kindOfAUnit"); + BOOST_CHECK(csPV->getDescription() == "any description"); + createdPV = devManager->createProcessArray(controlSystemToDevice, - name + "Out", 1); + name + "Out", 1, "anotherUnit","something"); BOOST_CHECK(createdPV->getName() == name + "Out"); + BOOST_CHECK(createdPV->getUnit() == "anotherUnit"); + BOOST_CHECK(createdPV->getDescription() == "something"); + devPV = devManager->getProcessArray(name + "Out"); BOOST_CHECK(devPV->getName() == name + "Out"); + BOOST_CHECK(devPV->getUnit() == "anotherUnit"); + BOOST_CHECK(devPV->getDescription() == "something"); + csPV = csManager->getProcessArray(name + "Out"); BOOST_CHECK(csPV->getName() == name + "Out"); + BOOST_CHECK(csPV->getUnit() == "anotherUnit"); + BOOST_CHECK(csPV->getDescription() == "something"); BOOST_CHECK_THROW( devManager->createProcessArray( static_cast(-1),name + "ShouldFail", 1), @@ -76,6 +94,9 @@ static void testCreateProcessVariables(const string& name, shared_ptr > createdPA = devManager->createProcessArray( deviceToControlSystem, arrayName + "In", 5); BOOST_CHECK(createdPA->getName() == arrayName + "In"); + BOOST_CHECK(createdPA->getUnit() == "n./a."); + BOOST_CHECK(createdPA->getDescription() == ""); + BOOST_CHECK(createdPA->getName() == arrayName + "In"); shared_ptr > devPA = devManager->getProcessArray( arrayName + "In"); BOOST_CHECK(devPA->getName() == arrayName + "In"); @@ -159,8 +180,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr devManager = pvManagers.second; devManager->createProcessArray(deviceToControlSystem, "double", 1); - devManager->createProcessArray(controlSystemToDevice, "floatArray", - 10); + devManager->createProcessArray(controlSystemToDevice, "floatArray", 10); // We expect a bad_cast exception to be thrown because the specified // PV name points to a PV of a different type. BOOST_CHECK_THROW(devManager->getProcessArray("double"), @@ -255,8 +275,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) devManager->createProcessArray(controlSystemToDevice, "double", 1); devManager->createProcessArray(deviceToControlSystem, "int32", 1); - devManager->createProcessArray(deviceToControlSystem, "floatArray", - 10); + devManager->createProcessArray(deviceToControlSystem, "floatArray", 10); vector csProcessVariables( csManager->getAllProcessVariables()); @@ -309,12 +328,9 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) devManager->createProcessArray(deviceToControlSystem, "int32In", 1); devManager->createProcessArray(controlSystemToDevice, "int32Out", 1); - devManager->createProcessArray(deviceToControlSystem, "floatArrayIn", - 10); - devManager->createProcessArray(controlSystemToDevice, - "floatArrayOut", 10); - devManager->createProcessArray(controlSystemToDevice, - "stopDeviceThread", 1); + devManager->createProcessArray(deviceToControlSystem, "floatArrayIn", 10); + devManager->createProcessArray(controlSystemToDevice, "floatArrayOut", 10); + devManager->createProcessArray(controlSystemToDevice, "stopDeviceThread", 1); TestDeviceCallable callable; callable.pvManager = devManager; @@ -431,8 +447,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) devManager->createProcessArray(deviceToControlSystem, "int32In", 1); devManager->createProcessArray(deviceToControlSystem, "doubleIn", 1); - devManager->createProcessArray(controlSystemToDevice, - "stopDeviceThread", 1); + devManager->createProcessArray(controlSystemToDevice, "stopDeviceThread", 1); TestDeviceCallable2 callable; callable.pvManager = devManager; @@ -530,8 +545,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) devManager->createProcessArray(controlSystemToDevice, "int32Out", 1); devManager->createProcessArray(controlSystemToDevice, "doubleOut", 1); - devManager->createProcessArray(deviceToControlSystem, - "stopControlSystemThread", 1); + devManager->createProcessArray(deviceToControlSystem, "stopControlSystemThread", 1); TestDeviceCallable3 callable; callable.pvManager = devManager; @@ -637,8 +651,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) devManager->createProcessArray(deviceToControlSystem, "intA", 1); devManager->createProcessArray(deviceToControlSystem, "intB", 1); devManager->createProcessArray(deviceToControlSystem, "index0", 1); - devManager->createProcessArray(controlSystemToDevice, - "stopDeviceThread", 1); + devManager->createProcessArray(controlSystemToDevice, "stopDeviceThread", 1); TestDeviceCallable4 callable; callable.pvManager = devManager; @@ -707,9 +720,9 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr devManager = pvManagers.second; ProcessArray::SharedPtr intAdev = devManager->createProcessArray< - int32_t>(deviceToControlSystem, "intA",1); + int32_t>(deviceToControlSystem, "intA", 1); ProcessArray::SharedPtr intBdev = devManager->createProcessArray< - int32_t>(deviceToControlSystem, "intB",1); + int32_t>(deviceToControlSystem, "intB", 1, "kindOfAUnit", "any description"); ProcessArray::SharedPtr intAcs = csManager->getProcessArray< int32_t>("intA"); ProcessArray::SharedPtr intBcs = csManager->getProcessArray< diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 3f186d8..8783a27 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -72,8 +72,10 @@ namespace ChimeraTK { } BOOST_CHECK(simpleArray->get().size() == N_ELEMENTS); // Now we test the constructor with non-default parameters. - simpleArray = createSimpleProcessArray(N_ELEMENTS, "test", SOME_NUMBER); + simpleArray = createSimpleProcessArray(N_ELEMENTS, "test", "myUnit", "someDescription", SOME_NUMBER); BOOST_CHECK(simpleArray->getName() == "test"); + BOOST_CHECK(simpleArray->getUnit() == "myUnit"); + BOOST_CHECK(simpleArray->getDescription() == "someDescription"); for (typename std::vector::iterator i = simpleArray->get().begin(); i != simpleArray->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); @@ -87,7 +89,7 @@ namespace ChimeraTK { referenceVector.push_back(1); referenceVector.push_back(2); referenceVector.push_back(3); - simpleArray = createSimpleProcessArray(referenceVector, "test"); + simpleArray = createSimpleProcessArray(referenceVector, "test", "", ""); BOOST_CHECK(simpleArray->getName() == "test"); BOOST_CHECK(simpleArray->get().size() == 4); BOOST_CHECK( @@ -120,7 +122,7 @@ namespace ChimeraTK { BOOST_CHECK(sender->isWriteable()); BOOST_CHECK(receiver->isReadable()); BOOST_CHECK(!receiver->isWriteable()); - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "test", + senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "test", "", "", SOME_NUMBER, 5); sender = senderReceiver.first; receiver = senderReceiver.second; @@ -136,7 +138,7 @@ namespace ChimeraTK { BOOST_CHECK(*i == SOME_NUMBER); } BOOST_CHECK(receiver->get().size() == N_ELEMENTS); - senderReceiver = createSynchronizedProcessArray(referenceVector, "test", + senderReceiver = createSynchronizedProcessArray(referenceVector, "test", "", "", 5, false); sender = senderReceiver.first; receiver = senderReceiver.second; @@ -157,7 +159,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr array1 = createSimpleProcessArray( N_ELEMENTS); typename ProcessArray::SharedPtr array2 = createSimpleProcessArray( - N_ELEMENTS, "", SOME_NUMBER); + N_ELEMENTS, "", "", "", SOME_NUMBER); // Test the assignment of another process array. (*array1) = (*array2); for (typename std::vector::iterator i = array1->get().begin(); @@ -188,7 +190,7 @@ namespace ChimeraTK { template void ProcessArrayTest::testGet() { typename ProcessArray::SharedPtr simpleArray = - createSimpleProcessArray(N_ELEMENTS, "", SOME_NUMBER); + createSimpleProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); typename std::vector & v = simpleArray->get(); typename std::vector const & cv = simpleArray->get(); for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { @@ -225,7 +227,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr array1 = createSimpleProcessArray( N_ELEMENTS); typename ProcessArray::SharedPtr array2 = createSimpleProcessArray( - N_ELEMENTS, "", SOME_NUMBER); + N_ELEMENTS, "", "", "", SOME_NUMBER); // Test the assignment of another process array. array1->set(*array2); for (typename std::vector::iterator i = array1->get().begin(); @@ -284,7 +286,7 @@ namespace ChimeraTK { boost::make_shared()); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, false, + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr(), sendNotificationListener); typename ProcessArray::SharedPtr sender = senderReceiver.first; @@ -304,7 +306,7 @@ namespace ChimeraTK { boost::make_shared()); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, false, + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, timeStampSource); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; @@ -323,7 +325,7 @@ namespace ChimeraTK { void ProcessArrayTest::testSynchronization() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, true); + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; // If we send two values consecutively, they both should be received because @@ -380,7 +382,7 @@ namespace ChimeraTK { // Calling writeDestructively() on a sender that has not the corresponding // flag set should result in an exception. senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, false); + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false); sender = senderReceiver.first; receiver = senderReceiver.second; BOOST_CHECK_THROW(sender->writeDestructively(), std::runtime_error); @@ -392,7 +394,7 @@ namespace ChimeraTK { VersionNumberSource>(); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", 0, 2, true, + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true, TimeStampSource::SharedPtr(), versionNumberSource); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; From 15160ec4256ce6c2af92d3f2a78af3580307567c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 23 Nov 2016 17:17:02 +0100 Subject: [PATCH 130/786] Added class for persistent data storage. The class is not yet used anywhere and still needs some better exception handling. --- CMakeLists.txt | 10 +- .../PersistentDataStorage.h | 126 ++++++++++++ src/PersistentDataStorage.cc | 179 ++++++++++++++++++ tests/src/testPersistentDatatStorage.cc | 58 ++++++ 4 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h create mode 100644 src/PersistentDataStorage.cc create mode 100644 tests/src/testPersistentDatatStorage.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index e047cc4..9effbc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,10 +39,18 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) message("Boost unit_test_framework not found, disabling testing") ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) +# DeviceAccess is needed due to the common interface classes FIND_PACKAGE(mtca4u-deviceaccess 00.18 REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${mtca4u-deviceaccess_CXX_FLAGS}") include_directories(SYSTEM ${mtca4u-deviceaccess_INCLUDE_DIRS}) +# libXML++ is used for parsing/writing XML files +FIND_PACKAGE(PkgConfig REQUIRED) +set(LIBXML++_VERSION "libxml++-2.6") +PKG_CHECK_MODULES(LibXML++ REQUIRED ${LIBXML++_VERSION}) +PKG_CHECK_MODULES(glib REQUIRED glib-2.0) +include_directories(SYSTEM "${LibXML++_INCLUDEDIR}/${LIBXML++_VERSION}" ${glib_INCLUDE_DIRS} ${LibXML++_INCLUDE_DIRS}) + #Includes inside the library are local includes, so we give the full path to the include directory include_directories(${CMAKE_SOURCE_DIR}/include/ChimeraTK/ControlSystemAdapter) aux_source_directory(${CMAKE_SOURCE_DIR}/src library_sources) @@ -85,7 +93,7 @@ endif(TESTING_IS_ENABLED) add_library(${PROJECT_NAME} SHARED ${library_sources} ) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION} SOVERSION ${${PROJECT_NAME}_SOVERSION}) -target_link_libraries(${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} ${LibXML++_LIBRARIES} ${glib_LIBRARIES}) #change the install prefix to the source directory in case the user has not specified a destination #i. e. CMAKE_INSTALL_PREFIX is not set manually diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h new file mode 100644 index 0000000..895c216 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -0,0 +1,126 @@ +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PERSISTENT_DATA_STORAGE_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PERSISTENT_DATA_STORAGE_H + +#include +#include +#include +#include +#include +#include +#include + +namespace xmlpp { + class Element; +} + +namespace ChimeraTK { + + class ControlSystemPVManager; + + /** + * Persistent data storage for process variables. Note: This class can only be used in applications based on the + * class ApplicationBase. + */ + class PersistentDataStorage { + + public: + + /** Constructor: Open and parse the storage file. The instance of ApplicationBase must already exist before + * calling the constructor! */ + PersistentDataStorage(); + + /** Destructor: Store variables to the file. */ + ~PersistentDataStorage(); + + /** Register a variable to be stored to and retrieved from the data storage. The returned value is the ID which + * must be passed to the other functions. */ + template + size_t registerVariable(std::string const &name, size_t nElements); + + /** Retrieve the current value for the variable with the given ID */ + template + const std::vector& retrieveValue(size_t id) const; + + /** Notify the storage system about a new value of the variable with the given ID (as returned by + * registerVariable) */ + template + void updateValue(int id, std::vector const &value); + + protected: + + /** Write out the file containing the persistent data */ + void writeToFile(); + + /** Generate XML tags for the given value */ + template + void generateXmlValueTags(xmlpp::Element *parent, size_t id); + + /** Read value from XML tags */ + template + void readXmlValueTags(const xmlpp::Element *parent, size_t id); + + /** File name to store the data to */ + std::string _filename; + + /** Vector of variable names. The index is the ID of the variable. */ + std::vector _variableNames; + + /** Vector of data types. The index is the ID of the variable. */ + std::vector _variableTypes; + + /** Type definition for the map holding the values for one specific data type. */ + template + using DataMap = std::map >; + + /** boost::fusion::map of the data type to the DataMap holding the values for the type */ + mtca4u::TemplateUserTypeMap _dataMap; + + }; + + /*********************************************************************************************************************/ + + template + size_t PersistentDataStorage::registerVariable(std::string const &name, size_t nElements) { + // check if already existing + auto position = std::find(_variableNames.begin(), _variableNames.end(), name); + + // create new element + if(position == _variableNames.end()) { + + // store name and type + _variableNames.push_back(name); + _variableTypes.push_back(&typeid(DataType)); + + // create value vector + size_t id = _variableNames.size()-1; + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + value.resize(nElements); + + // return id + return id; + } + // return existing id + else { + return position - _variableNames.begin(); + } + } + + /*********************************************************************************************************************/ + + template + const std::vector& PersistentDataStorage::retrieveValue(size_t id) const { + return boost::fusion::at_key(_dataMap.table).at(id); + } + + /*********************************************************************************************************************/ + + template + void PersistentDataStorage::updateValue(int id, std::vector const &value) { + boost::fusion::at_key(_dataMap.table)[id] = value; + } + +} // namespace ChimeraTK + +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PERSISTENT_DATA_STORAGE_H + + diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc new file mode 100644 index 0000000..6456c34 --- /dev/null +++ b/src/PersistentDataStorage.cc @@ -0,0 +1,179 @@ +/* + * PersistentDataStorage.cc + * + * Created on: Nov 23, 2016 + * Author: Martin Hierholzer + */ + +#include + +#include "PersistentDataStorage.h" +#include "ApplicationBase.h" + +namespace ChimeraTK { + + /*********************************************************************************************************************/ + + PersistentDataStorage::PersistentDataStorage() { + + _filename = ApplicationBase::getInstance().getName() + ".persist"; + + try { + xmlpp::DomParser parser; + //parser.set_validate(); + parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically. + parser.parse_file(_filename); + if(parser) { + // obtain root node + const xmlpp::Node *rootElement = parser.get_document()->get_root_node(); // object will be deleted by DomParser + + // iterate through variables + for(auto &elem : rootElement->get_children()) { + const xmlpp::Element *child = dynamic_cast(elem); + if(!child) continue; // comment or white spaces... + std::string name = child->get_attribute("name")->get_value(); + std::string type = child->get_attribute("type")->get_value(); + if(type == "int8") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "uint8") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "int16") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "uint16") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "int32") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "uint32") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "float") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "double") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "string") { readXmlValueTags(child, registerVariable(name, 0)); } + else { /* @todo ??? */ } + } + } + else { + std::cout << "No parser... " << std::endl; // @todo TODO proper exception handling + } + } + catch(const std::exception& ex) { // @todo TODO proper exception handling + std::cout << "Exception caught: " << ex.what() << std::endl; + } + + } + + /*********************************************************************************************************************/ + + template + void PersistentDataStorage::generateXmlValueTags(xmlpp::Element *parent, size_t id) { + + // obtain the data vector from the map + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + + // add one child element per element of the value + for(size_t idx=0; idxadd_child("val"); + valueElement->set_attribute("i", boost::lexical_cast(idx)); + valueElement->set_attribute("v", boost::lexical_cast(value[idx])); + } + } + + /*********************************************************************************************************************/ + + template + void PersistentDataStorage::readXmlValueTags(const xmlpp::Element *parent, size_t id) { + + // obtain the data vector from the map + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + + // collect values + for(auto &valElems : parent->get_children()) { + const xmlpp::Element *valChild = dynamic_cast(valElems); + if(!valChild) continue; // comment or white spaces... + + // obtain index and value as string + std::string s_idx = valChild->get_attribute("i")->get_value(); + std::string s_val = valChild->get_attribute("v")->get_value(); + + // convert to data type + size_t idx = boost::lexical_cast(s_idx); + DataType val = boost::lexical_cast(s_val); + + // resize vector if needed + if(value.size() <= idx) value.resize(idx+1); + + // store value + value[idx] = val; + } + } + + /*********************************************************************************************************************/ + + PersistentDataStorage::~PersistentDataStorage() { + writeToFile(); + } + + /*********************************************************************************************************************/ + + void PersistentDataStorage::writeToFile() { + + // create XML document with root node and a flat list of variables below this root + xmlpp::Document doc; + xmlpp::Element *rootElement = doc.create_root_node("PersistentData", "https://github.com/ChimeraTK/ControlSystemAdapter"); + rootElement->set_attribute("application", ApplicationBase::getInstance().getName()); + + for(size_t i=0; i<_variableNames.size(); ++i) { + + // create XML element for the variable and set name attribute + xmlpp::Element *variable = rootElement->add_child("variable"); + variable->set_attribute("name",_variableNames[i]); + + // generate value XML tags and set type name as a string + std::string dataTypeName{"unknown"}; + if(*_variableTypes[i] == typeid(int8_t)) { + generateXmlValueTags(variable, i); + dataTypeName = "int8"; + } + else if(*_variableTypes[i] == typeid(uint8_t)) { + generateXmlValueTags(variable, i); + dataTypeName = "uint8"; + } + else if(*_variableTypes[i] == typeid(int16_t)) { + generateXmlValueTags(variable, i); + dataTypeName = "int16"; + } + else if(*_variableTypes[i] == typeid(uint16_t)) { + generateXmlValueTags(variable, i); + dataTypeName = "uint16"; + } + else if(*_variableTypes[i] == typeid(int32_t)) { + generateXmlValueTags(variable, i); + dataTypeName = "int32"; + } + else if(*_variableTypes[i] == typeid(uint32_t)) { + generateXmlValueTags(variable, i); + dataTypeName = "uint32"; + } + else if(*_variableTypes[i] == typeid(float)) { + generateXmlValueTags(variable, i); + dataTypeName = "float"; + } + else if(*_variableTypes[i] == typeid(double)) { + generateXmlValueTags(variable, i); + dataTypeName = "double"; + } + else if(*_variableTypes[i] == typeid(std::string)) { + generateXmlValueTags(variable, i); + dataTypeName = "string"; + } + else { + /// @tod TODO what todo here? + } + + // set type attribute + variable->set_attribute("type",dataTypeName); + + } + + // write out to file + doc.write_to_file_formatted(_filename); + + } + + + /*********************************************************************************************************************/ + +} /* namespace ChimeraTK */ diff --git a/tests/src/testPersistentDatatStorage.cc b/tests/src/testPersistentDatatStorage.cc new file mode 100644 index 0000000..2c9dbf2 --- /dev/null +++ b/tests/src/testPersistentDatatStorage.cc @@ -0,0 +1,58 @@ +// Define a name for the test module. +#define BOOST_TEST_MODULE PersistentDataStorageTest + +// Only after defining the name include the unit test header. +#include +using namespace boost::unit_test_framework; + +#include + +#include +#include + +using namespace ChimeraTK; + +// a simple test application, just to fullfill the requirement of its existence +class TestApplication : public ApplicationBase { + public: + using ApplicationBase::ApplicationBase; + ~TestApplication() { shutdown(); } + void initialise() {}; + void run() {}; +}; + +// Create a test suite which holds all your tests. +BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) + +// Define one test after another. Each one needs a unique name. + BOOST_AUTO_TEST_CASE( testStoreAndRetrieve ) { + + TestApplication app{"myTestApplication"}; + + { + PersistentDataStorage storage; + + std::vector myVar1(10); + int id1 = storage.registerVariable("MyVar1", 10); + for(int i=0; i<10; ++i) myVar1[i] = 3*i; + storage.updateValue(id1, myVar1); + + auto myVar1stored = storage.retrieveValue(id1); + for(int i=0; i<10; ++i) BOOST_CHECK( myVar1stored[i] == 3*i ); + } + + { + PersistentDataStorage storage; + + std::vector myVar1(10); + int id1 = storage.registerVariable("MyVar1", 10); + myVar1 = storage.retrieveValue(id1); + for(int i=0; i<10; ++i) BOOST_CHECK( myVar1[i] == 3*i ); + } + + + + } + +// After you finished all test you have to end the test suite. +BOOST_AUTO_TEST_SUITE_END() From 28236e6256428439852ae4ebcf64fd80839bc3ac Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 09:50:07 +0100 Subject: [PATCH 131/786] extended the tests of the PersistentDataStorage a bit --- CMakeLists.txt | 5 +- src/PersistentDataStorage.cc | 2 +- tests/src/testPersistentDatatStorage.cc | 68 ++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9effbc6..364654d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,9 @@ FIND_PACKAGE(Boost REQUIRED COMPONENTS system) #boost thread is needed for synchronization and multi-threading FIND_PACKAGE(Boost REQUIRED COMPONENTS thread) +#boost system is needed by some tests +FIND_PACKAGE(Boost REQUIRED COMPONENTS filesystem) + #the unit test component is optional FIND_PACKAGE(Boost COMPONENTS unit_test_framework) IF(Boost_UNIT_TEST_FRAMEWORK_FOUND) @@ -74,7 +77,7 @@ if(TESTING_IS_ENABLED) # Linking the boost libraries and using their included counterparts at the # same time causes problems. #target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_LIBRARIES}) - target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) + target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) add_test(${executableName} ${executableName}) endforeach( testSourceFile ) diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index 6456c34..ea98243 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -42,7 +42,7 @@ namespace ChimeraTK { else if(type == "float") { readXmlValueTags(child, registerVariable(name, 0)); } else if(type == "double") { readXmlValueTags(child, registerVariable(name, 0)); } else if(type == "string") { readXmlValueTags(child, registerVariable(name, 0)); } - else { /* @todo ??? */ } + else { /* @todo TODO ??? */ } } } else { diff --git a/tests/src/testPersistentDatatStorage.cc b/tests/src/testPersistentDatatStorage.cc index 2c9dbf2..21417b0 100644 --- a/tests/src/testPersistentDatatStorage.cc +++ b/tests/src/testPersistentDatatStorage.cc @@ -6,6 +6,7 @@ using namespace boost::unit_test_framework; #include +#include #include #include @@ -24,33 +25,96 @@ class TestApplication : public ApplicationBase { // Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) +/** + * @todo FIXME No exception tests are yet done. They should be implemented asap! + */ + + // Define one test after another. Each one needs a unique name. BOOST_AUTO_TEST_CASE( testStoreAndRetrieve ) { TestApplication app{"myTestApplication"}; + // remove persistency file from previous test run + boost::filesystem::remove("myTestApplication.persist"); + + // create PersistentDataStorage for the first time and fill it with two variables { PersistentDataStorage storage; - + + // register integer variable MyVar1 with 10 array elements and fill it std::vector myVar1(10); int id1 = storage.registerVariable("MyVar1", 10); for(int i=0; i<10; ++i) myVar1[i] = 3*i; storage.updateValue(id1, myVar1); + // register floating-point variable /some/path.with.dots/to/MyVar2 with 100 array elements and fill it + std::vector myVar2(100); + int id2 = storage.registerVariable("/some/path.with.dots/to/MyVar2", 100); + for(int i=0; i<100; ++i) myVar2[i] = -120 + 7*i; + storage.updateValue(id2, myVar2); + + // check if both variables are properly stored auto myVar1stored = storage.retrieveValue(id1); for(int i=0; i<10; ++i) BOOST_CHECK( myVar1stored[i] == 3*i ); + auto myVar2stored = storage.retrieveValue(id2); + for(int i=0; i<100; ++i) BOOST_CHECK_CLOSE( myVar2stored[i], -120 + 7*i, 0.0001 ); } + // the first PersistentDataStorage is destroyed at this point, in its destructor the file is written + // create another PersistentDataStorage and read the previously stored variables from it { PersistentDataStorage storage; + // check MyVar1 std::vector myVar1(10); int id1 = storage.registerVariable("MyVar1", 10); myVar1 = storage.retrieveValue(id1); for(int i=0; i<10; ++i) BOOST_CHECK( myVar1[i] == 3*i ); + + // check /some/path.with.dots/to/MyVar2 + std::vector myVar2(100); + int id2 = storage.registerVariable("/some/path.with.dots/to/MyVar2", 100); + myVar2 = storage.retrieveValue(id2); + for(int i=0; i<100; ++i) BOOST_CHECK_CLOSE( myVar2[i], -120 + 7*i, 0.0001 ); + + // modify some elements of myVar1 + myVar1[7] = 42; + myVar1[3] = 120; + storage.updateValue(id1, myVar1); } - + // create another PersistentDataStorage and read the previously stored variables from it + { + PersistentDataStorage storage; + + // check MyVar1 + std::vector myVar1(10); + int id1 = storage.registerVariable("MyVar1", 10); + myVar1 = storage.retrieveValue(id1); + for(int i=0; i<10; ++i) { + if(i == 3) { + BOOST_CHECK( myVar1[i] == 120 ); + } + else if(i == 7) { + BOOST_CHECK( myVar1[i] == 42 ); + } + else { + BOOST_CHECK( myVar1[i] == 3*i ); + } + } + + // check /some/path.with.dots/to/MyVar2 + std::vector myVar2(100); + int id2 = storage.registerVariable("/some/path.with.dots/to/MyVar2", 100); + myVar2 = storage.retrieveValue(id2); + for(int i=0; i<100; ++i) BOOST_CHECK_CLOSE( myVar2[i], -120 + 7*i, 0.0001 ); + + // modify some elements of myVar1 + myVar1[7] = 42; + myVar1[3] = 120; + storage.updateValue(id1, myVar1); + } } From 3b0f947b8434d5949bba15afc464317d19d6b589 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 13:39:21 +0100 Subject: [PATCH 132/786] Use the PersistentDataStorage from the ControlSystemPVManager. This required some additional changes, since the instance of the PersistentDataStorage should be created by the ApplicationBase. --- .../ControlSystemAdapter/ApplicationBase.h | 9 + .../ControlSystemPVManager.h | 18 +- .../PersistentDataStorage.h | 11 +- .../ControlSystemAdapter/ProcessArray.h | 25 +++ src/PersistentDataStorage.cc | 176 +++++++++--------- tests/src/testPersistentDatatStorage.cc | 17 +- 6 files changed, 153 insertions(+), 103 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h index b6e2e9c..4074c7c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h +++ b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h @@ -67,6 +67,12 @@ namespace ChimeraTK { return applicationName; } + /** Obtain the PersistentDataStorage object */ + boost::shared_ptr getPersistentDataStorage() { + if(!_persistentDataStorage) _persistentDataStorage.reset(new PersistentDataStorage(applicationName)); + return _persistentDataStorage; + } + protected: /** The name of the application */ @@ -75,6 +81,9 @@ namespace ChimeraTK { /** Pointer to the process variable manager used to create variables exported to the control system */ boost::shared_ptr _processVariableManager; + /** Pointer to persistent data storage object, if used */ + boost::shared_ptr _persistentDataStorage; + /** Flag if shutdown() has been called. */ bool hasBeenShutdown{false}; diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 524a1a0..24d2847 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -8,6 +8,7 @@ #include #include "PVManager.h" +#include "ApplicationBase.h" namespace ChimeraTK { @@ -111,6 +112,14 @@ namespace ChimeraTK { * notification. */ ProcessVariable::SharedPtr nextNotification(); + + /** + * Enable the persistent data storage system provided by the ControlSystemAdapter. This function requires an + * existing instance of an ApplicationBase class. + */ + void enablePersistentDataStorage() { + _persistentDataStorage = ApplicationBase::getInstance().getPersistentDataStorage(); + } private: /** @@ -119,12 +128,19 @@ namespace ChimeraTK { */ boost::shared_ptr _pvManager; + /** + * All process variables should be registered in this persistent data storage + */ + boost::shared_ptr _persistentDataStorage; + }; template typename ProcessArray::SharedPtr ControlSystemPVManager::getProcessArray( const std::string& processVariableName) const { - return _pvManager->getProcessArray(processVariableName).first; + auto pv = _pvManager->getProcessArray(processVariableName).first; + if(_persistentDataStorage) pv->setPersistentDataStorage(_persistentDataStorage); + return pv; } } diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index 895c216..a5bc2b9 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -25,9 +25,8 @@ namespace ChimeraTK { public: - /** Constructor: Open and parse the storage file. The instance of ApplicationBase must already exist before - * calling the constructor! */ - PersistentDataStorage(); + /** Constructor: Open and parse the storage file. */ + PersistentDataStorage(std::string const &applicationName); /** Destructor: Store variables to the file. */ ~PersistentDataStorage(); @@ -51,6 +50,9 @@ namespace ChimeraTK { /** Write out the file containing the persistent data */ void writeToFile(); + /** Read the file containing the persistent data */ + void readFromFile(); + /** Generate XML tags for the given value */ template void generateXmlValueTags(xmlpp::Element *parent, size_t id); @@ -58,6 +60,9 @@ namespace ChimeraTK { /** Read value from XML tags */ template void readXmlValueTags(const xmlpp::Element *parent, size_t id); + + /** Application name */ + std::string _applicationName; /** File name to store the data to */ std::string _filename; diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 36eeb61..3d739df 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -16,9 +16,11 @@ #include #include + #include "ProcessVariableListener.h" #include "TimeStampSource.h" #include "VersionNumberSource.h" +#include "PersistentDataStorage.h" namespace ChimeraTK { /** @@ -482,6 +484,13 @@ namespace ChimeraTK { virtual void replaceTransferElement(boost::shared_ptr){ // You can't replace anything here. Just do nothing. } + + /** Associate a persistent data storage object to be updated on each write operation of this ProcessArray */ + void setPersistentDataStorage(boost::shared_ptr storage) { + _persistentDataStorage = storage; + _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), + mtca4u::NDRegisterAccessor::getNumberOfSamples()); + } private: @@ -593,6 +602,16 @@ namespace ChimeraTK { * Listener that is notified when the process variable is sent. */ boost::shared_ptr _sendNotificationListener; + + /** + * Persistent data storage which needs to be informed when the process variable is sent. + */ + boost::shared_ptr _persistentDataStorage; + + /** + * Variable ID for the persistent data storage + */ + size_t _persistentDataStorageID{0}; /** * Internal implementation of the various {@code send} methods. All these @@ -612,6 +631,12 @@ namespace ChimeraTK { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); } + // First update the persistent data storage, if any was associated. This cannot be done after sending, since the + // value might no longer be available within this instance. + if (_persistentDataStorage) { + _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); + } + // Before sending the value, we have to update the associated // time-stamp. TimeStamp newTimeStamp = diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index ea98243..0aaa090 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -14,90 +14,10 @@ namespace ChimeraTK { /*********************************************************************************************************************/ - PersistentDataStorage::PersistentDataStorage() { - - _filename = ApplicationBase::getInstance().getName() + ".persist"; - - try { - xmlpp::DomParser parser; - //parser.set_validate(); - parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically. - parser.parse_file(_filename); - if(parser) { - // obtain root node - const xmlpp::Node *rootElement = parser.get_document()->get_root_node(); // object will be deleted by DomParser - - // iterate through variables - for(auto &elem : rootElement->get_children()) { - const xmlpp::Element *child = dynamic_cast(elem); - if(!child) continue; // comment or white spaces... - std::string name = child->get_attribute("name")->get_value(); - std::string type = child->get_attribute("type")->get_value(); - if(type == "int8") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "uint8") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "int16") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "uint16") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "int32") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "uint32") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "float") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "double") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "string") { readXmlValueTags(child, registerVariable(name, 0)); } - else { /* @todo TODO ??? */ } - } - } - else { - std::cout << "No parser... " << std::endl; // @todo TODO proper exception handling - } - } - catch(const std::exception& ex) { // @todo TODO proper exception handling - std::cout << "Exception caught: " << ex.what() << std::endl; - } - - } - - /*********************************************************************************************************************/ - - template - void PersistentDataStorage::generateXmlValueTags(xmlpp::Element *parent, size_t id) { - - // obtain the data vector from the map - std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; - - // add one child element per element of the value - for(size_t idx=0; idxadd_child("val"); - valueElement->set_attribute("i", boost::lexical_cast(idx)); - valueElement->set_attribute("v", boost::lexical_cast(value[idx])); - } - } - - /*********************************************************************************************************************/ - - template - void PersistentDataStorage::readXmlValueTags(const xmlpp::Element *parent, size_t id) { - - // obtain the data vector from the map - std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; - - // collect values - for(auto &valElems : parent->get_children()) { - const xmlpp::Element *valChild = dynamic_cast(valElems); - if(!valChild) continue; // comment or white spaces... - - // obtain index and value as string - std::string s_idx = valChild->get_attribute("i")->get_value(); - std::string s_val = valChild->get_attribute("v")->get_value(); - - // convert to data type - size_t idx = boost::lexical_cast(s_idx); - DataType val = boost::lexical_cast(s_val); - - // resize vector if needed - if(value.size() <= idx) value.resize(idx+1); - - // store value - value[idx] = val; - } + PersistentDataStorage::PersistentDataStorage(std::string const &applicationName) { + _filename = applicationName+".persist"; + _applicationName = applicationName; + readFromFile(); } /*********************************************************************************************************************/ @@ -113,7 +33,7 @@ namespace ChimeraTK { // create XML document with root node and a flat list of variables below this root xmlpp::Document doc; xmlpp::Element *rootElement = doc.create_root_node("PersistentData", "https://github.com/ChimeraTK/ControlSystemAdapter"); - rootElement->set_attribute("application", ApplicationBase::getInstance().getName()); + rootElement->set_attribute("application", _applicationName); for(size_t i=0; i<_variableNames.size(); ++i) { @@ -173,6 +93,92 @@ namespace ChimeraTK { } + /*********************************************************************************************************************/ + + template + void PersistentDataStorage::generateXmlValueTags(xmlpp::Element *parent, size_t id) { + + // obtain the data vector from the map + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + + // add one child element per element of the value + for(size_t idx=0; idxadd_child("val"); + valueElement->set_attribute("i", boost::lexical_cast(idx)); + valueElement->set_attribute("v", boost::lexical_cast(value[idx])); + } + } + + /*********************************************************************************************************************/ + + void PersistentDataStorage::readFromFile() { + + try { + xmlpp::DomParser parser; + //parser.set_validate(); + parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically. + parser.parse_file(_filename); + if(parser) { + // obtain root node + const xmlpp::Node *rootElement = parser.get_document()->get_root_node(); // object will be deleted by DomParser + /// @todo TODO check if the application name is correct? + + // iterate through variables + for(auto &elem : rootElement->get_children()) { + const xmlpp::Element *child = dynamic_cast(elem); + if(!child) continue; // comment or white spaces... + std::string name = child->get_attribute("name")->get_value(); + std::string type = child->get_attribute("type")->get_value(); + if(type == "int8") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "uint8") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "int16") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "uint16") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "int32") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "uint32") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "float") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "double") { readXmlValueTags(child, registerVariable(name, 0)); } + else if(type == "string") { readXmlValueTags(child, registerVariable(name, 0)); } + else { /* @todo TODO ??? */ } + } + } + else { + std::cout << "No parser... " << std::endl; // @todo TODO proper exception handling + } + } + catch(const std::exception& ex) { // @todo TODO proper exception handling + std::cout << "Exception caught: " << ex.what() << std::endl; + } + + } + + /*********************************************************************************************************************/ + + template + void PersistentDataStorage::readXmlValueTags(const xmlpp::Element *parent, size_t id) { + + // obtain the data vector from the map + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + + // collect values + for(auto &valElems : parent->get_children()) { + const xmlpp::Element *valChild = dynamic_cast(valElems); + if(!valChild) continue; // comment or white spaces... + + // obtain index and value as string + std::string s_idx = valChild->get_attribute("i")->get_value(); + std::string s_val = valChild->get_attribute("v")->get_value(); + + // convert to data type + size_t idx = boost::lexical_cast(s_idx); + DataType val = boost::lexical_cast(s_val); + + // resize vector if needed + if(value.size() <= idx) value.resize(idx+1); + + // store value + value[idx] = val; + } + } /*********************************************************************************************************************/ diff --git a/tests/src/testPersistentDatatStorage.cc b/tests/src/testPersistentDatatStorage.cc index 21417b0..cf8d23c 100644 --- a/tests/src/testPersistentDatatStorage.cc +++ b/tests/src/testPersistentDatatStorage.cc @@ -13,15 +13,6 @@ using namespace boost::unit_test_framework; using namespace ChimeraTK; -// a simple test application, just to fullfill the requirement of its existence -class TestApplication : public ApplicationBase { - public: - using ApplicationBase::ApplicationBase; - ~TestApplication() { shutdown(); } - void initialise() {}; - void run() {}; -}; - // Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) @@ -32,15 +23,13 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) // Define one test after another. Each one needs a unique name. BOOST_AUTO_TEST_CASE( testStoreAndRetrieve ) { - - TestApplication app{"myTestApplication"}; // remove persistency file from previous test run boost::filesystem::remove("myTestApplication.persist"); // create PersistentDataStorage for the first time and fill it with two variables { - PersistentDataStorage storage; + PersistentDataStorage storage{"myTestApplication"}; // register integer variable MyVar1 with 10 array elements and fill it std::vector myVar1(10); @@ -64,7 +53,7 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) // create another PersistentDataStorage and read the previously stored variables from it { - PersistentDataStorage storage; + PersistentDataStorage storage{"myTestApplication"}; // check MyVar1 std::vector myVar1(10); @@ -86,7 +75,7 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) // create another PersistentDataStorage and read the previously stored variables from it { - PersistentDataStorage storage; + PersistentDataStorage storage{"myTestApplication"}; // check MyVar1 std::vector myVar1(10); From 2346b3fb236dda4c3decb9e97434c3eed4b20816 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 14:29:11 +0100 Subject: [PATCH 133/786] only add persistent data storage for control-system-to-device variables --- include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 24d2847..0d031a6 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -139,7 +139,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr ControlSystemPVManager::getProcessArray( const std::string& processVariableName) const { auto pv = _pvManager->getProcessArray(processVariableName).first; - if(_persistentDataStorage) pv->setPersistentDataStorage(_persistentDataStorage); + if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); return pv; } From c96402b952ef5f7ae92730bcfae0bc613ad451bc Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 14:29:45 +0100 Subject: [PATCH 134/786] send initial value when the variable is connected to the persistent data storage for the first time --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 3d739df..1b9ebb0 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -485,11 +485,24 @@ namespace ChimeraTK { // You can't replace anything here. Just do nothing. } - /** Associate a persistent data storage object to be updated on each write operation of this ProcessArray */ + /** + * Associate a persistent data storage object to be updated on each write operation of this ProcessArray. If no + * persistent data storage as associated previously, the value from the persistent storage is read and send to + * the receiver. + * + * Note: This function should only be used for sender-type variables! + */ void setPersistentDataStorage(boost::shared_ptr storage) { + assert(isWriteable()); + bool sendInitialValue = false; + if(!_persistentDataStorage) sendInitialValue = true; _persistentDataStorage = storage; _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), mtca4u::NDRegisterAccessor::getNumberOfSamples()); + if(sendInitialValue) { + mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); + write(); + } } private: From 3337b15667129bb8c8ff2f3d5633457bbca936ab Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 15:10:05 +0100 Subject: [PATCH 135/786] set the persistent data store to process variables in all functions which allow returning a process variable --- .../ControlSystemAdapter/ControlSystemPVManager.h | 7 +++++-- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 9 +-------- src/ControlSystemPVManager.cc | 8 ++++++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 0d031a6..47d6675 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -129,9 +129,12 @@ namespace ChimeraTK { boost::shared_ptr _pvManager; /** - * All process variables should be registered in this persistent data storage + * All process variables should be registered in this persistent data storage. + * This member is mutable, since it is required in a non-const form inside const functions like getProcessArray(). + * The change of the persistent data storage object does not represent a state change which is visible to the + * outside, thus this is a legit use of the mutable qualifier. */ - boost::shared_ptr _persistentDataStorage; + mutable boost::shared_ptr _persistentDataStorage; }; diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 1b9ebb0..079219b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -485,15 +485,8 @@ namespace ChimeraTK { // You can't replace anything here. Just do nothing. } - /** - * Associate a persistent data storage object to be updated on each write operation of this ProcessArray. If no - * persistent data storage as associated previously, the value from the persistent storage is read and send to - * the receiver. - * - * Note: This function should only be used for sender-type variables! - */ void setPersistentDataStorage(boost::shared_ptr storage) { - assert(isWriteable()); + if(!isWriteable()) return; bool sendInitialValue = false; if(!_persistentDataStorage) sendInitialValue = true; _persistentDataStorage = storage; diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index 2e60569..44ee194 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -9,7 +9,9 @@ namespace ChimeraTK { ProcessVariable::SharedPtr ControlSystemPVManager::getProcessVariable( const std::string & processVariableName) const { - return _pvManager->getProcessVariable(processVariableName).first; + auto pv = _pvManager->getProcessVariable(processVariableName).first; + if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); + return pv; } std::vector ControlSystemPVManager::getAllProcessVariables() const { @@ -21,7 +23,9 @@ namespace ChimeraTK { csProcessVariables.reserve(processVariables.size()); for (PVManager::ProcessVariableMap::const_iterator i = processVariables.begin(); i != processVariables.end(); i++) { - csProcessVariables.push_back(i->second.first); + auto pv = i->second.first; + if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); + csProcessVariables.push_back(pv); } return csProcessVariables; } From 8eb78bc46ee1167fc4f245e22ada3a2880cf65ea Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 15:29:08 +0100 Subject: [PATCH 136/786] added test for the persistent data storage when used inside the PVManager --- tests/src/testPersistentDatatStorage.cc | 91 +++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tests/src/testPersistentDatatStorage.cc b/tests/src/testPersistentDatatStorage.cc index cf8d23c..efe90c6 100644 --- a/tests/src/testPersistentDatatStorage.cc +++ b/tests/src/testPersistentDatatStorage.cc @@ -8,11 +8,23 @@ using namespace boost::unit_test_framework; #include #include +#include +#include #include #include using namespace ChimeraTK; +// define empty test application to fullfill the requirement of having an instance +class MyTestApplication : public ApplicationBase { + public: + using ApplicationBase::ApplicationBase; + ~MyTestApplication() { shutdown(); } + void initialise() {}; + void run() {}; +}; + + // Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) @@ -107,5 +119,84 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) } + BOOST_AUTO_TEST_CASE( testUsageInPVManager ) { + + // remove persistency file from previous test run + boost::filesystem::remove("myTestApplication.persist"); + + // first application instance: initialise the variables with some values and store them in the persistency file + { + + // create instance of test application + MyTestApplication myTestApplication{"myTestApplication"}; + + // create PV managers + auto pvManagers = createPVManager(); + auto csManager = pvManagers.first; + auto devManager = pvManagers.second; + + // create some variables + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); + devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); + + // enable persist data storage + csManager->enablePersistentDataStorage(); + + // obtain the process variables, send some values to the variables + auto v1 = csManager->getProcessArray("SomeCsToDevVar"); + for(int i=0; i<7; ++i) v1->get()[i] = i*17; + v1->write(); + + auto v2 = csManager->getProcessArray("AnotherCsToDevVar"); + for(int i=0; i<42; ++i) v2->get()[i] = i*3.1415 * 1e12; + v2->write(); + + auto v3 = devManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored + for(int i=0; i<7; ++i) v3->get()[i] = 9*i + 666; + v3->write(); + + } + + // second application instance: check if stored values are properly retrieved + { + + // create instance of test application + MyTestApplication myTestApplication{"myTestApplication"}; + + // create PV managers + auto pvManagers = createPVManager(); + auto csManager = pvManagers.first; + auto devManager = pvManagers.second; + + // create some variables + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); + devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); + + // enable persist data storage + csManager->enablePersistentDataStorage(); + + // obtain all variables from the manager to initialise them with the persistent data storage + csManager->getAllProcessVariables(); + + // obtain the process variables, send some values to the variables + auto v1 = devManager->getProcessArray("SomeCsToDevVar"); + v1->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK( v1->get()[i] == i*17 ); + + auto v2 = devManager->getProcessArray("AnotherCsToDevVar"); + v2->readNonBlocking(); + for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->get()[i], i*3.1415 * 1e30, 2e23); + + auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored + v3->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK( v3->get()[i] == 0 ); + + } + + + } + // After you finished all test you have to end the test suite. BOOST_AUTO_TEST_SUITE_END() From c8e2f8d89324cdbe17c1363025efe571cfe0c2db Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 15:29:59 +0100 Subject: [PATCH 137/786] renamed file due to a typo in its name --- ...testPersistentDatatStorage.cc => testPersistentDataStorage.cc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/src/{testPersistentDatatStorage.cc => testPersistentDataStorage.cc} (100%) diff --git a/tests/src/testPersistentDatatStorage.cc b/tests/src/testPersistentDataStorage.cc similarity index 100% rename from tests/src/testPersistentDatatStorage.cc rename to tests/src/testPersistentDataStorage.cc From 4a969f33a052c3dc581b081636205e2d24740815 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 16:08:41 +0100 Subject: [PATCH 138/786] updated documentation of PersistentDataStorage --- .../ControlSystemAdapter/PersistentDataStorage.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index a5bc2b9..975be0f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -18,8 +18,19 @@ namespace ChimeraTK { class ControlSystemPVManager; /** - * Persistent data storage for process variables. Note: This class can only be used in applications based on the - * class ApplicationBase. + * Persistent data storage for process variables. + * + * The PersistentDataStorage will create a file in the current working directory based on the provided application + * name (".persist"). This file will be an XML file containing all values of the registered + * variables. The file is written when the PersistentDataStorage is destroyed and read when it is constructed. + * After reading the file, all variables will be updated with the current values taken from the file. This will be + * seen as a value received by the application just like any other update. + * + * @todo TODO list: + * * thread safety (only an issue when having multiple ControlSystemPVManagers) + * * automatic periodic commits + * * manual commits triggered by the application (with support for other implementations, e.g. if the persistency + * is already provided by the control system middleware) */ class PersistentDataStorage { From 96fcab1f77c3c95d06779ebbe319f681f253f657 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 17:04:16 +0100 Subject: [PATCH 139/786] added TINE_EXPORT C macro/constant to the compiler flags, which seens to be required for the latest DOOCS version --- cmake/Modules/FindDOOCS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 79bdfd0..579d679 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -64,7 +64,7 @@ set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSapi nsl dl pthread m rt ldap) set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) -set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG") +set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG -DTINE_EXPORT") set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") # extract DOOCS version from librar so symlink. Note: This is platform dependent and only works From c4f979b821ef91d81b74f709c44b781436cbc757 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 24 Nov 2016 17:10:55 +0100 Subject: [PATCH 140/786] Just defining TINE_EXPORT was not enough, so we set it to a space instead --- cmake/Modules/FindDOOCS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 579d679..ee08754 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -64,7 +64,7 @@ set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSapi nsl dl pthread m rt ldap) set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) -set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG -DTINE_EXPORT") +set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG -DTINE_EXPORT=\" \"") set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") # extract DOOCS version from librar so symlink. Note: This is platform dependent and only works From 75f8d78da02c24872f7f697419b0441ac0c93dd9 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 6 Dec 2016 13:12:35 +0100 Subject: [PATCH 141/786] removed doxygen warnings introduced by inferface changes --- .../ControlSystemAdapter/ControlSystemPVManager.h | 8 ++++---- include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h | 8 ++++---- src/PersistentDataStorage.cc | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 47d6675..4d6cee5 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -56,9 +56,9 @@ namespace ChimeraTK { /** * Returns a reference to a process array that has been created earlier * using the - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type @@ -71,9 +71,9 @@ namespace ChimeraTK { /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)}, + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. */ diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 00f9196..ac90d68 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -118,9 +118,9 @@ namespace ChimeraTK { /** * Returns a reference to a process array that has been created earlier * using the - * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} * or - * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type @@ -132,9 +132,9 @@ namespace ChimeraTK { /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, T, bool, std::size_t)}, + * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, * or - * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. */ diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index 0aaa090..9c62a6e 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -80,7 +80,7 @@ namespace ChimeraTK { dataTypeName = "string"; } else { - /// @tod TODO what todo here? + /// @todo TODO what todo here? } // set type attribute From bd232f3da6339d4046e309bb070e306f9dee001b Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 6 Dec 2016 17:23:37 +0100 Subject: [PATCH 142/786] check if persistency file exists before reading --- src/PersistentDataStorage.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index 0aaa090..4788b06 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -5,6 +5,8 @@ * Author: Martin Hierholzer */ +#include + #include #include "PersistentDataStorage.h" @@ -112,7 +114,16 @@ namespace ChimeraTK { /*********************************************************************************************************************/ void PersistentDataStorage::readFromFile() { - + + // check if file exists + struct stat buffer; + if(stat(_filename.c_str(), &buffer) != 0) { + // file does not exist: print message and do nothing + std::cerr << "ChimeraTK::PersistentDataStorage: Persistency file '" << _filename << "' does not exist. " + "It will be created when exiting the application." << std::endl; + return; + } + try { xmlpp::DomParser parser; //parser.set_validate(); From 33c8621856100d0dec0fcb7483f8ac425c7b2fd4 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 9 Dec 2016 10:44:10 +0100 Subject: [PATCH 143/786] increased minor version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 364654d..6cf2d64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,12 @@ cmake_minimum_required(VERSION 2.8.0) project(ChimeraTK-ControlSystemAdapter) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 02) +set(${PROJECT_NAME}_MINOR_VERSION 03) set(${PROJECT_NAME}_PATCH_VERSION 00) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) set(${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}) - + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow #-Weffc++ -ansi -pedantic -Wuninitialized -std=c++11") #skip -Weffc++ for the moment. It gives too many false positives From 2304b199b1d0687febb172ff739ed1f4b9b8a779 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 9 Dec 2016 11:22:57 +0100 Subject: [PATCH 144/786] fixed build environment: DeviceAccess library path was neither set when building the tests nor pushed to the downstream projects --- CMakeLists.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cf2d64..b33abfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 2.8.0) project(ChimeraTK-ControlSystemAdapter) +include(cmake/add_dependency.cmake) + set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 03) set(${PROJECT_NAME}_PATCH_VERSION 00) @@ -43,9 +45,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) # DeviceAccess is needed due to the common interface classes -FIND_PACKAGE(mtca4u-deviceaccess 00.18 REQUIRED) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${mtca4u-deviceaccess_CXX_FLAGS}") -include_directories(SYSTEM ${mtca4u-deviceaccess_INCLUDE_DIRS}) +add_dependency(mtca4u-deviceaccess 00.18 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) @@ -73,10 +73,6 @@ if(TESTING_IS_ENABLED) #The IndependentTestCore is written to work with the control system adapter as installed, so #we add that path as system just for the tests. target_include_directories(${executableName} SYSTEM BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/include) - #no target library yet - # Linking the boost libraries and using their included counterparts at the - # same time causes problems. - #target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_LIBRARIES}) target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) add_test(${executableName} ${executableName}) endforeach( testSourceFile ) @@ -128,7 +124,7 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include #but there are none. set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIRS}") set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}") -set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS}") +set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS} ${mtca4u-deviceaccess_LIBRARY_DIRS}") set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11 ${mtca4u-deviceaccess_CXX_FLAGS}") set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags") include(${CMAKE_SOURCE_DIR}/cmake/create_cmake_config_files.cmake) From 554cffe3c657383a47614397b5b704cc30ce444b Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 9 Dec 2016 11:23:54 +0100 Subject: [PATCH 145/786] increased patch level --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b33abfe..716feaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 03) -set(${PROJECT_NAME}_PATCH_VERSION 00) +set(${PROJECT_NAME}_PATCH_VERSION 01) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) set(${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}) From 2491ea28eeb6d12dc2685a5127f02edb6bd18eb5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 9 Dec 2016 13:21:58 +0100 Subject: [PATCH 146/786] Increased the sleep times in a test to avoid spurious test failures. This is still an ugly work-around, since the tests should not be based on real sleeps. --- tests/src/testPVManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index f5c0904..00e21af 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -372,7 +372,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) // Send the values, wait a moment for the other thread to send the updates // and then receive the new values. sendAll(outboundProcessVariables); - boost::this_thread::sleep_for(boost::chrono::milliseconds(15)); + boost::this_thread::sleep_for(boost::chrono::milliseconds(150)); receiveAll(inboundProcessVariables); BOOST_CHECK(int32In->accessData(0) == 55); @@ -388,7 +388,7 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) // Send the values, wait a moment for the other thread to send the updates // and then receive the new values. sendAll(outboundProcessVariables); - boost::this_thread::sleep_for(boost::chrono::milliseconds(15)); + boost::this_thread::sleep_for(boost::chrono::milliseconds(150)); receiveAll(inboundProcessVariables); BOOST_CHECK(int32In->accessData(0) == -300); From aff8c03136a5bfa0943b63b5279763fad13eee8a Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 9 Dec 2016 13:27:29 +0100 Subject: [PATCH 147/786] also set CXX_FLAGS from the dependency --- cmake/add_dependency.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index e292318..3cabac9 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -39,6 +39,7 @@ FUNCTION(add_dependency dependency_project_name required_version) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_LIBRARIES ${${dependency_project_name}_LIBRARIES} PARENT_SCOPE) SET(${dependency_project_name}_LINK_FLAGS ${${dependency_project_name}_LINK_FLAGS} PARENT_SCOPE) + SET(${dependency_project_name}_CXX_FLAGS ${${dependency_project_name}_CXX_FLAGS} PARENT_SCOPE) ENDFUNCTION(add_dependency) From fae30af7304d5c74a42dee798fa9c49aec843171 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 09:10:21 +0100 Subject: [PATCH 148/786] export FOUND flag to the calling CMakeLists.txt --- cmake/add_dependency.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index 3cabac9..5c3b4e6 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -40,6 +40,7 @@ FUNCTION(add_dependency dependency_project_name required_version) SET(${dependency_project_name}_LIBRARIES ${${dependency_project_name}_LIBRARIES} PARENT_SCOPE) SET(${dependency_project_name}_LINK_FLAGS ${${dependency_project_name}_LINK_FLAGS} PARENT_SCOPE) SET(${dependency_project_name}_CXX_FLAGS ${${dependency_project_name}_CXX_FLAGS} PARENT_SCOPE) + SET(${dependency_project_name}_FOUND ${${dependency_project_name}_FOUND} PARENT_SCOPE) ENDFUNCTION(add_dependency) From 31e13ec946e62db8fab80d911be0ad1728a16adb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 09:40:58 +0100 Subject: [PATCH 149/786] Fixed the confusing and inconsistently used names *_LINK_FLAGS and *_LINKER_FLAGS by always setting and getting both variables --- cmake/FindPROJECT_NAME.cmake.in.in | 3 ++- cmake/Modules/FindDOOCS.cmake | 3 ++- cmake/add_dependency.cmake | 3 ++- cmake/create_cmake_config_files.cmake | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmake/FindPROJECT_NAME.cmake.in.in b/cmake/FindPROJECT_NAME.cmake.in.in index 2190bb2..2102790 100644 --- a/cmake/FindPROJECT_NAME.cmake.in.in +++ b/cmake/FindPROJECT_NAME.cmake.in.in @@ -41,7 +41,8 @@ set(@PROJECT_NAME@_INCLUDE_DIRS @@PROJECT_NAME@_INCLUDE_DIRS@) set(@PROJECT_NAME@_LIBRARY_DIRS @@PROJECT_NAME@_LIBRARY_DIRS@) set(@PROJECT_NAME@_LIBRARIES ${@PROJECT_NAME@_LIBRARY} @@PROJECT_NAME@_LIBRARIES@) set(@PROJECT_NAME@_CXX_FLAGS "@@PROJECT_NAME@_CXX_FLAGS@") -set(@PROJECT_NAME@_LINKER_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@") +set(@PROJECT_NAME@_LINKER_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@ @@PROJECT_NAME@_LINK_FLAGS@") +set(@PROJECT_NAME@_LINK_FLAGS "@@PROJECT_NAME@_LINKER_FLAGS@ @@PROJECT_NAME@_LINK_FLAGS@") set(@PROJECT_NAME@_PREFIX "@CMAKE_INSTALL_PREFIX@") # Use a macro provided by CMake to check if all the listed arguments are valid and set @PROJECT_NAME@_FOUND accordingly. diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index ee08754..50510f6 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -65,7 +65,8 @@ set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG -DTINE_EXPORT=\" \"") -set(DOOCS_LINK_FLAGS "-Wl,--no-as-needed") +set(DOOCS_LINKER_FLAGS "-Wl,--no-as-needed") +set(DOOCS_LINK_FLAGS "${DOOCS_LINKER_FLAGS}") # extract DOOCS version from librar so symlink. Note: This is platform dependent and only works # if DOOCS was installed from the Debian pagackes. Find a better version detection scheme! diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index 5c3b4e6..fcbecdf 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -38,7 +38,8 @@ FUNCTION(add_dependency dependency_project_name required_version) link_directories(${${dependency_project_name}_LIBRARY_DIR}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_LIBRARIES ${${dependency_project_name}_LIBRARIES} PARENT_SCOPE) - SET(${dependency_project_name}_LINK_FLAGS ${${dependency_project_name}_LINK_FLAGS} PARENT_SCOPE) + SET(${dependency_project_name}_LINKER_FLAGS "${${dependency_project_name}_LINKER_FLAGS} ${${dependency_project_name}_LINK_FLAGS}" PARENT_SCOPE) + SET(${dependency_project_name}_LINK_FLAGS "${${dependency_project_name}_LINKER_FLAGS} ${${dependency_project_name}_LINK_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_CXX_FLAGS ${${dependency_project_name}_CXX_FLAGS} PARENT_SCOPE) SET(${dependency_project_name}_FOUND ${${dependency_project_name}_FOUND} PARENT_SCOPE) diff --git a/cmake/create_cmake_config_files.cmake b/cmake/create_cmake_config_files.cmake index 86b6377..deda9ac 100644 --- a/cmake/create_cmake_config_files.cmake +++ b/cmake/create_cmake_config_files.cmake @@ -35,7 +35,7 @@ foreach(INCLUDE_DIR ${LIST}) set(${PROJECT_NAME}_CXX_FLAGS_MAKEFILE "${${PROJECT_NAME}_CXX_FLAGS_MAKEFILE} -I${INCLUDE_DIR}") endforeach() -set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS}") +set(${PROJECT_NAME}_LINKER_FLAGS_MAKEFILE "${${PROJECT_NAME}_LINKER_FLAGS} ${${PROJECT_NAME}_LINK_FLAGS}") string(REPLACE " " ";" LIST "${${PROJECT_NAME}_LIBRARY_DIRS}") foreach(LIBRARY_DIR ${LIST}) From 18a3203475edd2719562cea8ead4c29fda9da330 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 09:44:50 +0100 Subject: [PATCH 150/786] added missing DeviceAccess linker flags --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 716feaf..1568e94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,11 +126,9 @@ set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUD set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}") set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS} ${mtca4u-deviceaccess_LIBRARY_DIRS}") set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11 ${mtca4u-deviceaccess_CXX_FLAGS}") -set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags") +set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags ${mtca4u-deviceaccess_LINKER_FLAGS}") include(${CMAKE_SOURCE_DIR}/cmake/create_cmake_config_files.cmake) ## The instructions to create a debian package are loaded from a separate file #include(${CMAKE_SOURCE_DIR}/cmake/prepare_debian_package.cmake) - - From 92aaf568d5db5756e24fa6ac311b36e2d40eb655 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 09:52:10 +0100 Subject: [PATCH 151/786] use readelf instead of readlink to detect the DOOCS version (imported change from doocsllrfwrapper) --- cmake/Modules/FindDOOCS.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 50510f6..02eb704 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -70,7 +70,8 @@ set(DOOCS_LINK_FLAGS "${DOOCS_LINKER_FLAGS}") # extract DOOCS version from librar so symlink. Note: This is platform dependent and only works # if DOOCS was installed from the Debian pagackes. Find a better version detection scheme! -execute_process(COMMAND bash -c "readlink ${DOOCS_DIR}/libDOOCSapi.so | sed -e 's/^.*libDOOCSapi.so.//' -e 's/-.*$//'" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) +execute_process(COMMAND bash -c "readelf -d ${DOOCS_DIR}/libDOOCSapi.so | grep SONAME | sed -e 's/^.*Library soname: \\[libDOOCSapi\\.so\\.//' -e 's/\\]$//'" + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE DOOCS_VERSION) # use a macro provided by CMake to check if all the listed arguments are valid and set DOOCS_FOUND accordingly include(FindPackageHandleStandardArgs) From 873d1a80b4e1c547536f4dc2f8efedd32075722c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 10:34:00 +0100 Subject: [PATCH 152/786] fixed wrong quotes when setting the TINE_EXPORT C preprocessor symbol to a space --- cmake/Modules/FindDOOCS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 02eb704..4e0e527 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -64,7 +64,7 @@ set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSapi nsl dl pthread m rt ldap) set(DOOCS_INCLUDE_DIRS ${DOOCS_DIR}/include) set(DOOCS_LIBRARY_DIRS ${DOOCS_DIR}/) -set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG -DTINE_EXPORT=\" \"") +set(DOOCS_CXX_FLAGS "-Wall -fPIC -D_REENTRANT -DLINUX -D__LINUX__ -DDMSG -DTINE_EXPORT=' '") set(DOOCS_LINKER_FLAGS "-Wl,--no-as-needed") set(DOOCS_LINK_FLAGS "${DOOCS_LINKER_FLAGS}") From 67de5b8f93a55248848e97b8d452919f7086e972 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 10:46:27 +0100 Subject: [PATCH 153/786] added ignore pattern for KDevelop 4 project files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a997f81..84f373c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ ###################### build .project +.kdev4 +*.kdev4 #files ending with ~ are emacs backups *~ #files ending with .swp might show up when using vi From 5122ae36258697b41a708e22b24525c1c492d71c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 10:54:56 +0100 Subject: [PATCH 154/786] also export the LIBRARY_DIRS variable to the calling CMakeLists.txt (needed to publish it to further downstream dependencies) --- cmake/add_dependency.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index fcbecdf..96f116e 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -38,9 +38,10 @@ FUNCTION(add_dependency dependency_project_name required_version) link_directories(${${dependency_project_name}_LIBRARY_DIR}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_LIBRARIES ${${dependency_project_name}_LIBRARIES} PARENT_SCOPE) + SET(${dependency_project_name}_LIBRARY_DIRS "${${dependency_project_name}_LIBRARY_DIRS} ${${dependency_project_name}_LIBRARY_DIR}" PARENT_SCOPE) SET(${dependency_project_name}_LINKER_FLAGS "${${dependency_project_name}_LINKER_FLAGS} ${${dependency_project_name}_LINK_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_LINK_FLAGS "${${dependency_project_name}_LINKER_FLAGS} ${${dependency_project_name}_LINK_FLAGS}" PARENT_SCOPE) - SET(${dependency_project_name}_CXX_FLAGS ${${dependency_project_name}_CXX_FLAGS} PARENT_SCOPE) + SET(${dependency_project_name}_CXX_FLAGS "${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_FOUND ${${dependency_project_name}_FOUND} PARENT_SCOPE) ENDFUNCTION(add_dependency) From c560d49c5824541b68f96b746e30a85eeab08018 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Dec 2016 16:22:33 +0100 Subject: [PATCH 155/786] increased patch level --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1568e94..cf97b36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 03) -set(${PROJECT_NAME}_PATCH_VERSION 01) +set(${PROJECT_NAME}_PATCH_VERSION 02) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) set(${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}) From e2995488a33978846cb77df321bc30f7ee6b53b2 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 13 Dec 2016 11:39:13 +0100 Subject: [PATCH 156/786] chaned internal variable name because some compiler were complaining that is shadows a class-wide function --- .../ControlSystemAdapter/VersionNumberSource.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h index 3dda4a7..1b9fd91 100644 --- a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h +++ b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h @@ -37,13 +37,15 @@ namespace ChimeraTK { inline VersionNumber nextVersionNumber() { VersionNumber currentVersionNumber = _lastReturnedVersionNumber.load( boost::memory_order_acquire); - VersionNumber nextVersionNumber = currentVersionNumber + 1; + // Don't call this variable nextVersionNumber (but nextVersionNr) because + // compilers might complain that it shadows the function name + VersionNumber nextVersionNr = currentVersionNumber + 1; while (!_lastReturnedVersionNumber.compare_exchange_weak( - currentVersionNumber, nextVersionNumber, boost::memory_order_acq_rel, + currentVersionNumber, nextVersionNr, boost::memory_order_acq_rel, boost::memory_order_acquire)) { - nextVersionNumber = currentVersionNumber + 1; + nextVersionNr = currentVersionNumber + 1; } - return nextVersionNumber; + return nextVersionNr; } private: From 4c816250c8e055837df89cb08ca24eb31f86e611 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 22 Dec 2016 16:18:23 +0100 Subject: [PATCH 157/786] removed the _lastSentIndex private member, since it is no longer used --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 079219b..2a5b3ec 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -83,7 +83,6 @@ namespace ChimeraTK { _maySendDestructively(true), _buffers(boost::make_shared >(1)), _currentIndex(0), - _lastSentIndex(0) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. @@ -115,7 +114,6 @@ namespace ChimeraTK { _fullBufferQueue(boost::make_shared>(numberOfBuffers)), _emptyBufferQueue(boost::make_shared>(numberOfBuffers)), _currentIndex(0), - _lastSentIndex(0), _versionNumberSource(versionNumberSource) { // allocate and initialise buffer of the base class @@ -185,7 +183,6 @@ namespace ChimeraTK { _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue(receiver->_emptyBufferQueue), _currentIndex(1), - _lastSentIndex(1), _receiver(receiver), _timeStampSource(timeStampSource), _versionNumberSource(versionNumberSource), @@ -580,13 +577,6 @@ namespace ChimeraTK { */ std::size_t _currentIndex; - /** - * Index of the buffer that has been sent last. This index is only - * meaningful if this is a sender and swapping is not allowed on the - * receiver. - */ - std::size_t _lastSentIndex; - /** * Pointer to the receiver associated with this sender. This field is only * used if this process variable represents a sender. @@ -682,7 +672,6 @@ namespace ChimeraTK { (*_buffers)[nextIndex].timeStamp = newTimeStamp; (*_buffers)[nextIndex].versionNumber = newVersionNumber; } - _lastSentIndex = _currentIndex; _currentIndex = nextIndex; if (_sendNotificationListener) { _sendNotificationListener->notify(_receiver); From 8b20e364503121d83eb55d725df0b0434d11cccf Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 22 Dec 2016 16:29:27 +0100 Subject: [PATCH 158/786] removed extra comma --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 2a5b3ec..7a0d600 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -82,7 +82,7 @@ namespace ChimeraTK { _vectorSize(initialValue.size()), _maySendDestructively(true), _buffers(boost::make_shared >(1)), - _currentIndex(0), + _currentIndex(0) { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. From 15047ba910271c967b8ae994005215d0a2b42340 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 23 Dec 2016 10:06:57 +0100 Subject: [PATCH 159/786] some code cleaning --- .../ControlSystemAdapter/ProcessArray.h | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 7a0d600..a588f87 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -4,12 +4,12 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -74,6 +74,8 @@ namespace ChimeraTK { * instance is not synchronized with any other instance and thus the send * and receive operations are not supported. However, all other operations * can be used like on any process variable. + * + * TODO @todo Is this really a wanted feature? I see no benefit in it... */ ProcessArray(InstanceType instanceType, const std::string &name, const std::string &unit, const std::string &description, const std::vector &initialValue) @@ -122,14 +124,12 @@ namespace ChimeraTK { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != RECEIVER) { - throw std::invalid_argument( - "This constructor may only be used for a receiver process scalar."); + throw std::invalid_argument("This constructor may only be used for a receiver process scalar."); } // We need at least two buffers for the queue (so four buffers in total) // in order to guarantee that we never have to block. if (numberOfBuffers < 2) { - throw std::invalid_argument( - "The number of buffers must be at least two."); + throw std::invalid_argument("The number of buffers must be at least two."); } // We have to limit the number of buffers because we cannot allocate // more buffers than can be represented by size_t and the total number @@ -142,9 +142,8 @@ namespace ChimeraTK { throw std::invalid_argument("The number of buffers is too large."); } // We have to initialize the buffers by copying in the initial vectors. - for (typename std::vector::iterator i = _buffers->begin(); - i != _buffers->end(); ++i) { - i->value=initialValue; + for (auto &i : *_buffers) { + i.value = initialValue; } // The buffer with the index 0 is assigned to the receiver and the // buffer with the index 1 is assigned to the sender. All buffers have @@ -194,12 +193,10 @@ namespace ChimeraTK { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != SENDER) { - throw std::invalid_argument( - "This constructor may only be used for a sender process scalar."); + throw std::invalid_argument("This constructor may only be used for a sender process scalar."); } if (!receiver) { - throw std::invalid_argument( - "The pointer to the receiver must not be null."); + throw std::invalid_argument("The pointer to the receiver must not be null."); } if (receiver->_instanceType != RECEIVER) { throw std::invalid_argument( @@ -337,23 +334,21 @@ namespace ChimeraTK { bool readNonBlocking() { if (_instanceType != RECEIVER) { - throw std::logic_error( - "Receive operation is only allowed for a receiver process variable."); + throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + throw std::runtime_error("Cannot run receive operation because the size of the vector belonging to the current" + " buffer has been modified."); } std::size_t nextIndex; if (_fullBufferQueue->pop(nextIndex)) { // We only use the incoming update if it has a higher version number // than the current value. This check is disabled when there is no // version number source. - if (!_versionNumberSource - || ((*_buffers)[nextIndex]).versionNumber > getVersionNumber()) { + if (!_versionNumberSource || ((*_buffers)[nextIndex]).versionNumber > getVersionNumber()) { _emptyBufferQueue->push(_currentIndex); _currentIndex = nextIndex; mtca4u::NDRegisterAccessor::buffer_2D[0].swap( ((*_buffers)[_currentIndex]).value ); From fa0a803c27beeb3c335b890b922049492d8afcfb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 23 Dec 2016 13:58:35 +0100 Subject: [PATCH 160/786] implement blocking read and add test for it --- CMakeLists.txt | 2 +- .../ControlSystemAdapter/ProcessArray.h | 54 ++++++++++++++++--- tests/src/testProcessArray.cpp | 41 ++++++++++++++ 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf97b36..cd6b1bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ if(TESTING_IS_ENABLED) #The IndependentTestCore is written to work with the control system adapter as installed, so #we add that path as system just for the tests. target_include_directories(${executableName} SYSTEM BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/include) - target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}) + target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} pthread) add_test(${executableName} ${executableName}) endforeach( testSourceFile ) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index a588f87..69e28bc 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -115,6 +116,8 @@ namespace ChimeraTK { _buffers(boost::make_shared>(numberOfBuffers + 2)), _fullBufferQueue(boost::make_shared>(numberOfBuffers)), _emptyBufferQueue(boost::make_shared>(numberOfBuffers)), + _notificationQueue(boost::make_shared, + boost::lockfree::capacity<2>>>()), _currentIndex(0), _versionNumberSource(versionNumberSource) { @@ -181,15 +184,13 @@ namespace ChimeraTK { _buffers(receiver->_buffers), _fullBufferQueue(receiver->_fullBufferQueue), _emptyBufferQueue(receiver->_emptyBufferQueue), + _notificationQueue(receiver->_notificationQueue), _currentIndex(1), _receiver(receiver), _timeStampSource(timeStampSource), _versionNumberSource(versionNumberSource), _sendNotificationListener(sendNotificationListener) { - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != SENDER) { @@ -202,6 +203,11 @@ namespace ChimeraTK { throw std::invalid_argument( "The pointer to the receiver must point to an instance that is actually a receiver."); } + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; + // put future into the notification queue + _notificationQueue->push(_notificationPromise.get_future()); } /** @@ -306,7 +312,15 @@ namespace ChimeraTK { } virtual void read(){ - throw std::logic_error("Blocking read is not supported by process array."); + // Obtain futures from the notification queue and wait on them until we receive data. We start with checking + // for data before obtaining a future from the notification queue, since this is faster and the notification + // queue is shorter than the data queue. + while(!readNonBlocking()) { + boost::shared_future future; + bool atLeastOneFuturePresent = _notificationQueue->pop(future); + assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm + future.wait(); + } } /** @@ -549,7 +563,7 @@ namespace ChimeraTK { /** * Buffers that hold the actual values. */ - boost::shared_ptr > _buffers; + boost::shared_ptr> _buffers; /** * Queue holding the indices of the full buffers. Those are the buffers @@ -558,13 +572,31 @@ namespace ChimeraTK { * thread, so there are two threads which might consume elements and thus * an spsc_queue is not safe. */ - boost::shared_ptr > _fullBufferQueue; + boost::shared_ptr> _fullBufferQueue; /** * Queue holding the empty buffers. Those are the buffers that have been * returned to the sender by the receiver. */ - boost::shared_ptr > _emptyBufferQueue; + boost::shared_ptr> _emptyBufferQueue; + + /** + * Queue holding futures for notification. The receiver can obtain a future from this queue when the + * _fullBufferQueue is empty and wait on it until new data has been sent. + * + * It is sufficient to have a fixed queue length of 2, since it is only important to guarantee the presence of + * at least one unfulfilled future in the queue. + * + * We are using a shared future, since the spsc_queue expects a copy-constructable data type. Since the data + * type void is not particularly expensive to copy, this should have no performance impact. + */ + boost::shared_ptr, + boost::lockfree::capacity<2>>> _notificationQueue; + + /** + * The promise corresponding to the unfulfilled future in the _notificationQueue + */ + boost::promise _notificationPromise; /** * Index into the _buffers array that is currently owned by this instance @@ -671,6 +703,14 @@ namespace ChimeraTK { if (_sendNotificationListener) { _sendNotificationListener->notify(_receiver); } + // Notify the receiver through the notification queue. It must be guaranteed that there is at least one + // unfulfilled future in the notification queue at all times, so we try pushing a new one first. If this + // failes, there is already one unfulfilled and one fulfilled future in the queue, so we are done. + boost::promise nextPromise; + if(_notificationQueue->push(nextPromise.get_future())) { + _notificationPromise.set_value(); + _notificationPromise = std::move(nextPromise); + } } }; diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 8783a27..7250ee8 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -28,6 +29,7 @@ namespace ChimeraTK { static void testTimeStampSource(); static void testSynchronization(); static void testVersionNumbers(); + static void testBlockingRead(); private: static size_t const N_ELEMENTS = 12; @@ -56,6 +58,7 @@ namespace ChimeraTK { add(BOOST_TEST_CASE(&ProcessArrayTest::testTimeStampSource)); add(BOOST_TEST_CASE(&ProcessArrayTest::testSynchronization)); add(BOOST_TEST_CASE(&ProcessArrayTest::testVersionNumbers)); + add(BOOST_TEST_CASE(&ProcessArrayTest::testBlockingRead)); } }; @@ -435,6 +438,44 @@ namespace ChimeraTK { BOOST_CHECK(receiver->get()[0] == 4); } + template + void ProcessArrayTest::testBlockingRead() { + auto senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); + auto sender = senderReceiver.first; + auto receiver = senderReceiver.second; + + // blocking read should just return the value if it is already there + sender->accessData(0) = 42; + sender->write(); + receiver->read(); + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + + // start blocking read first in the background and send then the data + { + std::thread backgroundTask( [&receiver] { receiver->read(); } ); + usleep(200000); + sender->accessData(0) = 43; + sender->write(); + backgroundTask.join(); + BOOST_CHECK_EQUAL(receiver->accessData(0), 43); + } + + // same with multiple transfers + { + std::thread backgroundTask( [&receiver] { + for(int i=0; i<10; ++i) { + receiver->read(); + BOOST_CHECK_EQUAL(static_cast(receiver->accessData(0)), 2 + i); + } + }); + for(int i=0; i<10; ++i) { + usleep(10000); + sender->accessData(0) = 2+i; + sender->write(); + } + backgroundTask.join(); + } + } } //namespace ChimeraTK test_suite* From e193ae86288bd90c85ec40a83e7c7b82066cfc3f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 23 Dec 2016 15:48:13 +0100 Subject: [PATCH 161/786] prevent warning on Release builds --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 69e28bc..1e4a0ab 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -319,6 +319,7 @@ namespace ChimeraTK { boost::shared_future future; bool atLeastOneFuturePresent = _notificationQueue->pop(future); assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm + (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled future.wait(); } } From 82636e88caf3a243e00de6a5be90b90e4b99161d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 24 Jan 2017 16:05:48 +0100 Subject: [PATCH 162/786] - necessary changes for the latest DeviceAccess version - added "override" keyword to functions inherited from the TransferElement base - deprecated some functions no longer fitting into the concept --- .../ControlSystemAdapter/ProcessArray.h | 92 ++++++++++--------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 1e4a0ab..f93694a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -217,6 +217,8 @@ namespace ChimeraTK { * It can be of a different implementation, though. The size of the assigned * array must be smaller than or equal to the target size. * This behaves excactly like the set(...) method. + * + * @todo THIS FUNCTION IS DEPRECTED */ ProcessArray & operator=(ProcessArray const & other) { set(other); @@ -228,12 +230,17 @@ namespace ChimeraTK { * The size of the assigned array must be smaller than or equal to the * target size. * This behaves exactly like the set(...) method. + * + * @todo THIS FUNCTION IS DEPRECTED, use ScalarRegisterAccessor::operator=() instead */ ProcessArray & operator=(std::vector const & v) { set(v); return *this; } + /* + * @todo THIS FUNCTION IS DEPRECTED + */ operator std::vector() const { return get(); } @@ -242,6 +249,8 @@ namespace ChimeraTK { * Updates this process variable's value with the elements from the * specified vector. The vector's number of elements must match this process * variable's number of elements. + * + * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead */ void set(std::vector const & v) { get() = v; @@ -251,6 +260,8 @@ namespace ChimeraTK { * Updates this process variable's value with the other process variable's * value. The other process variable's number of elements must match this * process variable's number of elements. + * + * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead */ void set(ProcessArray const & other) { set(other.get()); @@ -262,6 +273,8 @@ namespace ChimeraTK { * The boost::scoped_ptr passed must not be null * and must point to a vector that has the same size as the vector it is * swapped with. + * + * @todo THIS FUNCTION IS DEPRECTED, use accessData()[0].swap() instead */ void swap(std::vector & otherVector) { if (otherVector.size() != get().size()) { @@ -277,6 +290,8 @@ namespace ChimeraTK { * The reference returned by this method becomes invalid when a receive, * send, or swap operation is performed on this process variable. Use of an * invalid reference results in undefined behavior. + * + * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead */ std::vector & get() { return mtca4u::NDRegisterAccessor::buffer_2D[0]; @@ -290,40 +305,29 @@ namespace ChimeraTK { * The reference returned by this method becomes invalid when a receive, * send, or swap operation is performed on this process variable. Use of an * invalid reference results in undefined behavior. + * + * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead */ std::vector const & get() const { return mtca4u::NDRegisterAccessor::buffer_2D[0]; } - virtual bool isReadable() const { + bool isReadable() const override { return _instanceType == RECEIVER; } - virtual bool isWriteable() const { + bool isWriteable() const override { return _instanceType == SENDER; } - virtual bool isReadOnly() const { + bool isReadOnly() const override { return !isWriteable(); } - TimeStamp getTimeStamp() const { + TimeStamp getTimeStamp() const override { return ((*_buffers)[_currentIndex]).timeStamp; } - virtual void read(){ - // Obtain futures from the notification queue and wait on them until we receive data. We start with checking - // for data before obtaining a future from the notification queue, since this is faster and the notification - // queue is shorter than the data queue. - while(!readNonBlocking()) { - boost::shared_future future; - bool atLeastOneFuturePresent = _notificationQueue->pop(future); - assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm - (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled - future.wait(); - } - } - /** * Returns the version number that is associated with the current value. * This is the version number that was received with the {@link readNonBlocking()} @@ -341,13 +345,26 @@ namespace ChimeraTK { * source, its version number always stays at zero and the version-number * logic is disabled. */ - VersionNumber getVersionNumber() const { + VersionNumber getVersionNumber() const { /// @todo FIXME this function must be present in TransferElement already! // On the other hand, this should not matter too much because the current // value will be in an undefined state anyway and thus we might not care. return ((*_buffers)[_currentIndex]).versionNumber; } - bool readNonBlocking() { + void doReadTransfer() override { + // Obtain futures from the notification queue and wait on them until we receive data. We start with checking + // for data before obtaining a future from the notification queue, since this is faster and the notification + // queue is shorter than the data queue. + while(!doReadTransferNonBlocking()) { + boost::shared_future future; + bool atLeastOneFuturePresent = _notificationQueue->pop(future); + assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm + (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled + future.wait(); + } + } + + bool doReadTransferNonBlocking() override { if (_instanceType != RECEIVER) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } @@ -366,7 +383,6 @@ namespace ChimeraTK { if (!_versionNumberSource || ((*_buffers)[nextIndex]).versionNumber > getVersionNumber()) { _emptyBufferQueue->push(_currentIndex); _currentIndex = nextIndex; - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( ((*_buffers)[_currentIndex]).value ); return true; } else { _emptyBufferQueue->push(nextIndex); @@ -376,20 +392,12 @@ namespace ChimeraTK { return false; } } + + void postRead() override { + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( ((*_buffers)[_currentIndex]).value ); + } - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * If this process variable has a version-number source, a new version - * number is retrieved from this source and used for the value being sent to - * the receiver. Otherwise, a version number of zero is used. - * - * Throws an exception if this process variable is not a sender. - */ - void write() { + void write() override { VersionNumber newVersionNumber; if (_versionNumberSource) { newVersionNumber = _versionNumberSource->nextVersionNumber(); @@ -411,7 +419,7 @@ namespace ChimeraTK { * * Throws an exception if this process variable is not a sender. */ - void write(VersionNumber newVersionNumber) { + void write(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! ??? writeInternal(newVersionNumber, true); } @@ -434,7 +442,7 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - void writeDestructively() { + void writeDestructively() { /// @todo FIXME this function must be present in TransferElement already! VersionNumber newVersionNumber; if (_versionNumberSource) { newVersionNumber = _versionNumberSource->nextVersionNumber(); @@ -463,7 +471,7 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - void writeDestructively(VersionNumber newVersionNumber) { + void writeDestructively(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! if (!_maySendDestructively) { throw std::runtime_error( "This process variable must not be sent destructively because the corresponding flag has not been set."); @@ -471,28 +479,28 @@ namespace ChimeraTK { writeInternal(newVersionNumber, false); } - const std::type_info& getValueType() const { + const std::type_info& getValueType() const override { return typeid(T); } - bool isArray() const { + bool isArray() const override { return true; } - virtual bool isSameRegister(const boost::shared_ptr& e) const{ + bool isSameRegister(const boost::shared_ptr& e) const override { // only true if the very instance of the transfer element is the same return e.get() == this; } - virtual std::vector > getHardwareAccessingElements(){ + std::vector > getHardwareAccessingElements() override { return { boost::enable_shared_from_this::shared_from_this() }; } - virtual void replaceTransferElement(boost::shared_ptr){ + void replaceTransferElement(boost::shared_ptr) override { // You can't replace anything here. Just do nothing. } - void setPersistentDataStorage(boost::shared_ptr storage) { + void setPersistentDataStorage(boost::shared_ptr storage) override { if(!isWriteable()) return; bool sendInitialValue = false; if(!_persistentDataStorage) sendInitialValue = true; From a8a124680838afb6df9de4dc67b561e47bebafcd Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 24 Jan 2017 16:06:59 +0100 Subject: [PATCH 163/786] - increased the minor version - increased the required version of DeviceAccess --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd6b1bc..ef7273c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 03) -set(${PROJECT_NAME}_PATCH_VERSION 02) +set(${PROJECT_NAME}_MINOR_VERSION 04) +set(${PROJECT_NAME}_PATCH_VERSION 00) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) set(${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}) @@ -45,7 +45,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.18 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.19 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) From 034f77fa5059026c820847817ffc3cc3d84fafa5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 8 Feb 2017 17:03:36 +0100 Subject: [PATCH 164/786] - properly shutdown the potentially still-running readAsync thread in the receiver - moved objects common between the sender and the receiver into a shared state object for better code clarity --- .../ControlSystemAdapter/ProcessArray.h | 180 +++++++++++------- 1 file changed, 107 insertions(+), 73 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index f93694a..c21e128 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -3,11 +3,9 @@ #include #include - #include #include #include -#include #include #include @@ -24,6 +22,7 @@ #include "PersistentDataStorage.h" namespace ChimeraTK { + /** * Array implementation of the ProcessVariable. This implementation is used * for all three use cases (sender, receiver, and stand-alone). @@ -84,7 +83,7 @@ namespace ChimeraTK { _instanceType(instanceType), _vectorSize(initialValue.size()), _maySendDestructively(true), - _buffers(boost::make_shared >(1)), + _sharedState(boost::make_shared(1)), _currentIndex(0) { // It would be better to do the validation before initializing, but this @@ -113,11 +112,7 @@ namespace ChimeraTK { _instanceType(instanceType), _vectorSize(initialValue.size()), _maySendDestructively(false), - _buffers(boost::make_shared>(numberOfBuffers + 2)), - _fullBufferQueue(boost::make_shared>(numberOfBuffers)), - _emptyBufferQueue(boost::make_shared>(numberOfBuffers)), - _notificationQueue(boost::make_shared, - boost::lockfree::capacity<2>>>()), + _sharedState(boost::make_shared(numberOfBuffers)), _currentIndex(0), _versionNumberSource(versionNumberSource) { @@ -145,14 +140,14 @@ namespace ChimeraTK { throw std::invalid_argument("The number of buffers is too large."); } // We have to initialize the buffers by copying in the initial vectors. - for (auto &i : *_buffers) { + for (auto &i : _sharedState->_buffers) { i.value = initialValue; } // The buffer with the index 0 is assigned to the receiver and the // buffer with the index 1 is assigned to the sender. All buffers have // to be added to the empty-buffer queue. - for (std::size_t i = 2; i < _buffers->size(); ++i) { - _emptyBufferQueue->push(i); + for (std::size_t i = 2; i < _sharedState->_buffers.size(); ++i) { + _sharedState->_emptyBufferQueue.push(i); } } @@ -181,10 +176,7 @@ namespace ChimeraTK { _instanceType(instanceType), _vectorSize(receiver->_vectorSize), _maySendDestructively(maySendDestructively), - _buffers(receiver->_buffers), - _fullBufferQueue(receiver->_fullBufferQueue), - _emptyBufferQueue(receiver->_emptyBufferQueue), - _notificationQueue(receiver->_notificationQueue), + _sharedState(receiver->_sharedState), _currentIndex(1), _receiver(receiver), _timeStampSource(timeStampSource), @@ -207,7 +199,28 @@ namespace ChimeraTK { mtca4u::NDRegisterAccessor::buffer_2D.resize(1); mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; // put future into the notification queue - _notificationQueue->push(_notificationPromise.get_future()); + _sharedState->_notificationQueue.push(_sharedState->_notificationPromise.get_future()); + } + + /** + * Exception class for notifying a pending asynchronous doReadTransfer() to shut down + */ + class ShutdownException {}; + + ~ProcessArray() { + // if this is a receiver, shutdown a potentially running readAsync + if(_instanceType == RECEIVER) { + if(this->readAsyncThread.joinable()) { + this->readAsyncThread.interrupt(); + try { + _sharedState->_notificationPromise.set_value(); + } + catch(boost::promise_already_satisfied) { + // ignore -> already notified + } + this->readAsyncThread.join(); + } + } } /** @@ -325,7 +338,7 @@ namespace ChimeraTK { } TimeStamp getTimeStamp() const override { - return ((*_buffers)[_currentIndex]).timeStamp; + return (_sharedState->_buffers[_currentIndex]).timeStamp; } /** @@ -348,7 +361,7 @@ namespace ChimeraTK { VersionNumber getVersionNumber() const { /// @todo FIXME this function must be present in TransferElement already! // On the other hand, this should not matter too much because the current // value will be in an undefined state anyway and thus we might not care. - return ((*_buffers)[_currentIndex]).versionNumber; + return (_sharedState->_buffers[_currentIndex]).versionNumber; } void doReadTransfer() override { @@ -357,10 +370,11 @@ namespace ChimeraTK { // queue is shorter than the data queue. while(!doReadTransferNonBlocking()) { boost::shared_future future; - bool atLeastOneFuturePresent = _notificationQueue->pop(future); + bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled future.wait(); + boost::this_thread::interruption_point(); } } @@ -371,21 +385,21 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. - if ((*_buffers)[_currentIndex].value.size() != _vectorSize) { + if (_sharedState->_buffers[_currentIndex].value.size() != _vectorSize) { throw std::runtime_error("Cannot run receive operation because the size of the vector belonging to the current" " buffer has been modified."); } std::size_t nextIndex; - if (_fullBufferQueue->pop(nextIndex)) { + if (_sharedState->_fullBufferQueue.pop(nextIndex)) { // We only use the incoming update if it has a higher version number // than the current value. This check is disabled when there is no // version number source. - if (!_versionNumberSource || ((*_buffers)[nextIndex]).versionNumber > getVersionNumber()) { - _emptyBufferQueue->push(_currentIndex); + if (!_versionNumberSource || (_sharedState->_buffers[nextIndex]).versionNumber > getVersionNumber()) { + _sharedState->_emptyBufferQueue.push(_currentIndex); _currentIndex = nextIndex; return true; } else { - _emptyBufferQueue->push(nextIndex); + _sharedState->_emptyBufferQueue.push(nextIndex); return false; } } else { @@ -394,7 +408,7 @@ namespace ChimeraTK { } void postRead() override { - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( ((*_buffers)[_currentIndex]).value ); + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); } void write() override { @@ -568,44 +582,59 @@ namespace ChimeraTK { * on this process array. */ bool _maySendDestructively; - + /** - * Buffers that hold the actual values. + * The state shared between the sender and the receiver */ - boost::shared_ptr> _buffers; + struct SharedState { - /** - * Queue holding the indices of the full buffers. Those are the buffers - * that have been sent but not yet received. We do not use an spsc_queue - * for this queue, because might want to take elements from the sending - * thread, so there are two threads which might consume elements and thus - * an spsc_queue is not safe. - */ - boost::shared_ptr> _fullBufferQueue; + SharedState(size_t numberOfBuffers) + : _buffers(numberOfBuffers + 2), + _fullBufferQueue(numberOfBuffers), + _emptyBufferQueue(numberOfBuffers) + {} - /** - * Queue holding the empty buffers. Those are the buffers that have been - * returned to the sender by the receiver. - */ - boost::shared_ptr> _emptyBufferQueue; + /** + * Buffers that hold the actual values. + */ + std::vector _buffers; - /** - * Queue holding futures for notification. The receiver can obtain a future from this queue when the - * _fullBufferQueue is empty and wait on it until new data has been sent. - * - * It is sufficient to have a fixed queue length of 2, since it is only important to guarantee the presence of - * at least one unfulfilled future in the queue. - * - * We are using a shared future, since the spsc_queue expects a copy-constructable data type. Since the data - * type void is not particularly expensive to copy, this should have no performance impact. - */ - boost::shared_ptr, - boost::lockfree::capacity<2>>> _notificationQueue; - - /** - * The promise corresponding to the unfulfilled future in the _notificationQueue - */ - boost::promise _notificationPromise; + /** + * Queue holding the indices of the full buffers. Those are the buffers + * that have been sent but not yet received. We do not use an spsc_queue + * for this queue, because might want to take elements from the sending + * thread, so there are two threads which might consume elements and thus + * an spsc_queue is not safe. + */ + boost::lockfree::queue _fullBufferQueue; + + /** + * Queue holding the empty buffers. Those are the buffers that have been + * returned to the sender by the receiver. + */ + boost::lockfree::spsc_queue _emptyBufferQueue; + + /** + * Queue holding futures for notification. The receiver can obtain a future from this queue when the + * _fullBufferQueue is empty and wait on it until new data has been sent. + * + * It is sufficient to have a fixed queue length of 2, since it is only important to guarantee the presence of + * at least one unfulfilled future in the queue. + * + * We are using a shared future, since the spsc_queue expects a copy-constructable data type. Since the data + * type void is not particularly expensive to copy, this should have no performance impact. + */ + boost::lockfree::spsc_queue< boost::shared_future, boost::lockfree::capacity<2> > _notificationQueue; + + /** + * The promise corresponding to the unfulfilled future in the _notificationQueue. + * This promise is basically only used by the sender. Only in the destructor of the receiver it is used to + * shutdown properly, thus it must be in the shared state. + */ + boost::promise _notificationPromise; + + }; + boost::shared_ptr _sharedState; /** * Index into the _buffers array that is currently owned by this instance @@ -675,38 +704,38 @@ namespace ChimeraTK { _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); - ((*_buffers)[_currentIndex]).timeStamp = newTimeStamp; - ((*_buffers)[_currentIndex]).versionNumber = newVersionNumber; + (_sharedState->_buffers[_currentIndex]).timeStamp = newTimeStamp; + (_sharedState->_buffers[_currentIndex]).versionNumber = newVersionNumber; if (shouldCopy) { - (*_buffers)[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + _sharedState->_buffers[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; } else { - (*_buffers)[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + _sharedState->_buffers[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); } std::size_t nextIndex; - if (_emptyBufferQueue->pop(nextIndex)) { - _fullBufferQueue->push(_currentIndex); + if (_sharedState->_emptyBufferQueue.pop(nextIndex)) { + _sharedState->_fullBufferQueue.push(_currentIndex); } else { // We can still send, but we will lose older data. - if (_fullBufferQueue->pop(nextIndex)) { - _fullBufferQueue->push(_currentIndex); + if (_sharedState->_fullBufferQueue.pop(nextIndex)) { + _sharedState->_fullBufferQueue.push(_currentIndex); } else { // It is possible, that we did not find an empty buffer but before // we could get a full buffer, the receiver processed all full // buffers. In this case, the queue of empty buffers cannot be empty // any longer because we have at least four buffers. - if (!_emptyBufferQueue->pop(nextIndex)) { + if (!_sharedState->_emptyBufferQueue.pop(nextIndex)) { // This should never happen and is just an assertion for extra // safety. throw std::runtime_error( "Assertion that empty-buffer queue has at least one element failed."); } - _fullBufferQueue->push(_currentIndex); + _sharedState->_fullBufferQueue.push(_currentIndex); } } if (shouldCopy) { - (*_buffers)[nextIndex].timeStamp = newTimeStamp; - (*_buffers)[nextIndex].versionNumber = newVersionNumber; + _sharedState->_buffers[nextIndex].timeStamp = newTimeStamp; + _sharedState->_buffers[nextIndex].versionNumber = newVersionNumber; } _currentIndex = nextIndex; if (_sendNotificationListener) { @@ -716,9 +745,14 @@ namespace ChimeraTK { // unfulfilled future in the notification queue at all times, so we try pushing a new one first. If this // failes, there is already one unfulfilled and one fulfilled future in the queue, so we are done. boost::promise nextPromise; - if(_notificationQueue->push(nextPromise.get_future())) { - _notificationPromise.set_value(); - _notificationPromise = std::move(nextPromise); + if(_sharedState->_notificationQueue.push(nextPromise.get_future())) { + try { + _sharedState->_notificationPromise.set_value(); + } + catch(boost::promise_already_satisfied) { + // ignore -> this may happen only during shutdown + } + _sharedState->_notificationPromise = std::move(nextPromise); } } From d51e19a6b6dd1265ded1a9fa4fb2b3604efb4a91 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 10 Feb 2017 14:30:10 +0100 Subject: [PATCH 165/786] removed doxygen warnings --- .../ControlSystemAdapter/ProcessArray.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index c21e128..4570ad8 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -164,7 +164,7 @@ namespace ChimeraTK { * The optional send-notification listener is notified every time the * sender's {@link ProcessArray::write()} method is called. It can be * used to queue a request for the receiver's - * {@link ProcessArray::readNonBlocking()} method to be called. The process + * readNonBlocking() method to be called. The process * variable passed to the listener is the receiver and not the sender. */ ProcessArray(InstanceType instanceType, bool maySendDestructively, @@ -343,13 +343,13 @@ namespace ChimeraTK { /** * Returns the version number that is associated with the current value. - * This is the version number that was received with the {@link readNonBlocking()} + * This is the version number that was received with the readNonBlocking() * operation that received the respective value or the last {@link write()} * operation. If the last send operation was destructive, the version number * (like the current value) is undefined. * * The version number is used to resolve conflicting updates. When an update - * is received using the {@link readNonBlocking()} method, it is only used if its + * is received using the readNonBlocking() method, it is only used if its * value has a version number that is greater than the version number of the * current value. Initially, each process variable has a version number of * zero. @@ -794,7 +794,7 @@ namespace ChimeraTK { * The sender allows full read-write access. Changes that have been made to * the sender can be sent to the receiver through the * {@link ProcessArray::write()} method. The receiver can be updated with these - * changes by calling its {@link ProcessArray::readNonBlocking()} method. + * changes by calling its readNonBlocking() method. * * The synchronization is implemented in a thread-safe manner, so that the * sender and the receiver can safely be used by different threads without @@ -805,7 +805,7 @@ namespace ChimeraTK { * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This * number specifies, how many times {@link ProcessArray::write()} can be called - * in a row without losing data when {@link ProcessArray::readNonBlocking()} is not + * in a row without losing data when readNonBlocking() is not * called in between. * * If the maySendDestructively flag is true (it is @@ -820,7 +820,7 @@ namespace ChimeraTK { * * The optional send-notification listener is notified every time the sender's * {@link ProcessArray::write()} method is called. It can be used to queue a - * request for the receiver's {@link ProcessArray::readNonBlocking()} method to be + * request for the receiver's readNonBlocking() method to be * called. The process variable passed to the listener is the receiver and * not the sender. * @@ -845,7 +845,7 @@ namespace ChimeraTK { * The sender allows full read-write access. Changes that have been made to * the sender can be sent to the receiver through the * {@link ProcessArray::write()} method. The receiver can be updated with these - * changes by calling its {@link ProcessArray::readNonBlocking()} method. + * changes by calling its readNonBlocking() method. * * The synchronization is implemented in a thread-safe manner, so that the * sender and the receiver can safely be used by different threads without @@ -856,7 +856,7 @@ namespace ChimeraTK { * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This * number specifies, how many times {@link ProcessArray::write()} can be called - * in a row without losing data when {@link ProcessArray::readNonBlocking()} is not + * in a row without losing data when readNonBlocking() is not * called in between. * * If the maySendDestructively flag is true (it is @@ -871,7 +871,7 @@ namespace ChimeraTK { * * The optional send-notification listener is notified every time the sender's * {@link ProcessArray::write()} method is called. It can be used to queue a - * request for the receiver's {@link ProcessArray::readNonBlocking()} method to be + * request for the receiver's readNonBlocking() method to be * called. The process variable passed to the listener is the receiver and * not the sender. * From ffdf0123293190351d7ad4f7bd655d7ff2cb6bde Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 10 Feb 2017 15:33:24 +0100 Subject: [PATCH 166/786] added include script to generate doxygen documentation from cmake/Doxyfile.in --- cmake/enable_doxygen_documentation.cmake | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 cmake/enable_doxygen_documentation.cmake diff --git a/cmake/enable_doxygen_documentation.cmake b/cmake/enable_doxygen_documentation.cmake new file mode 100644 index 0000000..382546a --- /dev/null +++ b/cmake/enable_doxygen_documentation.cmake @@ -0,0 +1,54 @@ +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +####################################################################################################################### +# Enable doxygen documentation, mainly (but not only for) for libraries +# +# Requirements: +# * A file 'cmake/Doxyfile.in' which is configured to generate output to ${CMAKE_BINARY_DIR}/doc/ +# +# Generates: +# * A target 'doc' +# * The target is automatically build unless -DSUPPRESS_AUTO_DOC_BUILD=true is given at configuration +# * The documentation is installed to share/doc/${PROJECT_NAME}-${${PROJECT_NAME}_SOVERSION} +# +# If generation of the documentation is off, 'make install' might not install documentation, or you install outdated documentation! +# +###################################################################################################################### + + +#use -DSUPPRESS_AUTO_DOC_BUILD=true to suppress to create the doc with every +#build. The 'make doc' target will still exist +if(SUPPRESS_AUTO_DOC_BUILD) + message("ATTENTION: Automatically building the documentation is disabled. 'make install' might not install documentation, or install outdated documentation! Re-enable by configuring cmake with '-DSUPPRESS_AUTO_DOC_BUILD=false'") + unset(DOC_DEPENDENCY) +else(SUPPRESS_AUTO_DOC_BUILD) + message("Automatically building the documentation is enabled. Disable by configuring cmake with '-DSUPPRESS_AUTO_DOC_BUILD=true'") + set(DOC_DEPENDENCY ALL) +endif(SUPPRESS_AUTO_DOC_BUILD) + + +find_package(Doxygen) +if(DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + + add_custom_target(doc ${DOC_DEPENDENCY} + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + #note the / after ${CMAKE_BINARY_DIR}/doc/. This causes the directory to be renamed to the destination, not copied into + #The optional allows make install to run even if documentation has not been build. + install(DIRECTORY ${CMAKE_BINARY_DIR}/doc/ DESTINATION share/doc/${PROJECT_NAME}-${${PROJECT_NAME}_SOVERSION} + COMPONENT doc OPTIONAL) +else(DOXYGEN_FOUND) + message("Doxygen not found, documentation cannot be build.") +endif(DOXYGEN_FOUND) From eab825b6ea2aaef517896b0decc18691a8543905 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 10 Feb 2017 15:36:40 +0100 Subject: [PATCH 167/786] Moved Doxyfile to cmake-configurable Doxyfile.in --- doc/Doxyfile => cmake/Doxyfile.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename doc/Doxyfile => cmake/Doxyfile.in (99%) diff --git a/doc/Doxyfile b/cmake/Doxyfile.in similarity index 99% rename from doc/Doxyfile rename to cmake/Doxyfile.in index afb9f4f..2057aac 100644 --- a/doc/Doxyfile +++ b/cmake/Doxyfile.in @@ -38,7 +38,7 @@ PROJECT_NAME = "ChimeraTK Control System Adapter" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = @ChimeraTK-ControlSystemAdapter_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -51,14 +51,14 @@ PROJECT_BRIEF = "Tools to facilitate the interfacing to control systems # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = ./DESY_logo.png +PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/doc/DESY_logo.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = . +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/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 @@ -781,8 +781,8 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../src \ - ../include +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src \ + @CMAKE_CURRENT_SOURCE_DIR@/include # 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 From 02e286ee9319f17fda202e1ece164b159b70fa75 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 10 Feb 2017 15:37:42 +0100 Subject: [PATCH 168/786] - Added automatic documentation and 'doc' target - Increased required DeviceAccess version --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef7273c..1c51074 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.19 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.21 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) @@ -131,4 +131,8 @@ include(${CMAKE_SOURCE_DIR}/cmake/create_cmake_config_files.cmake) ## The instructions to create a debian package are loaded from a separate file + #include(${CMAKE_SOURCE_DIR}/cmake/prepare_debian_package.cmake) + +include(${CMAKE_SOURCE_DIR}/cmake/enable_doxygen_documentation.cmake) + From 16937e7cc32113d7f659c7efd609308b1a542fa3 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 13 Feb 2017 10:56:02 +0100 Subject: [PATCH 169/786] removed unneeded exception class --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index c21e128..91f33bb 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -202,11 +202,6 @@ namespace ChimeraTK { _sharedState->_notificationQueue.push(_sharedState->_notificationPromise.get_future()); } - /** - * Exception class for notifying a pending asynchronous doReadTransfer() to shut down - */ - class ShutdownException {}; - ~ProcessArray() { // if this is a receiver, shutdown a potentially running readAsync if(_instanceType == RECEIVER) { From 58c25ac61ede9c4359c0015e7c2c0858249e67b5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 14 Feb 2017 15:08:27 +0100 Subject: [PATCH 170/786] added optimised implementation for asynchronous read. --- .../ControlSystemAdapter/ProcessArray.h | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index fc75f02..3e49cef 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -401,8 +401,55 @@ namespace ChimeraTK { return false; } } + + mtca4u::TransferFuture readAsync() override { + if (_instanceType != RECEIVER) { + throw std::logic_error("Receive operation is only allowed for a receiver process variable."); + } + ChimeraTK::ExperimentalFeatures::check("asynchronous read"); + if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; // the last future given out by this fuction is still active + + // check if we already have data + boost::promise promise; + boost::shared_future readyFuture = promise.get_future().share(); + promise.set_value(); + if(doReadTransferNonBlocking()) { + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + return mtca4u::TransferElement::activeFuture; + } + else { + boost::shared_future future; + // obtain future from queue + bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); + assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm + (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled + // if this future is already fulfilled, obtain the next one + if(future.is_ready()) { + // before we can obtain the next future, we must check for new data once more + if(doReadTransferNonBlocking()) { + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + return mtca4u::TransferElement::activeFuture; + } + atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); + assert(atLeastOneFuturePresent); + } + // return the future + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + return mtca4u::TransferElement::activeFuture; + } + } void postRead() override { + // if this is called inside mtca4u::TransferFuture::wait(), we still need to obtain the new data from the queue + if(mtca4u::TransferElement::hasActiveFuture) { + bool hasNewDataAfterFuture = doReadTransferNonBlocking(); + assert(hasNewDataAfterFuture); // if this is not true, we did not receive new data despied being notified about new data... + (void)hasNewDataAfterFuture; // prevent warning in case asserts are disabled + } + // swap data out of the queue buffer mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); } From a262fc28fae264d4358d39f23800c6e97899aadc Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 16 Feb 2017 10:56:56 +0100 Subject: [PATCH 171/786] added test for async read --- tests/src/testAsyncRead.cpp | 254 ++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 tests/src/testAsyncRead.cpp diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp new file mode 100644 index 0000000..37656a6 --- /dev/null +++ b/tests/src/testAsyncRead.cpp @@ -0,0 +1,254 @@ +#include +#include +#include + +#include +#include + +#include +#include + +#include "ProcessArray.h" + + +using namespace boost::unit_test_framework; +using namespace ChimeraTK; +using namespace mtca4u; + +/**********************************************************************************************************************/ +class AsyncReadTest { + public: + + /// test normal asychronous read + void testAsyncRead(); + + /// test the TransferElement::readAny() function on ProcessArrays + void testReadAny(); + +}; + +/**********************************************************************************************************************/ +class AsyncReadTestSuite : public test_suite { + public: + AsyncReadTestSuite() : test_suite("Async read test suite") { + boost::shared_ptr asyncReadTest( new AsyncReadTest ); + + add( BOOST_CLASS_TEST_CASE( &AsyncReadTest::testAsyncRead, asyncReadTest ) ); + add( BOOST_CLASS_TEST_CASE( &AsyncReadTest::testReadAny, asyncReadTest ) ); + }}; + +/**********************************************************************************************************************/ +test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/ [] ) +{ + ChimeraTK::ExperimentalFeatures::enable(); + + framework::master_test_suite().p_name.value = "Async read test suite"; + framework::master_test_suite().add(new AsyncReadTestSuite); + + return NULL; +} + +/**********************************************************************************************************************/ +void AsyncReadTest::testAsyncRead() { + std::cout << "testAsyncRead" << std::endl; + + auto senderReceiver = createSynchronizedProcessArray(1, "Test"); + auto sender = senderReceiver.first; + auto receiver = senderReceiver.second; + + // obtain register accessor with integral type + auto accessor = mtca4u::ScalarRegisterAccessor(receiver); + auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); + + // simple reading through readAsync without actual need + TransferFuture future; + senderAccessor = 5; + senderAccessor.write(); + future = accessor.readAsync(); + future.wait(); + BOOST_CHECK( accessor == 5 ); + + // check that future's wait() function won't return before the read is complete + for(int i=0; i<5; ++i) { + senderAccessor = 42+i; + future = accessor.readAsync(); + std::atomic flag; + flag = false; + std::thread thread([&future, &flag] { future.wait(); flag = true; }); + usleep(100000); + BOOST_CHECK(flag == false); + senderAccessor.write(); + thread.join(); + BOOST_CHECK( accessor == 42+i ); + } + + // check that obtaining the same future multiple times works properly + senderAccessor = 666; + for(int i=0; i<5; ++i) { + future = accessor.readAsync(); + BOOST_CHECK( accessor == 46 ); // still the old value from the last test part + } + senderAccessor.write(); + future.wait(); + BOOST_CHECK( accessor == 666 ); + + // now try another asynchronous transfer + senderAccessor = 999; + future = accessor.readAsync(); + std::atomic flag; + flag = false; + std::thread thread([&future, &flag] { future.wait(); flag = true; }); + usleep(100000); + BOOST_CHECK(flag == false); + senderAccessor.write(); + thread.join(); + BOOST_CHECK( accessor == 999 ); + +} + +/**********************************************************************************************************************/ + +void AsyncReadTest::testReadAny() { + std::cout << "testReadAny" << std::endl; + + // obtain accessors for process variables + auto senderReceiver1 = createSynchronizedProcessArray(1, "Test1"); + auto a1 = mtca4u::ScalarRegisterAccessor(senderReceiver1.second); + auto s1 = mtca4u::ScalarRegisterAccessor(senderReceiver1.first); + auto senderReceiver2 = createSynchronizedProcessArray(1, "Test2"); + auto a2 = mtca4u::ScalarRegisterAccessor(senderReceiver2.second); + auto s2 = mtca4u::ScalarRegisterAccessor(senderReceiver2.first); + auto senderReceiver3 = createSynchronizedProcessArray(1, "Test3"); + auto a3 = mtca4u::ScalarRegisterAccessor(senderReceiver3.second); + auto s3 = mtca4u::ScalarRegisterAccessor(senderReceiver3.first); + auto senderReceiver4 = createSynchronizedProcessArray(1, "Test4"); + auto a4 = mtca4u::ScalarRegisterAccessor(senderReceiver4.second); + auto s4 = mtca4u::ScalarRegisterAccessor(senderReceiver4.first); + + // initialise the buffers of the accessors + a1 = 1; + a2 = 2; + a3 = 3; + a4 = 4; + + // initialise the dummy registers + s1 = 42; + s2 = 123; + s3 = 120; + s4 = 345; + + // variable Test1 + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s1.write(); + thread.join(); + BOOST_CHECK( a1 == 42 ); + } + + // variable Test3 + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s3.write(); + thread.join(); + BOOST_CHECK( a3 == 120 ); + } + + // variable Test3 again + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s3 = 121; + s3.write(); + thread.join(); + BOOST_CHECK( a3 == 121 ); + } + + // variable Test2 + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s2.write(); + thread.join(); + BOOST_CHECK( a2 == 123 ); + } + + // variable Test4 + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s4.write(); + thread.join(); + BOOST_CHECK( a4 == 345 ); + } + + // variable Test4 again + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s4.write(); + thread.join(); + BOOST_CHECK( a4 == 345 ); + } + + // variable Test3 a 3rd time + { + // launch the readAny in a background thread + std::atomic flag{false}; + std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + + // check that it doesn't return too soon + usleep(100000); + BOOST_CHECK(flag == false); + + // write register and check that readAny() completes + s3 = 122; + s3.write(); + thread.join(); + BOOST_CHECK( a3 == 122 ); + } + +} From 13f8a823845fb17dc16b7fb62c10642d3ea958fd Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 16 Feb 2017 13:52:06 +0100 Subject: [PATCH 172/786] fix a bug in async read (was triggering an assertion) --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 3e49cef..5fb9799 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -416,6 +416,7 @@ namespace ChimeraTK { if(doReadTransferNonBlocking()) { mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; + _asyncReadTransferNeeded = false; return mtca4u::TransferElement::activeFuture; } else { @@ -430,6 +431,7 @@ namespace ChimeraTK { if(doReadTransferNonBlocking()) { mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; + _asyncReadTransferNeeded = false; return mtca4u::TransferElement::activeFuture; } atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); @@ -438,13 +440,15 @@ namespace ChimeraTK { // return the future mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; + _asyncReadTransferNeeded = true; return mtca4u::TransferElement::activeFuture; } } void postRead() override { - // if this is called inside mtca4u::TransferFuture::wait(), we still need to obtain the new data from the queue - if(mtca4u::TransferElement::hasActiveFuture) { + // if this is called inside mtca4u::TransferFuture::wait(), we may still need to obtain the new data from the queue + if(_asyncReadTransferNeeded) { + _asyncReadTransferNeeded = false; bool hasNewDataAfterFuture = doReadTransferNonBlocking(); assert(hasNewDataAfterFuture); // if this is not true, we did not receive new data despied being notified about new data... (void)hasNewDataAfterFuture; // prevent warning in case asserts are disabled @@ -715,6 +719,12 @@ namespace ChimeraTK { * Variable ID for the persistent data storage */ size_t _persistentDataStorageID{0}; + + /** + * Flag set in readAsync() whether doReadTransferNonBlocking() must or must not be called in postRead() after the + * future go tready. + */ + bool _asyncReadTransferNeeded{false}; /** * Internal implementation of the various {@code send} methods. All these From 94f7272ed728c5fefd9fafe1271fd69d832b11c2 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 20 Feb 2017 16:55:39 +0100 Subject: [PATCH 173/786] TransferElement::readAsync() is now returning a reference --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 5fb9799..5890933 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -402,7 +402,7 @@ namespace ChimeraTK { } } - mtca4u::TransferFuture readAsync() override { + mtca4u::TransferFuture& readAsync() override { if (_instanceType != RECEIVER) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } From b61af7748f6bd9a333e2a8f8ab4fbdfa31b6b4f4 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 20 Feb 2017 17:30:08 +0100 Subject: [PATCH 174/786] the TransferFuture is now non-copyable --- tests/src/testAsyncRead.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index 37656a6..59509b1 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -61,20 +61,20 @@ void AsyncReadTest::testAsyncRead() { auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); // simple reading through readAsync without actual need - TransferFuture future; + TransferFuture *future; senderAccessor = 5; senderAccessor.write(); - future = accessor.readAsync(); - future.wait(); + future = &(accessor.readAsync()); + future->wait(); BOOST_CHECK( accessor == 5 ); // check that future's wait() function won't return before the read is complete for(int i=0; i<5; ++i) { senderAccessor = 42+i; - future = accessor.readAsync(); + future = &(accessor.readAsync()); std::atomic flag; flag = false; - std::thread thread([&future, &flag] { future.wait(); flag = true; }); + std::thread thread([&future, &flag] { future->wait(); flag = true; }); usleep(100000); BOOST_CHECK(flag == false); senderAccessor.write(); @@ -85,19 +85,19 @@ void AsyncReadTest::testAsyncRead() { // check that obtaining the same future multiple times works properly senderAccessor = 666; for(int i=0; i<5; ++i) { - future = accessor.readAsync(); + future = &(accessor.readAsync()); BOOST_CHECK( accessor == 46 ); // still the old value from the last test part } senderAccessor.write(); - future.wait(); + future->wait(); BOOST_CHECK( accessor == 666 ); // now try another asynchronous transfer senderAccessor = 999; - future = accessor.readAsync(); + future = &(accessor.readAsync()); std::atomic flag; flag = false; - std::thread thread([&future, &flag] { future.wait(); flag = true; }); + std::thread thread([&future, &flag] { future->wait(); flag = true; }); usleep(100000); BOOST_CHECK(flag == false); senderAccessor.write(); From 01c48f88add28c82fd3829c447b2fb5681ba49a5 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 2 Mar 2017 11:55:02 +0100 Subject: [PATCH 175/786] added missing exports to parent scope in add_dependency macro --- cmake/add_dependency.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index 96f116e..eef63ea 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -36,13 +36,17 @@ FUNCTION(add_dependency dependency_project_name required_version) include_directories(SYSTEM ${${dependency_project_name}_INCLUDE_DIRS} ${${dependency_project_name}_INCLUDE_DIR}) link_directories(${${dependency_project_name}_LIBRARY_DIRS}) link_directories(${${dependency_project_name}_LIBRARY_DIR}) + #set the cxx flags used in the child project SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) + #set all the flags we found also in the parent scope, so the child can hand them over to the grand children SET(${dependency_project_name}_LIBRARIES ${${dependency_project_name}_LIBRARIES} PARENT_SCOPE) SET(${dependency_project_name}_LIBRARY_DIRS "${${dependency_project_name}_LIBRARY_DIRS} ${${dependency_project_name}_LIBRARY_DIR}" PARENT_SCOPE) SET(${dependency_project_name}_LINKER_FLAGS "${${dependency_project_name}_LINKER_FLAGS} ${${dependency_project_name}_LINK_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_LINK_FLAGS "${${dependency_project_name}_LINKER_FLAGS} ${${dependency_project_name}_LINK_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_CXX_FLAGS "${${dependency_project_name}_CXX_FLAGS}" PARENT_SCOPE) SET(${dependency_project_name}_FOUND ${${dependency_project_name}_FOUND} PARENT_SCOPE) + SET(${dependency_project_name}_VERSION ${${dependency_project_name}_VERSION} PARENT_SCOPE) + SET(${dependency_project_name}_INCLUDE_DIRS ${${dependency_project_name}_INCLUDE_DIRS} PARENT_SCOPE) ENDFUNCTION(add_dependency) From c4af66ff473d88f51b2dde6bd1011936119948f7 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 30 Mar 2017 08:23:18 +0200 Subject: [PATCH 176/786] Added instructions how to use the project template. --- HowTo.project-template | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 HowTo.project-template diff --git a/HowTo.project-template b/HowTo.project-template new file mode 100644 index 0000000..6d0d6d8 --- /dev/null +++ b/HowTo.project-template @@ -0,0 +1,38 @@ +This project uses the ChimeraTK project-template. + +This file descibes how to corretly use the template project. + +Concept: +The project template is merged via git. The idea is to have the whole project-template repository merged into the project which uses it, incl. all the history. Like this updates and improvements in the template scripts can easily be propagated to all client projects by simply merging the head of project template. + +Instructions: + +A) Preparing your project to use the project template + +1. Add the project template repository as a git remote to your project. This only is done once for each local repository. +$ git remote add project-template https://github.com/ChimeraTK/project-template.git + +We intentionally use the read-only https version here. Never write to the project template from one of the client projects! + +2. Update the remote on you hard disk so git knows what is in project-template +$ git remote update + +3. Merge the project template master into your project +$ git merge project-template/master + +Not you can use the files which are provided in the cmake directory in your CMakeLists.txt + +B) Getting updates of project template + +Just repeat steps 2. and 3. of section A. If you are on a different computer with a different checkout directory, you might have to repeat step 1 if the remote is not there (check with 'git remote show'). + +C) Updating/Improving the project template. + +Never write to the project template from one of the client projects! +If you have improvements or additions to the project template, check out the project-template repository separately, make the modifications and commit/push them. + +Be careful not to break functionality for other project which are using the template! + +After the project template is updated you can merge the changes into your project as described in section B). + +Note: For testing you might want to add your local repository of project-template as a remote to your project, so you don't have to push untested changes. Be careful that everything is pushed to github when you are done in order not to lose consistency of the repositories. From 1d8d7df463bdcc855a8dda03cca4c2c85ad291b7 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 18 Apr 2017 17:01:48 +0200 Subject: [PATCH 177/786] added function to obtain a unique ID for a process array --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 5890933..c9c02ea 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -573,6 +573,13 @@ namespace ChimeraTK { } } + /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the + * same variable but different for any other process variable within the same process. The unique ID will not be + * persistent accross executions of the process. */ + size_t getUniqueId() const { + return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state + } + private: /** From 228962e6bf782c63eb86aff4e81fbe962414a69d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 20 Apr 2017 18:22:22 +0200 Subject: [PATCH 178/786] implement doReadTransferLatest() which has been introduced in the latest DeviceAccess version --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index c9c02ea..77d96b3 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -402,6 +402,12 @@ namespace ChimeraTK { } } + bool doReadTransferLatest() override { + bool hasRead = false; + while(doReadTransferNonBlocking()) hasRead = true; + return hasRead; + } + mtca4u::TransferFuture& readAsync() override { if (_instanceType != RECEIVER) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); From a55cbc1c353818b4ec95de962e0e98c5d3ed8b81 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 27 Apr 2017 18:03:00 +0200 Subject: [PATCH 179/786] needed changes for latest DeviceAccess version (bool-type return value of TransferElement::write()) --- .../ControlSystemAdapter/ProcessArray.h | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 77d96b3..1c5660f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -463,14 +463,14 @@ namespace ChimeraTK { mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); } - void write() override { + bool write() override { VersionNumber newVersionNumber; if (_versionNumberSource) { newVersionNumber = _versionNumberSource->nextVersionNumber(); } else { newVersionNumber = 0; } - write(newVersionNumber); + return write(newVersionNumber); } /** @@ -485,8 +485,8 @@ namespace ChimeraTK { * * Throws an exception if this process variable is not a sender. */ - void write(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! ??? - writeInternal(newVersionNumber, true); + bool write(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! ??? + return writeInternal(newVersionNumber, true); } /** @@ -508,14 +508,14 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - void writeDestructively() { /// @todo FIXME this function must be present in TransferElement already! + bool writeDestructively() { /// @todo FIXME this function must be present in TransferElement already! VersionNumber newVersionNumber; if (_versionNumberSource) { newVersionNumber = _versionNumberSource->nextVersionNumber(); } else { newVersionNumber = 0; } - writeDestructively(newVersionNumber); + return writeDestructively(newVersionNumber); } /** @@ -537,12 +537,12 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - void writeDestructively(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! + bool writeDestructively(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! if (!_maySendDestructively) { throw std::runtime_error( "This process variable must not be sent destructively because the corresponding flag has not been set."); } - writeInternal(newVersionNumber, false); + return writeInternal(newVersionNumber, false); } const std::type_info& getValueType() const override { @@ -744,8 +744,10 @@ namespace ChimeraTK { * methods basically do the same and only differ in whether the data in the * sent array is copied to the buffer used as the new current buffer and * which version number is used. + * + * The return value will be true, if older data was overwritten during the send operation, or otherwise false. */ - void writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { + bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { if (_instanceType != SENDER) { throw std::logic_error( "Send operation is only allowed for a sender process variable."); @@ -778,12 +780,14 @@ namespace ChimeraTK { _sharedState->_buffers[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); } std::size_t nextIndex; + bool lostData = false; if (_sharedState->_emptyBufferQueue.pop(nextIndex)) { _sharedState->_fullBufferQueue.push(_currentIndex); } else { // We can still send, but we will lose older data. if (_sharedState->_fullBufferQueue.pop(nextIndex)) { _sharedState->_fullBufferQueue.push(_currentIndex); + lostData = true; } else { // It is possible, that we did not find an empty buffer but before // we could get a full buffer, the receiver processed all full @@ -819,8 +823,9 @@ namespace ChimeraTK { } _sharedState->_notificationPromise = std::move(nextPromise); } - } + return lostData; + } }; /*********************************************************************************************************************/ From 77ae038707dd3c646bf173a56b086317d1bf5494 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 5 May 2017 10:37:47 +0200 Subject: [PATCH 180/786] added variable names to exception descriptions --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 1c5660f..675a4ef 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -750,14 +750,15 @@ namespace ChimeraTK { bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { if (_instanceType != SENDER) { throw std::logic_error( - "Send operation is only allowed for a sender process variable."); + "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); } // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. if (mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been modified."); + "Cannot run receive operation because the size of the vector belonging to the current buffer has been " + "modified. Variable name: "+this->getName()); } // First update the persistent data storage, if any was associated. This cannot be done after sending, since the // value might no longer be available within this instance. From 37bb329fe5948e496ad056d49e41843c1ed59508 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 5 May 2017 18:32:07 +0200 Subject: [PATCH 181/786] corrected the required version of DeviceAccess --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c51074..428ef8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.21 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.23 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) From b5c4ef19ad78a87edbdd9733ab5fc2d5bd448aaf Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 May 2017 13:59:41 +0200 Subject: [PATCH 182/786] Don't use 0 as a default value for the scalar initialValue (in createProcessArray()), since this does not work for std::string (it compiles but leads to a very strange exception when calling the function). Instead the default constructor of the type T is used, which results in a initialisation with 0 for all numeric data types and with an empty string for std::string. --- .../ControlSystemAdapter/DevicePVManager.h | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index ac90d68..9dc9a86 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -82,7 +82,7 @@ namespace ChimeraTK { SynchronizationDirection synchronizationDirection, const std::string& processVariableName, std::size_t size, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", - T initialValue = 0, bool maySendDestructively = false, std::size_t numberOfBuffers = 2); + T initialValue = T(), bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** * Creates a new process array and registers it with the PV manager. @@ -228,16 +228,16 @@ namespace ChimeraTK { const std::string& unit, const std::string& description, T initialValue, bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { - case controlSystemToDevice: - return _pvManager->createProcessArrayControlSystemToDevice( - processVariableName, std::vector(size, initialValue), unit, description, - maySendDestructively, numberOfBuffers).second; - case deviceToControlSystem: - return _pvManager->createProcessArrayDeviceToControlSystem( - processVariableName, std::vector(size, initialValue), unit, description, - maySendDestructively, numberOfBuffers).second; - default: - throw std::invalid_argument("invalid SynchronizationDirection"); + case controlSystemToDevice: + return _pvManager->createProcessArrayControlSystemToDevice( + processVariableName, std::vector(size, initialValue), unit, description, + maySendDestructively, numberOfBuffers).second; + case deviceToControlSystem: + return _pvManager->createProcessArrayDeviceToControlSystem( + processVariableName, std::vector(size, initialValue), unit, description, + maySendDestructively, numberOfBuffers).second; + default: + throw std::invalid_argument("invalid SynchronizationDirection"); } } @@ -248,16 +248,16 @@ namespace ChimeraTK { const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { - case controlSystemToDevice: - return _pvManager->createProcessArrayControlSystemToDevice( - processVariableName, initialValue, unit, description, maySendDestructively, - numberOfBuffers).second; - case deviceToControlSystem: - return _pvManager->createProcessArrayDeviceToControlSystem( - processVariableName, initialValue, unit, description, maySendDestructively, - numberOfBuffers).second; - default: - throw std::invalid_argument("invalid SynchronizationDirection"); + case controlSystemToDevice: + return _pvManager->createProcessArrayControlSystemToDevice( + processVariableName, initialValue, unit, description, maySendDestructively, + numberOfBuffers).second; + case deviceToControlSystem: + return _pvManager->createProcessArrayDeviceToControlSystem( + processVariableName, initialValue, unit, description, maySendDestructively, + numberOfBuffers).second; + default: + throw std::invalid_argument("invalid SynchronizationDirection"); } } From 13608a20cbba9eb335c1f5107fcc2a608f040536 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 18 May 2017 15:26:22 +0200 Subject: [PATCH 183/786] made the what() string of the bad_cast exception thrown in PVManager::getProcessArray() more elaborate (now includes the name of the PV and the two mismatching types) --- include/ChimeraTK/ControlSystemAdapter/PVManager.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index c023fa2..bfef0dd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -516,7 +516,16 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr devPV = boost::dynamic_pointer_cast< ProcessArray, ProcessVariable>(processVariable.second); if (!csPV || !devPV) { - throw std::bad_cast(); + struct bad_cast : std::bad_cast { + bad_cast(const std::string &what) : _what(what) {} + const char* what() const noexcept override { + return _what.c_str(); + } + std::string _what; + }; + throw bad_cast("PVManager::getProcessArray() called for variable '" + processVariableName +"' with type " + +typeid(T).name()+" which is not the original type " + processVariable.first->getValueType().name() + + " of this process variable."); } return std::make_pair(csPV, devPV); } else { From 616a6cadcb3521a925b07a7b5f0c32961e631107 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 19 May 2017 11:47:14 +0200 Subject: [PATCH 184/786] change an assertion into a conditionally thrown exception, so it can be caught --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 675a4ef..0783980 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -456,8 +456,16 @@ namespace ChimeraTK { if(_asyncReadTransferNeeded) { _asyncReadTransferNeeded = false; bool hasNewDataAfterFuture = doReadTransferNonBlocking(); - assert(hasNewDataAfterFuture); // if this is not true, we did not receive new data despied being notified about new data... - (void)hasNewDataAfterFuture; // prevent warning in case asserts are disabled + if(!hasNewDataAfterFuture) { + // we did not receive new data despite being notified about new data... + // this either means there is a bug in the ControlSystemAdapter, or the application has called postRead() from + // a different thread while mtca4u::TransferFuture::wait() is still waiting. This is in principle violating + // the interface definition, but can be safe under certain circumstances (e.g. in ApplicationCore's testable + // mode, when a stall is detected and debugging information should be printed). Thus we do not raise an + // assertion but throw an exception which can be caught when this is known to be safe. + throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " + "ProcessArray from different threads?"); + } } // swap data out of the queue buffer mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); From 6640565666ccb6ca24233205420ad4cce37f1951 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 22 May 2017 12:45:57 +0200 Subject: [PATCH 185/786] make sure to use already obtained future to avoid running the notification queue empty --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 0783980..be552e9 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -360,6 +360,9 @@ namespace ChimeraTK { } void doReadTransfer() override { + // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is + // already fulfilled. Otherwise we might run out of futures on the notification queue. + if(mtca4u::TransferElement::hasActiveFuture) mtca4u::TransferElement::activeFuture.wait(); // Obtain futures from the notification queue and wait on them until we receive data. We start with checking // for data before obtaining a future from the notification queue, since this is faster and the notification // queue is shorter than the data queue. From 67d02b97af0db91122100081510c167916a5c417 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 2 Jun 2017 17:46:19 +0200 Subject: [PATCH 186/786] make use of the new TransferFuture::hasNewData() function to also allow mixing readAny()/readAsync() and readNonBlocking()/readLatest() --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index be552e9..ae02855 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -380,6 +380,11 @@ namespace ChimeraTK { if (_instanceType != RECEIVER) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } + // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is + // already fulfilled. Otherwise we might run out of futures on the notification queue. + if(mtca4u::TransferElement::hasActiveFuture) { + return mtca4u::TransferElement::activeFuture.hasNewData(); + } // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. @@ -467,7 +472,7 @@ namespace ChimeraTK { // mode, when a stall is detected and debugging information should be printed). Thus we do not raise an // assertion but throw an exception which can be caught when this is known to be safe. throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " - "ProcessArray from different threads?"); + "ProcessArray from different threads? Variable name: "+this->getName()); } } // swap data out of the queue buffer From 330b15908150badaaabd5e35f2e7cd431042392f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 6 Jun 2017 08:54:46 +0200 Subject: [PATCH 187/786] Fixed readAsync() no longer functioning after recent change of doReadTransferNonBlocking(), since postRead() uses that function. (Tests were catching this problem) --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index ae02855..ae992da 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -463,6 +463,7 @@ namespace ChimeraTK { // if this is called inside mtca4u::TransferFuture::wait(), we may still need to obtain the new data from the queue if(_asyncReadTransferNeeded) { _asyncReadTransferNeeded = false; + mtca4u::TransferElement::hasActiveFuture = false; // prevent doReadTransferNonBlocking() from checking the future only bool hasNewDataAfterFuture = doReadTransferNonBlocking(); if(!hasNewDataAfterFuture) { // we did not receive new data despite being notified about new data... From 753cfda523070c8f935aa109609513f4323dd98f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 6 Jun 2017 08:55:51 +0200 Subject: [PATCH 188/786] extended the test a bit to distinguish two possible failure modes --- tests/src/testAsyncRead.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index 59509b1..b121b54 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -67,6 +67,12 @@ void AsyncReadTest::testAsyncRead() { future = &(accessor.readAsync()); future->wait(); BOOST_CHECK( accessor == 5 ); + + senderAccessor = 6; + senderAccessor.write(); + future = &(accessor.readAsync()); + future->wait(); + BOOST_CHECK( accessor == 6 ); // check that future's wait() function won't return before the read is complete for(int i=0; i<5; ++i) { From f995d258c53adbc7242e2afc3af4cf3fb0c8f74c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 7 Jun 2017 13:37:17 +0200 Subject: [PATCH 189/786] fixed incorrect handling of still-active futures in read() and readNonBlocking() (e.g. after a readAny()) --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index ae992da..fbabd5d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -362,7 +362,12 @@ namespace ChimeraTK { void doReadTransfer() override { // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is // already fulfilled. Otherwise we might run out of futures on the notification queue. - if(mtca4u::TransferElement::hasActiveFuture) mtca4u::TransferElement::activeFuture.wait(); + if(mtca4u::TransferElement::hasActiveFuture) { + mtca4u::TransferElement::activeFuture.getBoostFuture().wait(); + mtca4u::TransferElement::hasActiveFuture = false; + if(!_asyncReadTransferNeeded) return; + _asyncReadTransferNeeded = false; + } // Obtain futures from the notification queue and wait on them until we receive data. We start with checking // for data before obtaining a future from the notification queue, since this is faster and the notification // queue is shorter than the data queue. @@ -383,7 +388,12 @@ namespace ChimeraTK { // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is // already fulfilled. Otherwise we might run out of futures on the notification queue. if(mtca4u::TransferElement::hasActiveFuture) { - return mtca4u::TransferElement::activeFuture.hasNewData(); + auto theFuture = mtca4u::TransferElement::activeFuture.getBoostFuture(); + auto status = theFuture.wait_for(boost::chrono::duration(0)); + if(status == boost::future_status::timeout) return false; + mtca4u::TransferElement::hasActiveFuture = false; + if(!_asyncReadTransferNeeded) return true; + _asyncReadTransferNeeded = false; } // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into From 404602f13200df2344b138aa53c1a3eac3941172 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 7 Jun 2017 13:38:07 +0200 Subject: [PATCH 190/786] added test cases for mixing readAsync() and read()/readNonBlocking() --- tests/src/testAsyncRead.cpp | 171 ++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index b121b54..c1c3fb8 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -25,9 +25,13 @@ class AsyncReadTest { /// test the TransferElement::readAny() function on ProcessArrays void testReadAny(); + /// test mixing asynchronous with synchronous reads + void testMixedRead(); + }; /**********************************************************************************************************************/ + class AsyncReadTestSuite : public test_suite { public: AsyncReadTestSuite() : test_suite("Async read test suite") { @@ -35,9 +39,11 @@ class AsyncReadTestSuite : public test_suite { add( BOOST_CLASS_TEST_CASE( &AsyncReadTest::testAsyncRead, asyncReadTest ) ); add( BOOST_CLASS_TEST_CASE( &AsyncReadTest::testReadAny, asyncReadTest ) ); + add( BOOST_CLASS_TEST_CASE( &AsyncReadTest::testMixedRead, asyncReadTest ) ); }}; /**********************************************************************************************************************/ + test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/ [] ) { ChimeraTK::ExperimentalFeatures::enable(); @@ -49,6 +55,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/ [] ) } /**********************************************************************************************************************/ + void AsyncReadTest::testAsyncRead() { std::cout << "testAsyncRead" << std::endl; @@ -258,3 +265,167 @@ void AsyncReadTest::testReadAny() { } } + +/**********************************************************************************************************************/ + +void AsyncReadTest::testMixedRead() { + std::cout << "testMixedRead" << std::endl; + + auto senderReceiver = createSynchronizedProcessArray(1, "someName"); + auto sender = senderReceiver.first; + auto receiver = senderReceiver.second; + + // obtain register accessor with integral type + auto accessor = mtca4u::ScalarRegisterAccessor(receiver); + auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); + + TransferFuture *future; + + // plain readAsync and read mixed one after each other + senderAccessor = 5; + senderAccessor.write(); + future = &(accessor.readAsync()); + future->wait(); + BOOST_CHECK( accessor == 5 ); + + senderAccessor = 6; + senderAccessor.write(); + accessor.read(); + BOOST_CHECK( accessor == 6 ); + + senderAccessor = 7; + senderAccessor.write(); + future = &(accessor.readAsync()); + future->wait(); + BOOST_CHECK( accessor == 7 ); + + senderAccessor = 8; + senderAccessor.write(); + accessor.read(); + BOOST_CHECK( accessor == 8 ); + + future = &(accessor.readAsync()); + senderAccessor = 9; + senderAccessor.write(); + future->wait(); + BOOST_CHECK( accessor == 9 ); + + senderAccessor = 10; + senderAccessor.write(); + accessor.read(); + BOOST_CHECK( accessor == 10 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // mixing with read() when future stays unfulfilled + future = &(accessor.readAsync()); + usleep(10000); + BOOST_CHECK(future->hasNewData() == false); + + senderAccessor = 11; + senderAccessor.write(); + usleep(10000); + + accessor.read(); + BOOST_CHECK( accessor == 11 ); + senderAccessor = 12; + senderAccessor.write(); + accessor.read(); + BOOST_CHECK( accessor == 12 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // mixing with read() when future stays unfulfilled, different order + future = &(accessor.readAsync()); + usleep(10000); + BOOST_CHECK(future->hasNewData() == false); + + senderAccessor = 13; + senderAccessor.write(); + senderAccessor = 14; + senderAccessor.write(); + usleep(10000); + + accessor.read(); + BOOST_CHECK( accessor == 13 ); + + accessor.read(); + BOOST_CHECK( accessor == 14 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // readAsync with data already present, then discard the future + senderAccessor = 15; + senderAccessor.write(); + future = &(accessor.readAsync()); + usleep(10000); + accessor.read(); + BOOST_CHECK( accessor == 15 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // readAsync with data already present, then discard the future, with more data + senderAccessor = 15; + senderAccessor.write(); + senderAccessor = 16; + senderAccessor.write(); + future = &(accessor.readAsync()); + usleep(10000); + accessor.read(); + BOOST_CHECK( accessor == 15 ); + accessor.read(); + BOOST_CHECK( accessor == 16 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // mixing with readNonBlocking() when future stays unfulfilled + future = &(accessor.readAsync()); + usleep(10000); + BOOST_CHECK(future->hasNewData() == false); + + senderAccessor = 13; + senderAccessor.write(); + senderAccessor = 14; + senderAccessor.write(); + usleep(10000); + + BOOST_CHECK( accessor.readNonBlocking() == true ); + BOOST_CHECK( accessor == 13 ); + + BOOST_CHECK( accessor.readNonBlocking() == true ); + BOOST_CHECK( accessor == 14 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // mixing with readNonBlocking() when future stays unfulfilled and a readNonBlocking() also is unsuccessfull + future = &(accessor.readAsync()); + usleep(10000); + BOOST_CHECK(future->hasNewData() == false); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + senderAccessor = 13; + senderAccessor.write(); + senderAccessor = 14; + senderAccessor.write(); + usleep(10000); + + BOOST_CHECK( accessor.readNonBlocking() == true ); + BOOST_CHECK( accessor == 13 ); + + BOOST_CHECK( accessor.readNonBlocking() == true ); + BOOST_CHECK( accessor == 14 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + + // readAsync with data already present, then discard the future - readNonBlocking() version + senderAccessor = 15; + senderAccessor.write(); + future = &(accessor.readAsync()); + usleep(10000); + BOOST_CHECK( accessor.readNonBlocking() == true ); + BOOST_CHECK( accessor == 15 ); + + BOOST_CHECK( accessor.readNonBlocking() == false ); + +} From 3ea7c663edd4e4e9da18538a8654a3a07e370c33 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 8 Jun 2017 12:58:20 +0200 Subject: [PATCH 191/786] temporarily changed the exception thrown in the blocking postRead() if no new data is there when expected. --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index fbabd5d..d00552c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -482,8 +482,16 @@ namespace ChimeraTK { // the interface definition, but can be safe under certain circumstances (e.g. in ApplicationCore's testable // mode, when a stall is detected and debugging information should be printed). Thus we do not raise an // assertion but throw an exception which can be caught when this is known to be safe. - throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " - "ProcessArray from different threads? Variable name: "+this->getName()); + + /// @todo Change this back to an exception, this busy-waiting loop is only a temporary work around until the + /// problem has been fully understood and solved. + /* throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " + "ProcessArray from different threads? Variable name: "+this->getName()); */ + std::cerr << "*** ERROR: ProcessArrya::postRead() called despite no new data has arrived. Are you reading the " + "same ProcessArray from different threads? Variable name: "+this->getName() << std::endl; + std::cerr << " Using 1ms polling interval to wait for actual new data." << std::endl; + while(!doReadTransferNonBlocking()) usleep(1000); + std::cerr << " New data has arrived for " << this->getName() << ". Busy waiting stopped." << std::endl; } } // swap data out of the queue buffer From 3eee7afc658c48f76cf45b69e60c4122c917eb4a Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 9 Jun 2017 09:24:13 +0200 Subject: [PATCH 192/786] increased minor version and updated required version of DeviceAccess --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 428ef8e..3dda057 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 04) +set(${PROJECT_NAME}_MINOR_VERSION 05) set(${PROJECT_NAME}_PATCH_VERSION 00) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) @@ -45,7 +45,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.23 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.24 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) From 4b11fd33ddc13c6061d67bf0fde9d092465b116a Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 12 Jun 2017 10:55:06 +0200 Subject: [PATCH 193/786] renamed IntependentTestCore to ReferenceTestApplication --- CMakeLists.txt | 6 ++-- ...tTestCore.h => ReferenceTestApplication.h} | 24 +++++++------- tests/src/fullStubTest.cpp | 32 +++++++++---------- tests/src/testReferenceTestCoreWithTread.cpp | 12 +++---- 4 files changed, 37 insertions(+), 37 deletions(-) rename tests/include/ChimeraTK/ControlSystemAdapter/Testing/{IndependentTestCore.h => ReferenceTestApplication.h} (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 428ef8e..51eaa0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ if(TESTING_IS_ENABLED) get_filename_component(executableName ${testSourceFile} NAME_WE) add_executable(${executableName} ${testSourceFile} ${testAuxSources}) target_include_directories(${executableName} PRIVATE ${CMAKE_SOURCE_DIR}/tests/include/ChimeraTK/ControlSystemAdapter/Testing) - #The IndependentTestCore is written to work with the control system adapter as installed, so + #The ReferenceTestApplication is written to work with the control system adapter as installed, so #we add that path as system just for the tests. target_include_directories(${executableName} SYSTEM BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/include) target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} pthread) @@ -113,10 +113,10 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include PATTERN "ChimeraTK/*.h" PATTERN "ChimeraTK/ControlSystemAdapter/*.h") -#We additionally install the independent reference test application, it's header only +#We additionally install the reference test application, it's header only install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include FILES_MATCHING - PATTERN "ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h") + PATTERN "ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h") #Create the config files by filling the correct variables into the template (*.cmake.in). #All variables @VARIABLE@ are replaced with the current values, but they have to be set first.... diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h similarity index 92% rename from tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h rename to tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h index ca59afe..f8eb2a0 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h @@ -1,5 +1,5 @@ -#ifndef _INDEPENDENT_CONTOL_CORE_H_ -#define _INDEPENDENT_CONTOL_CORE_H_ +#ifndef _REFERENCE_TEST_APPLICATION_H_ +#define _REFERENCE_TEST_APPLICATION_H_ #include @@ -87,7 +87,7 @@ typedef boost::fusion::map< > HolderMap; -class IndependentTestCore{ +class ReferenceTestApplication{ public: ChimeraTK::DevicePVManager::SharedPtr processVariableManager; @@ -134,7 +134,7 @@ class IndependentTestCore{ * If needed for the test, a thread can be started which automatically executes the 'mainBody()' function in * an endless loop. */ - IndependentTestCore(boost::shared_ptr const & processVariableManager_) + ReferenceTestApplication(boost::shared_ptr const & processVariableManager_) //initialise all process variables, using the factory : processVariableManager( processVariableManager_ ), holderMap( @@ -151,10 +151,10 @@ class IndependentTestCore{ syncUtil.sendAll(); - _deviceThread.reset( new boost::thread( boost::bind( &IndependentTestCore::mainLoop, this ) ) ); + _deviceThread.reset( new boost::thread( boost::bind( &ReferenceTestApplication::mainLoop, this ) ) ); } - ~IndependentTestCore(){ + ~ReferenceTestApplication(){ // stop the device thread before any other destructors are called if (_deviceThread){ _deviceThread->interrupt(); @@ -164,7 +164,7 @@ class IndependentTestCore{ }; -inline void IndependentTestCore::mainLoop(){ +inline void ReferenceTestApplication::mainLoop(){ mainLoopMutex().lock(); while (!boost::this_thread::interruption_requested()) { @@ -194,13 +194,13 @@ struct PerformInputToOutput{ } }; -inline void IndependentTestCore::mainBody(){ +inline void ReferenceTestApplication::mainBody(){ syncUtil.receiveAll(); for_each( holderMap, PerformInputToOutput() ); } -inline void IndependentTestCore::runMainLoopOnce(){ +inline void ReferenceTestApplication::runMainLoopOnce(){ mainLoopExecutionRequested() = true; do{ mainLoopMutex().unlock(); @@ -211,7 +211,7 @@ inline void IndependentTestCore::runMainLoopOnce(){ }while( mainLoopExecutionRequested() ); } -inline void IndependentTestCore::initialiseManualLoopControl(){ +inline void ReferenceTestApplication::initialiseManualLoopControl(){ manuallyControlMainLoop() = true; do{ boost::this_thread::sleep_for( boost::chrono::milliseconds(10) ); @@ -219,9 +219,9 @@ inline void IndependentTestCore::initialiseManualLoopControl(){ mainLoopMutex().lock(); } -inline void IndependentTestCore::releaseManualLoopControl(){ +inline void ReferenceTestApplication::releaseManualLoopControl(){ manuallyControlMainLoop() = false; initalisationForManualLoopControlFinished() = false; mainLoopMutex().unlock(); } -#endif // _INDEPENDENT_CONTOL_CORE_H_ +#endif // _REFERENCE_TEST_APPLICATION_H_ diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index e2ecfd2..bddd43c 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -3,34 +3,34 @@ #include #include "ControlSystemSynchronizationUtility.h" -#include "IndependentTestCore.h" +#include "ReferenceTestApplication.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; -struct TestCoreFixture{ +struct TestApplicationFixture{ std::pair, boost::shared_ptr > pvManagers; boost::shared_ptr csManager; boost::shared_ptr devManager; - IndependentTestCore testCore; + ReferenceTestApplication testApplication; ControlSystemSynchronizationUtility csSyncUtil; - TestCoreFixture() : + TestApplicationFixture() : pvManagers( createPVManager() ), csManager( pvManagers.first ), devManager( pvManagers.second ), - testCore(devManager), + testApplication(devManager), csSyncUtil(csManager){ - std::cout << "this is TestCoreFixture():" << std::endl; - IndependentTestCore::initialiseManualLoopControl(); + std::cout << "this is TestApplicationFixture():" << std::endl; + ReferenceTestApplication::initialiseManualLoopControl(); csSyncUtil.receiveAll(); } - ~TestCoreFixture(){ - std::cout << "this is ~TestCoreFixture():" << std::endl; - IndependentTestCore::releaseManualLoopControl(); + ~TestApplicationFixture(){ + std::cout << "this is ~TestApplicationFixture():" << std::endl; + ReferenceTestApplication::releaseManualLoopControl(); } template @@ -43,7 +43,7 @@ struct TestCoreFixture{ toDeviceScalar->accessData(0) = previousReadValue+13; csSyncUtil.sendAll(); - IndependentTestCore::runMainLoopOnce(); + ReferenceTestApplication::runMainLoopOnce(); csSyncUtil.receiveAll(); BOOST_CHECK( fromDeviceScalar->accessData(0) == previousReadValue+13 ); @@ -82,7 +82,7 @@ struct TestCoreFixture{ } csSyncUtil.sendAll(); - IndependentTestCore::runMainLoopOnce(); + ReferenceTestApplication::runMainLoopOnce(); csSyncUtil.receiveAll(); for (size_t i = 0; i < fromDeviceArray->get().size(); ++i){ @@ -94,7 +94,7 @@ struct TestCoreFixture{ BOOST_AUTO_TEST_SUITE( FullStubTestSuite ) -BOOST_FIXTURE_TEST_CASE( test_read_scalar, TestCoreFixture){ +BOOST_FIXTURE_TEST_CASE( test_read_scalar, TestApplicationFixture){ // just after creation of the fixture the constants should be available to the control system BOOST_CHECK( csManager->getProcessArray("CHAR/DATA_TYPE_CONSTANT")->get()[0] == -1 ); BOOST_CHECK( csManager->getProcessArray("UCHAR/DATA_TYPE_CONSTANT")->get()[0] == 1 ); @@ -106,7 +106,7 @@ BOOST_FIXTURE_TEST_CASE( test_read_scalar, TestCoreFixture){ BOOST_CHECK( csManager->getProcessArray("DOUBLE/DATA_TYPE_CONSTANT")->get()[0] == 1./8 ); } -BOOST_FIXTURE_TEST_CASE( test_write_scalar, TestCoreFixture){ +BOOST_FIXTURE_TEST_CASE( test_write_scalar, TestApplicationFixture){ typedWriteScalarTest("CHAR"); typedWriteScalarTest("UCHAR"); typedWriteScalarTest("SHORT"); @@ -117,7 +117,7 @@ BOOST_FIXTURE_TEST_CASE( test_write_scalar, TestCoreFixture){ typedWriteScalarTest("DOUBLE"); } -BOOST_FIXTURE_TEST_CASE( test_read_array, TestCoreFixture){ +BOOST_FIXTURE_TEST_CASE( test_read_array, TestApplicationFixture){ typedReadArrayTest("CHAR"); typedReadArrayTest("UCHAR"); typedReadArrayTest("SHORT"); @@ -128,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE( test_read_array, TestCoreFixture){ typedReadArrayTest("DOUBLE"); } -BOOST_FIXTURE_TEST_CASE( test_write_array, TestCoreFixture){ +BOOST_FIXTURE_TEST_CASE( test_write_array, TestApplicationFixture){ typedWriteArrayTest("CHAR"); typedWriteArrayTest("UCHAR"); typedWriteArrayTest("SHORT"); diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index 8cb609f..6ebbdc7 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -1,28 +1,28 @@ -#define BOOST_TEST_MODULE ReferenceTestCoreWithThreadTest +#define BOOST_TEST_MODULE ReferenceTestApplicationWithThreadTest // Only after defining the name include the unit test header. #include #include "ControlSystemSynchronizationUtility.h" -#include "IndependentTestCore.h" +#include "ReferenceTestApplication.h" using namespace boost::unit_test_framework; using namespace ChimeraTK; -// We are testing the reference core when executed with it's own thread running. +// We are testing the reference application when executed with it's own thread running. // Synchronisation is done through the atomic mainBodyCompletelyExecuted variable. // This test is only executed for int32_t scalars because it is only testing the thread // functionality. The rest is done in the {FIXEM: find a better name ) fullStubTest -BOOST_AUTO_TEST_SUITE( ReferenceTestCoreWithThreadTestSuite ) +BOOST_AUTO_TEST_SUITE( ReferenceTestApplicationWithThreadTestSuite ) BOOST_AUTO_TEST_CASE( testInt32_t ){ auto pvManagers = createPVManager(); auto csManager = pvManagers.first; auto devManager = pvManagers.second; - // create the control core with it's own thread running the main loop - IndependentTestCore testCore( devManager ); + // create the control application with it's own thread running the main loop + ReferenceTestApplication testApplication( devManager ); ControlSystemSynchronizationUtility csSyncUtil(csManager); From 99f21f8f3f926de6dfccbe5c15085a2d2d155b98 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 12 Jun 2017 13:56:32 +0200 Subject: [PATCH 194/786] made internal members protected in ReferenceTestApplication to make clear what is the intended public interface. --- .../Testing/ReferenceTestApplication.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h index f8eb2a0..7b3e031 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -87,8 +88,8 @@ typedef boost::fusion::map< > HolderMap; -class ReferenceTestApplication{ - public: +class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ + protected: ChimeraTK::DevicePVManager::SharedPtr processVariableManager; boost::scoped_ptr< boost::thread > _deviceThread; @@ -118,15 +119,18 @@ class ReferenceTestApplication{ return _initalisationForManualLoopControlFinished; } + public: static void initialiseManualLoopControl(); static void releaseManualLoopControl(); + protected: /// An infinite while loop, running mainBody() void mainLoop(); /// The 'body' of the main loop, i.e. the functionality once, without the loop around it. void mainBody(); + public: static void runMainLoopOnce(); /** The constructor gets an instance of the variable factory to use. From 5ffad456c999162834a23d391af2bec0e826b082 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 12 Jun 2017 14:13:45 +0200 Subject: [PATCH 195/786] some code cosmetics --- .../Testing/ReferenceTestApplication.h | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h index 7b3e031..3448d22 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h @@ -89,6 +89,25 @@ typedef boost::fusion::map< class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ + public: + // Sets the application into testing mode: The main control loop will stop at the + // beginning, before executing mainBody. + static void initialiseManualLoopControl(); + // Goes out of testing mode. The endless main loop starts again. + static void releaseManualLoopControl(); + // When in testing mode, this runs the main loop exactly one. This is needed for testing. + // It guarantees that the main body has completed execution and has been run exactly one. + static void runMainLoopOnce(); + + /** The constructor gets an instance of the variable factory to use. + * The variables in the factory should already be initialised because the hardware is initialised here. + * If needed for the test, a thread can be started which automatically executes the 'mainBody()' function in + * an endless loop. + */ + ReferenceTestApplication(boost::shared_ptr const & processVariableManager_); + + ~ReferenceTestApplication(); + protected: ChimeraTK::DevicePVManager::SharedPtr processVariableManager; @@ -119,28 +138,17 @@ class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ return _initalisationForManualLoopControlFinished; } - public: - static void initialiseManualLoopControl(); - static void releaseManualLoopControl(); - - protected: /// An infinite while loop, running mainBody() void mainLoop(); /// The 'body' of the main loop, i.e. the functionality once, without the loop around it. void mainBody(); - public: - static void runMainLoopOnce(); +}; - /** The constructor gets an instance of the variable factory to use. - * The variables in the factory should already be initialised because the hardware is initialised here. - * If needed for the test, a thread can be started which automatically executes the 'mainBody()' function in - * an endless loop. - */ - ReferenceTestApplication(boost::shared_ptr const & processVariableManager_) - //initialise all process variables, using the factory - : processVariableManager( processVariableManager_ ), +inline ReferenceTestApplication::ReferenceTestApplication(boost::shared_ptr const & processVariableManager_) + //initialise all process variables, using the factory + : processVariableManager( processVariableManager_ ), holderMap( boost::fusion::make_pair( TypedPVHolder( processVariableManager, "CHAR") ), boost::fusion::make_pair( TypedPVHolder( processVariableManager, "UCHAR") ), @@ -152,21 +160,18 @@ class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ boost::fusion::make_pair( TypedPVHolder( processVariableManager, "DOUBLE") ) ), syncUtil(processVariableManager){ - - syncUtil.sendAll(); - - _deviceThread.reset( new boost::thread( boost::bind( &ReferenceTestApplication::mainLoop, this ) ) ); - } + syncUtil.sendAll(); + _deviceThread.reset( new boost::thread( boost::bind( &ReferenceTestApplication::mainLoop, this ) ) ); +} - ~ReferenceTestApplication(){ - // stop the device thread before any other destructors are called - if (_deviceThread){ - _deviceThread->interrupt(); - _deviceThread->join(); - } - } +inline ReferenceTestApplication::~ReferenceTestApplication(){ + // stop the device thread before any other destructors are called + if (_deviceThread){ + _deviceThread->interrupt(); + _deviceThread->join(); + } +} -}; inline void ReferenceTestApplication::mainLoop(){ mainLoopMutex().lock(); From 21346f555cd5315c2d7fef854e922655392f1fac Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 13 Jun 2017 09:06:57 +0200 Subject: [PATCH 196/786] changed ReferenceTestApplication to derrive from ApplicationBase --- .../Testing/ReferenceTestApplication.h | 65 +++++++++++-------- tests/src/fullStubTest.cpp | 10 +-- tests/src/testReferenceTestCoreWithTread.cpp | 5 +- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h index 3448d22..4132c31 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h @@ -88,7 +88,7 @@ typedef boost::fusion::map< > HolderMap; -class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ +class ReferenceTestApplication : public ChimeraTK::ApplicationBase{ public: // Sets the application into testing mode: The main control loop will stop at the // beginning, before executing mainBody. @@ -99,23 +99,22 @@ class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ // It guarantees that the main body has completed execution and has been run exactly one. static void runMainLoopOnce(); - /** The constructor gets an instance of the variable factory to use. - * The variables in the factory should already be initialised because the hardware is initialised here. - * If needed for the test, a thread can be started which automatically executes the 'mainBody()' function in - * an endless loop. - */ - ReferenceTestApplication(boost::shared_ptr const & processVariableManager_); - + ReferenceTestApplication(); ~ReferenceTestApplication(); + /// Inherited from ApplicationBase + void initialise() override; + /// Inherited from ApplicationBase + void run() override; + protected: - ChimeraTK::DevicePVManager::SharedPtr processVariableManager; + // ChimeraTK::DevicePVManager::SharedPtr processVariableManager; boost::scoped_ptr< boost::thread > _deviceThread; - HolderMap holderMap; + boost::scoped_ptr _holderMap; // the syncUtil needs to be initalised after the PVs are added to the manager - ChimeraTK::DeviceSynchronizationUtility syncUtil; + boost::scoped_ptr syncUtil; static std::mutex & mainLoopMutex(){ static std::mutex _mainLoopMutex; @@ -146,22 +145,29 @@ class ReferenceTestApplication{// : public ChimeraTK::ApplicationBase{ }; -inline ReferenceTestApplication::ReferenceTestApplication(boost::shared_ptr const & processVariableManager_) +inline ReferenceTestApplication::ReferenceTestApplication() //initialise all process variables, using the factory - : processVariableManager( processVariableManager_ ), - holderMap( - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "CHAR") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "UCHAR") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "SHORT") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "USHORT") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "INT") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "UINT") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "FLOAT") ), - boost::fusion::make_pair( TypedPVHolder( processVariableManager, "DOUBLE") ) - ), - syncUtil(processVariableManager){ - syncUtil.sendAll(); - _deviceThread.reset( new boost::thread( boost::bind( &ReferenceTestApplication::mainLoop, this ) ) ); + : ApplicationBase("ReferenceTest"){ +} + +inline void ReferenceTestApplication::initialise(){ + // fixme : if ! processVariableManager_ throw + _holderMap.reset( new HolderMap( + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "CHAR") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "UCHAR") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "SHORT") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "USHORT") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "INT") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "UINT") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "FLOAT") ), + boost::fusion::make_pair( TypedPVHolder( _processVariableManager, "DOUBLE") ) + )); + syncUtil.reset(new ChimeraTK::DeviceSynchronizationUtility(_processVariableManager)); + syncUtil->sendAll(); +} + +inline void ReferenceTestApplication::run(){ + _deviceThread.reset( new boost::thread( boost::bind( &ReferenceTestApplication::mainLoop, this ) ) ); } inline ReferenceTestApplication::~ReferenceTestApplication(){ @@ -170,6 +176,8 @@ inline ReferenceTestApplication::~ReferenceTestApplication(){ _deviceThread->interrupt(); _deviceThread->join(); } + // we need to call shutdown from ApplicationBase. FIXME: why do we have to do this manually? + shutdown(); } @@ -205,8 +213,8 @@ struct PerformInputToOutput{ inline void ReferenceTestApplication::mainBody(){ - syncUtil.receiveAll(); - for_each( holderMap, PerformInputToOutput() ); + syncUtil->receiveAll(); + for_each( *_holderMap, PerformInputToOutput() ); } inline void ReferenceTestApplication::runMainLoopOnce(){ @@ -233,4 +241,5 @@ inline void ReferenceTestApplication::releaseManualLoopControl(){ initalisationForManualLoopControlFinished() = false; mainLoopMutex().unlock(); } + #endif // _REFERENCE_TEST_APPLICATION_H_ diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index bddd43c..a955ae6 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -22,11 +22,13 @@ struct TestApplicationFixture{ pvManagers( createPVManager() ), csManager( pvManagers.first ), devManager( pvManagers.second ), - testApplication(devManager), csSyncUtil(csManager){ - std::cout << "this is TestApplicationFixture():" << std::endl; - ReferenceTestApplication::initialiseManualLoopControl(); - csSyncUtil.receiveAll(); + std::cout << "this is TestApplicationFixture():" << std::endl; + testApplication.setPVManager(devManager); + testApplication.initialise(); + testApplication.run(); + ReferenceTestApplication::initialiseManualLoopControl(); + csSyncUtil.receiveAll(); } ~TestApplicationFixture(){ std::cout << "this is ~TestApplicationFixture():" << std::endl; diff --git a/tests/src/testReferenceTestCoreWithTread.cpp b/tests/src/testReferenceTestCoreWithTread.cpp index 6ebbdc7..41181bc 100644 --- a/tests/src/testReferenceTestCoreWithTread.cpp +++ b/tests/src/testReferenceTestCoreWithTread.cpp @@ -22,7 +22,10 @@ BOOST_AUTO_TEST_CASE( testInt32_t ){ auto devManager = pvManagers.second; // create the control application with it's own thread running the main loop - ReferenceTestApplication testApplication( devManager ); + ReferenceTestApplication testApplication; + testApplication.setPVManager( devManager ); + testApplication.initialise(); + testApplication.run(); ControlSystemSynchronizationUtility csSyncUtil(csManager); From 23a87d53c4113299711bbbf3a2cf0f259fb1fe91 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 11 Jul 2017 17:52:03 +0200 Subject: [PATCH 197/786] use set_version_numbers.cmake script to take into account the build version in the so version --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dda057..14b6304 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 05) set(${PROJECT_NAME}_PATCH_VERSION 00) -set(${PROJECT_NAME}_VERSION - ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) -set(${PROJECT_NAME}_SOVERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}) +include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow #-Weffc++ -ansi -pedantic -Wuninitialized -std=c++11") From b2528508767317037a2962f68090b3577d29adee Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 11 Jul 2017 17:52:33 +0200 Subject: [PATCH 198/786] increased patch level --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14b6304..9e37b77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 05) -set(${PROJECT_NAME}_PATCH_VERSION 00) +set(${PROJECT_NAME}_PATCH_VERSION 01) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From a0b8e1dd17bd7cfbac7cfb824c164c2900416915 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 11 Jul 2017 18:01:40 +0200 Subject: [PATCH 199/786] use the full library version for the so version, which includes also the build version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e37b77..d909f1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ if(TESTING_IS_ENABLED) endif(TESTING_IS_ENABLED) add_library(${PROJECT_NAME} SHARED ${library_sources} ) -set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION} SOVERSION ${${PROJECT_NAME}_SOVERSION}) +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_FULL_LIBRARY_VERSION} SOVERSION ${${PROJECT_NAME}_SOVERSION}) target_link_libraries(${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} ${LibXML++_LIBRARIES} ${glib_LIBRARIES}) #change the install prefix to the source directory in case the user has not specified a destination From 7331639db71a55b5e79d82c44a11c14163bf4daa Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 14 Jul 2017 10:18:51 +0200 Subject: [PATCH 200/786] reduced the number of transfers tested in a loop to the queue length, since otherwise there might be a loss of data occurring a as arace condition. --- tests/src/testProcessArray.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 7250ee8..8acfe41 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -460,15 +460,15 @@ namespace ChimeraTK { BOOST_CHECK_EQUAL(receiver->accessData(0), 43); } - // same with multiple transfers + // same with multiple transfers (not more than the queue length to avoid race conditions) { std::thread backgroundTask( [&receiver] { - for(int i=0; i<10; ++i) { + for(int i=0; i<2; ++i) { receiver->read(); BOOST_CHECK_EQUAL(static_cast(receiver->accessData(0)), 2 + i); } }); - for(int i=0; i<10; ++i) { + for(int i=0; i<2; ++i) { usleep(10000); sender->accessData(0) = 2+i; sender->write(); From d8485631749c886a5297536d119b169ee45057ae Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 11:50:34 +0200 Subject: [PATCH 201/786] removed the stand-alone version of the ProcessArray, since it is not needed and shouldn't be used even for tests (behaviour very different from the real version) --- .../ControlSystemAdapter/ProcessArray.h | 76 +-------- tests/src/testProcessArray.cpp | 150 ++++-------------- 2 files changed, 35 insertions(+), 191 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index d00552c..7932b48 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -25,7 +25,7 @@ namespace ChimeraTK { /** * Array implementation of the ProcessVariable. This implementation is used - * for all three use cases (sender, receiver, and stand-alone). + * for both use cases (sender and receiver). * * This class is not thread-safe and should only be used from a single thread. * @@ -53,10 +53,6 @@ namespace ChimeraTK { * can be used. */ enum InstanceType { - /** - * Instance acts on is own. - */ - STAND_ALONE, /** * Instance acts as the sender in a sender / receiver pair. @@ -69,34 +65,6 @@ namespace ChimeraTK { RECEIVER }; - /** - * Creates a process array that works independently. This means that the - * instance is not synchronized with any other instance and thus the send - * and receive operations are not supported. However, all other operations - * can be used like on any process variable. - * - * TODO @todo Is this really a wanted feature? I see no benefit in it... - */ - ProcessArray(InstanceType instanceType, const std::string &name, const std::string &unit, - const std::string &description, const std::vector &initialValue) - : mtca4u::NDRegisterAccessor(name, unit, description), - _instanceType(instanceType), - _vectorSize(initialValue.size()), - _maySendDestructively(true), - _sharedState(boost::make_shared(1)), - _currentIndex(0) - { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != STAND_ALONE) { - throw std::invalid_argument( - "This constructor may only be used for a stand-alone process scalar."); - } - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; - } - /** * Creates a process array that acts as a receiver. A receiver is * intended to work as a tandem with a sender and receives values that @@ -868,30 +836,6 @@ namespace ChimeraTK { /*** Non-member functions below this line ****************************************************************************/ /*********************************************************************************************************************/ - /** - * Creates a simple process array. A simple process array just works on its - * own and does not implement a synchronization mechanism. Apart from this, - * it is fully functional, so that you get, set, and swap values. - * - * The specified initial value is used for all the elements of the array. - */ - template - typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string &name = "", - const std::string &unit = "", const std::string &description = "", T initialValue = 0); - - /** - * Creates a simple process array. A simple process array just works on its - * own and does not implement a synchronization mechanism. Apart from this, - * it is fully functional, so that you get, set, and swap values. - * - * The array's size is set to the number of elements stored in the vector - * provided for initialization and all elements are initialized with the - * values provided by this vector. - */ - template - typename ProcessArray::SharedPtr createSimpleProcessArray(const std::vector& initialValue, - const std::string & name = "", const std::string &unit = "", const std::string &description = ""); - /** * Creates a synchronized process array. A synchronized process array works * as a pair of two process arrays, where one process array acts as a sender @@ -997,24 +941,6 @@ namespace ChimeraTK { /*********************************************************************************************************************/ /*** Implementations below this line *********************************************************************************/ -/*********************************************************************************************************************/ - - template - typename ProcessArray::SharedPtr createSimpleProcessArray(std::size_t size, const std::string & name, - const std::string &unit, const std::string &description, T initialValue) { - return boost::make_shared >(ProcessArray::STAND_ALONE, - name, unit, description, std::vector(size, initialValue)); - } - -/*********************************************************************************************************************/ - - template - typename ProcessArray::SharedPtr createSimpleProcessArray( - const std::vector& initialValue, const std::string & name, const std::string &unit, const std::string &description) { - return boost::make_shared >(ProcessArray::STAND_ALONE, - name, unit, description, initialValue); - } - /*********************************************************************************************************************/ template diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 8acfe41..1737cf2 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -21,7 +21,6 @@ namespace ChimeraTK { class ProcessArrayTest { public: static void testConstructors(); - static void testAssignment(); static void testGet(); static void testSet(); static void testSwap(); @@ -50,7 +49,6 @@ namespace ChimeraTK { ProcessArrayTestSuite() : test_suite("ProcessArray test suite") { add(BOOST_TEST_CASE(&ProcessArrayTest::testConstructors)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testAssignment)); add(BOOST_TEST_CASE(&ProcessArrayTest::testGet)); add(BOOST_TEST_CASE(&ProcessArrayTest::testSet)); add(BOOST_TEST_CASE(&ProcessArrayTest::testSwap)); @@ -64,42 +62,12 @@ namespace ChimeraTK { template void ProcessArrayTest::testConstructors() { - typename ProcessArray::SharedPtr simpleArray = - createSimpleProcessArray(N_ELEMENTS); - // The name should be empty and all elements should have been initialized - // with zeroes. - BOOST_CHECK(simpleArray->getName() == ""); - for (typename std::vector::iterator i = simpleArray->get().begin(); - i != simpleArray->get().end(); ++i) { - BOOST_CHECK(*i == 0); - } - BOOST_CHECK(simpleArray->get().size() == N_ELEMENTS); - // Now we test the constructor with non-default parameters. - simpleArray = createSimpleProcessArray(N_ELEMENTS, "test", "myUnit", "someDescription", SOME_NUMBER); - BOOST_CHECK(simpleArray->getName() == "test"); - BOOST_CHECK(simpleArray->getUnit() == "myUnit"); - BOOST_CHECK(simpleArray->getDescription() == "someDescription"); - for (typename std::vector::iterator i = simpleArray->get().begin(); - i != simpleArray->get().end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - BOOST_CHECK(simpleArray->get().size() == N_ELEMENTS); - BOOST_CHECK(!simpleArray->isReadable()); - BOOST_CHECK(!simpleArray->isWriteable()); - // Now we test the constructor that takes a whole reference vector. + std::vector referenceVector; referenceVector.push_back(0); referenceVector.push_back(1); referenceVector.push_back(2); referenceVector.push_back(3); - simpleArray = createSimpleProcessArray(referenceVector, "test", "", ""); - BOOST_CHECK(simpleArray->getName() == "test"); - BOOST_CHECK(simpleArray->get().size() == 4); - BOOST_CHECK( - std::equal(simpleArray->get().begin(), simpleArray->get().end(), - referenceVector.begin())); - BOOST_CHECK(!simpleArray->isReadable()); - BOOST_CHECK(!simpleArray->isWriteable()); // Now we repeat the tests but for a sender / receiver pair. We do not test // the notification listener and time-stamp source because there is no @@ -158,44 +126,16 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testAssignment() { - typename ProcessArray::SharedPtr array1 = createSimpleProcessArray( - N_ELEMENTS); - typename ProcessArray::SharedPtr array2 = createSimpleProcessArray( - N_ELEMENTS, "", "", "", SOME_NUMBER); - // Test the assignment of another process array. - (*array1) = (*array2); - for (typename std::vector::iterator i = array1->get().begin(); - i != array1->get().end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - // Test the assignment of a vector. - std::vector v(N_ELEMENTS, SOME_NUMBER + 1); - (*array1) = v; - for (typename std::vector::iterator i = array1->get().begin(); - i != array1->get().end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 1); - } - // For the synchronized variant we only test that the assignment operator - // does not throw if it should not. As the implementation is the same as - // for the simple array, there is no need to check the actual values. + void ProcessArrayTest::testGet() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS); + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - (*sender) = (*array1); - (*receiver) = (*array1); - (*sender) = v; - (*receiver) = v; - } - - template - void ProcessArrayTest::testGet() { - typename ProcessArray::SharedPtr simpleArray = - createSimpleProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); - typename std::vector & v = simpleArray->get(); - typename std::vector const & cv = simpleArray->get(); + sender->get(); + receiver->get(); + typename std::vector & v = sender->get(); + typename std::vector const & cv = sender->get(); for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } @@ -205,82 +145,60 @@ namespace ChimeraTK { } // If we have a pointer to a const array, we should get a reference to a // const vector. - typename boost::shared_ptr const> constSimpleArray = - simpleArray; + typename boost::shared_ptr const> constSimpleArray = sender; typename std::vector const & cv2 = constSimpleArray->get(); for (typename std::vector::const_iterator i = cv2.begin(); i != cv2.end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - // Next we run the tests for a synchronized array that supports swapping. We - // only test that the getter methods do not throw an exception, as we can - // assume that the returned reference is correct if it was correct for the - // simple array. - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - sender->get(); - receiver->get(); } template void ProcessArrayTest::testSet() { - typename ProcessArray::SharedPtr array1 = createSimpleProcessArray( - N_ELEMENTS); - typename ProcessArray::SharedPtr array2 = createSimpleProcessArray( - N_ELEMENTS, "", "", "", SOME_NUMBER); - // Test the assignment of another process array. - array1->set(*array2); - for (typename std::vector::iterator i = array1->get().begin(); - i != array1->get().end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } + typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> + senderReceiver = createSynchronizedProcessArray(N_ELEMENTS); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + // Test the assignment of a vector. std::vector v(N_ELEMENTS, SOME_NUMBER + 1); - array1->set(v); - for (typename std::vector::iterator i = array1->get().begin(); - i != array1->get().end(); ++i) { + sender->set(v); + receiver->set(v); + for(typename std::vector::iterator i = sender->get().begin(); i != sender->get().end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 1); + } + for(typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } - // For the synchronized variant we only test that the set method does not - // throw if it should not. As the implementation is the same as for the - // simple array, there is no need to check the actual values. + + } + + template + void ProcessArrayTest::testSwap() { + typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - sender->set(*array1); - receiver->set(*array1); - sender->set(v); - receiver->set(v); - } - template - void ProcessArrayTest::testSwap() { - typename ProcessArray::SharedPtr simpleArray = - createSimpleProcessArray(N_ELEMENTS); + // Test swapping with a vector typename std::vector v(N_ELEMENTS, SOME_NUMBER); - simpleArray->swap(v); - for (typename std::vector::iterator i = simpleArray->get().begin(); - i != simpleArray->get().end(); ++i) { + sender->swap(v); + for(typename std::vector::iterator i = sender->get().begin(); i != sender->get().end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == 0); } - // For the synchronized variant we only test that the swap method does not - // throw if it should not. As the implementation is the same as for the - // simple array, there is no need to check the actual values. - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; sender->swap(v); receiver->swap(v); + for(typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } + for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { + BOOST_CHECK(*i == 0); + } } template From 9d6a1b5c791ea708734d310a5f59de18a95d3d50 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 12:58:01 +0200 Subject: [PATCH 202/786] moved longer implementations out of the class definition for better code readability --- .../ControlSystemAdapter/ProcessArray.h | 765 ++++++++++-------- 1 file changed, 422 insertions(+), 343 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 7932b48..4a306fc 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -75,49 +75,7 @@ namespace ChimeraTK { */ ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, - VersionNumberSource::SharedPtr versionNumberSource) - : mtca4u::NDRegisterAccessor(name, unit, description), - _instanceType(instanceType), - _vectorSize(initialValue.size()), - _maySendDestructively(false), - _sharedState(boost::make_shared(numberOfBuffers)), - _currentIndex(0), - _versionNumberSource(versionNumberSource) - { - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument("This constructor may only be used for a receiver process scalar."); - } - // We need at least two buffers for the queue (so four buffers in total) - // in order to guarantee that we never have to block. - if (numberOfBuffers < 2) { - throw std::invalid_argument("The number of buffers must be at least two."); - } - // We have to limit the number of buffers because we cannot allocate - // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - 2)) { - throw std::invalid_argument("The number of buffers is too large."); - } - // We have to initialize the buffers by copying in the initial vectors. - for (auto &i : _sharedState->_buffers) { - i.value = initialValue; - } - // The buffer with the index 0 is assigned to the receiver and the - // buffer with the index 1 is assigned to the sender. All buffers have - // to be added to the empty-buffer queue. - for (std::size_t i = 2; i < _sharedState->_buffers.size(); ++i) { - _sharedState->_emptyBufferQueue.push(i); - } - } + VersionNumberSource::SharedPtr versionNumberSource); /** * Creates a process array that acts as a sender. A sender is intended @@ -139,52 +97,9 @@ namespace ChimeraTK { TimeStampSource::SharedPtr timeStampSource, VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener, - ProcessArray::SharedPtr receiver) - : mtca4u::NDRegisterAccessor(receiver->getName(), receiver->getUnit(), receiver->getDescription()), - _instanceType(instanceType), - _vectorSize(receiver->_vectorSize), - _maySendDestructively(maySendDestructively), - _sharedState(receiver->_sharedState), - _currentIndex(1), - _receiver(receiver), - _timeStampSource(timeStampSource), - _versionNumberSource(versionNumberSource), - _sendNotificationListener(sendNotificationListener) - { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument("This constructor may only be used for a sender process scalar."); - } - if (!receiver) { - throw std::invalid_argument("The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; - // put future into the notification queue - _sharedState->_notificationQueue.push(_sharedState->_notificationPromise.get_future()); - } - - ~ProcessArray() { - // if this is a receiver, shutdown a potentially running readAsync - if(_instanceType == RECEIVER) { - if(this->readAsyncThread.joinable()) { - this->readAsyncThread.interrupt(); - try { - _sharedState->_notificationPromise.set_value(); - } - catch(boost::promise_already_satisfied) { - // ignore -> already notified - } - this->readAsyncThread.join(); - } - } - } + ProcessArray::SharedPtr receiver); + + ~ProcessArray(); /** * FIXME: I think we don't want this. The base class is no copy/assignable anyway. @@ -327,154 +242,17 @@ namespace ChimeraTK { return (_sharedState->_buffers[_currentIndex]).versionNumber; } - void doReadTransfer() override { - // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is - // already fulfilled. Otherwise we might run out of futures on the notification queue. - if(mtca4u::TransferElement::hasActiveFuture) { - mtca4u::TransferElement::activeFuture.getBoostFuture().wait(); - mtca4u::TransferElement::hasActiveFuture = false; - if(!_asyncReadTransferNeeded) return; - _asyncReadTransferNeeded = false; - } - // Obtain futures from the notification queue and wait on them until we receive data. We start with checking - // for data before obtaining a future from the notification queue, since this is faster and the notification - // queue is shorter than the data queue. - while(!doReadTransferNonBlocking()) { - boost::shared_future future; - bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); - assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm - (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled - future.wait(); - boost::this_thread::interruption_point(); - } - } + void doReadTransfer() override; - bool doReadTransferNonBlocking() override { - if (_instanceType != RECEIVER) { - throw std::logic_error("Receive operation is only allowed for a receiver process variable."); - } - // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is - // already fulfilled. Otherwise we might run out of futures on the notification queue. - if(mtca4u::TransferElement::hasActiveFuture) { - auto theFuture = mtca4u::TransferElement::activeFuture.getBoostFuture(); - auto status = theFuture.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) return false; - mtca4u::TransferElement::hasActiveFuture = false; - if(!_asyncReadTransferNeeded) return true; - _asyncReadTransferNeeded = false; - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the sender might get into - // trouble when it suddenly experiences a vector of the wrong size. - if (_sharedState->_buffers[_currentIndex].value.size() != _vectorSize) { - throw std::runtime_error("Cannot run receive operation because the size of the vector belonging to the current" - " buffer has been modified."); - } - std::size_t nextIndex; - if (_sharedState->_fullBufferQueue.pop(nextIndex)) { - // We only use the incoming update if it has a higher version number - // than the current value. This check is disabled when there is no - // version number source. - if (!_versionNumberSource || (_sharedState->_buffers[nextIndex]).versionNumber > getVersionNumber()) { - _sharedState->_emptyBufferQueue.push(_currentIndex); - _currentIndex = nextIndex; - return true; - } else { - _sharedState->_emptyBufferQueue.push(nextIndex); - return false; - } - } else { - return false; - } - } + bool doReadTransferNonBlocking() override; - bool doReadTransferLatest() override { - bool hasRead = false; - while(doReadTransferNonBlocking()) hasRead = true; - return hasRead; - } + bool doReadTransferLatest() override; - mtca4u::TransferFuture& readAsync() override { - if (_instanceType != RECEIVER) { - throw std::logic_error("Receive operation is only allowed for a receiver process variable."); - } - ChimeraTK::ExperimentalFeatures::check("asynchronous read"); - if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; // the last future given out by this fuction is still active - - // check if we already have data - boost::promise promise; - boost::shared_future readyFuture = promise.get_future().share(); - promise.set_value(); - if(doReadTransferNonBlocking()) { - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - _asyncReadTransferNeeded = false; - return mtca4u::TransferElement::activeFuture; - } - else { - boost::shared_future future; - // obtain future from queue - bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); - assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm - (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled - // if this future is already fulfilled, obtain the next one - if(future.is_ready()) { - // before we can obtain the next future, we must check for new data once more - if(doReadTransferNonBlocking()) { - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - _asyncReadTransferNeeded = false; - return mtca4u::TransferElement::activeFuture; - } - atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); - assert(atLeastOneFuturePresent); - } - // return the future - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - _asyncReadTransferNeeded = true; - return mtca4u::TransferElement::activeFuture; - } - } + mtca4u::TransferFuture& readAsync() override; - void postRead() override { - // if this is called inside mtca4u::TransferFuture::wait(), we may still need to obtain the new data from the queue - if(_asyncReadTransferNeeded) { - _asyncReadTransferNeeded = false; - mtca4u::TransferElement::hasActiveFuture = false; // prevent doReadTransferNonBlocking() from checking the future only - bool hasNewDataAfterFuture = doReadTransferNonBlocking(); - if(!hasNewDataAfterFuture) { - // we did not receive new data despite being notified about new data... - // this either means there is a bug in the ControlSystemAdapter, or the application has called postRead() from - // a different thread while mtca4u::TransferFuture::wait() is still waiting. This is in principle violating - // the interface definition, but can be safe under certain circumstances (e.g. in ApplicationCore's testable - // mode, when a stall is detected and debugging information should be printed). Thus we do not raise an - // assertion but throw an exception which can be caught when this is known to be safe. - - /// @todo Change this back to an exception, this busy-waiting loop is only a temporary work around until the - /// problem has been fully understood and solved. - /* throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " - "ProcessArray from different threads? Variable name: "+this->getName()); */ - std::cerr << "*** ERROR: ProcessArrya::postRead() called despite no new data has arrived. Are you reading the " - "same ProcessArray from different threads? Variable name: "+this->getName() << std::endl; - std::cerr << " Using 1ms polling interval to wait for actual new data." << std::endl; - while(!doReadTransferNonBlocking()) usleep(1000); - std::cerr << " New data has arrived for " << this->getName() << ". Busy waiting stopped." << std::endl; - } - } - // swap data out of the queue buffer - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); - } + void postRead() override; - bool write() override { - VersionNumber newVersionNumber; - if (_versionNumberSource) { - newVersionNumber = _versionNumberSource->nextVersionNumber(); - } else { - newVersionNumber = 0; - } - return write(newVersionNumber); - } + bool write() override; /** * Sends the current value to the receiver. Returns true if an @@ -488,9 +266,7 @@ namespace ChimeraTK { * * Throws an exception if this process variable is not a sender. */ - bool write(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! ??? - return writeInternal(newVersionNumber, true); - } + bool write(VersionNumber newVersionNumber); /// @todo FIXME this function must be present in TransferElement already! /** * Sends the current value to the receiver. Returns true if an @@ -511,15 +287,7 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - bool writeDestructively() { /// @todo FIXME this function must be present in TransferElement already! - VersionNumber newVersionNumber; - if (_versionNumberSource) { - newVersionNumber = _versionNumberSource->nextVersionNumber(); - } else { - newVersionNumber = 0; - } - return writeDestructively(newVersionNumber); - } + bool writeDestructively(); /// @todo FIXME this function must be present in TransferElement already! /** * Sends the current value to the receiver. Returns true if an @@ -540,13 +308,7 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - bool writeDestructively(VersionNumber newVersionNumber) { /// @todo FIXME this function must be present in TransferElement already! - if (!_maySendDestructively) { - throw std::runtime_error( - "This process variable must not be sent destructively because the corresponding flag has not been set."); - } - return writeInternal(newVersionNumber, false); - } + bool writeDestructively(VersionNumber newVersionNumber); /// @todo FIXME this function must be present in TransferElement already! const std::type_info& getValueType() const override { return typeid(T); @@ -569,18 +331,7 @@ namespace ChimeraTK { // You can't replace anything here. Just do nothing. } - void setPersistentDataStorage(boost::shared_ptr storage) override { - if(!isWriteable()) return; - bool sendInitialValue = false; - if(!_persistentDataStorage) sendInitialValue = true; - _persistentDataStorage = storage; - _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), - mtca4u::NDRegisterAccessor::getNumberOfSamples()); - if(sendInitialValue) { - mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); - write(); - } - } + void setPersistentDataStorage(boost::shared_ptr storage) override ; /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the * same variable but different for any other process variable within the same process. The unique ID will not be @@ -750,86 +501,8 @@ namespace ChimeraTK { * * The return value will be true, if older data was overwritten during the send operation, or otherwise false. */ - bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { - if (_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the receiver might get into - // trouble when it suddenly experiences a vector of the wrong size. - if (mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been " - "modified. Variable name: "+this->getName()); - } - // First update the persistent data storage, if any was associated. This cannot be done after sending, since the - // value might no longer be available within this instance. - if (_persistentDataStorage) { - _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); - } - - // Before sending the value, we have to update the associated - // time-stamp. - TimeStamp newTimeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - (_sharedState->_buffers[_currentIndex]).timeStamp = newTimeStamp; - (_sharedState->_buffers[_currentIndex]).versionNumber = newVersionNumber; - if (shouldCopy) { - _sharedState->_buffers[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; - } - else { - _sharedState->_buffers[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); - } - std::size_t nextIndex; - bool lostData = false; - if (_sharedState->_emptyBufferQueue.pop(nextIndex)) { - _sharedState->_fullBufferQueue.push(_currentIndex); - } else { - // We can still send, but we will lose older data. - if (_sharedState->_fullBufferQueue.pop(nextIndex)) { - _sharedState->_fullBufferQueue.push(_currentIndex); - lostData = true; - } else { - // It is possible, that we did not find an empty buffer but before - // we could get a full buffer, the receiver processed all full - // buffers. In this case, the queue of empty buffers cannot be empty - // any longer because we have at least four buffers. - if (!_sharedState->_emptyBufferQueue.pop(nextIndex)) { - // This should never happen and is just an assertion for extra - // safety. - throw std::runtime_error( - "Assertion that empty-buffer queue has at least one element failed."); - } - _sharedState->_fullBufferQueue.push(_currentIndex); - } - } - if (shouldCopy) { - _sharedState->_buffers[nextIndex].timeStamp = newTimeStamp; - _sharedState->_buffers[nextIndex].versionNumber = newVersionNumber; - } - _currentIndex = nextIndex; - if (_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - // Notify the receiver through the notification queue. It must be guaranteed that there is at least one - // unfulfilled future in the notification queue at all times, so we try pushing a new one first. If this - // failes, there is already one unfulfilled and one fulfilled future in the queue, so we are done. - boost::promise nextPromise; - if(_sharedState->_notificationQueue.push(nextPromise.get_future())) { - try { - _sharedState->_notificationPromise.set_value(); - } - catch(boost::promise_already_satisfied) { - // ignore -> this may happen only during shutdown - } - _sharedState->_notificationPromise = std::move(nextPromise); - } + bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy); - return lostData; - } }; /*********************************************************************************************************************/ @@ -940,7 +613,413 @@ namespace ChimeraTK { ProcessVariableListener::SharedPtr()); /*********************************************************************************************************************/ -/*** Implementations below this line *********************************************************************************/ +/*** Implementations of member functions below this line *************************************************************/ +/*********************************************************************************************************************/ + + template + ProcessArray::ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, + const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, + VersionNumberSource::SharedPtr versionNumberSource) + : mtca4u::NDRegisterAccessor(name, unit, description), + _instanceType(instanceType), + _vectorSize(initialValue.size()), + _maySendDestructively(false), + _sharedState(boost::make_shared(numberOfBuffers)), + _currentIndex(0), + _versionNumberSource(versionNumberSource) + { + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != RECEIVER) { + throw std::invalid_argument("This constructor may only be used for a receiver process scalar."); + } + // We need at least two buffers for the queue (so four buffers in total) + // in order to guarantee that we never have to block. + if (numberOfBuffers < 2) { + throw std::invalid_argument("The number of buffers must be at least two."); + } + // We have to limit the number of buffers because we cannot allocate + // more buffers than can be represented by size_t and the total number + // of buffers is the specified number plus two. These extra two buffers + // are needed because the sender and the receiver each hold one buffer + // at all times and thus only the rest remains for the queue. We use + // this definition of the number of buffers to keep it consistent with + // what ProcessScalarImpl uses. + if (numberOfBuffers > (std::numeric_limits::max() - 2)) { + throw std::invalid_argument("The number of buffers is too large."); + } + // We have to initialize the buffers by copying in the initial vectors. + for (auto &i : _sharedState->_buffers) { + i.value = initialValue; + } + // The buffer with the index 0 is assigned to the receiver and the + // buffer with the index 1 is assigned to the sender. All buffers have + // to be added to the empty-buffer queue. + for (std::size_t i = 2; i < _sharedState->_buffers.size(); ++i) { + _sharedState->_emptyBufferQueue.push(i); + } + } + +/*********************************************************************************************************************/ + + template + ProcessArray::ProcessArray(InstanceType instanceType, bool maySendDestructively, + TimeStampSource::SharedPtr timeStampSource, + VersionNumberSource::SharedPtr versionNumberSource, + ProcessVariableListener::SharedPtr sendNotificationListener, + ProcessArray::SharedPtr receiver) + : mtca4u::NDRegisterAccessor(receiver->getName(), receiver->getUnit(), receiver->getDescription()), + _instanceType(instanceType), + _vectorSize(receiver->_vectorSize), + _maySendDestructively(maySendDestructively), + _sharedState(receiver->_sharedState), + _currentIndex(1), + _receiver(receiver), + _timeStampSource(timeStampSource), + _versionNumberSource(versionNumberSource), + _sendNotificationListener(sendNotificationListener) + { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != SENDER) { + throw std::invalid_argument("This constructor may only be used for a sender process scalar."); + } + if (!receiver) { + throw std::invalid_argument("The pointer to the receiver must not be null."); + } + if (receiver->_instanceType != RECEIVER) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; + // put future into the notification queue + _sharedState->_notificationQueue.push(_sharedState->_notificationPromise.get_future()); + } + +/*********************************************************************************************************************/ + + template + ProcessArray::~ProcessArray() { + // if this is a receiver, shutdown a potentially running readAsync + if(_instanceType == RECEIVER) { + if(this->readAsyncThread.joinable()) { + this->readAsyncThread.interrupt(); + try { + _sharedState->_notificationPromise.set_value(); + } + catch(boost::promise_already_satisfied) { + // ignore -> already notified + } + this->readAsyncThread.join(); + } + } + } + +/*********************************************************************************************************************/ + + template + void ProcessArray::doReadTransfer() { + // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is + // already fulfilled. Otherwise we might run out of futures on the notification queue. + if(mtca4u::TransferElement::hasActiveFuture) { + mtca4u::TransferElement::activeFuture.getBoostFuture().wait(); + mtca4u::TransferElement::hasActiveFuture = false; + if(!_asyncReadTransferNeeded) return; + _asyncReadTransferNeeded = false; + } + // Obtain futures from the notification queue and wait on them until we receive data. We start with checking + // for data before obtaining a future from the notification queue, since this is faster and the notification + // queue is shorter than the data queue. + while(!doReadTransferNonBlocking()) { + boost::shared_future future; + bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); + assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm + (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled + future.wait(); + boost::this_thread::interruption_point(); + } + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::doReadTransferNonBlocking() { + if (_instanceType != RECEIVER) { + throw std::logic_error("Receive operation is only allowed for a receiver process variable."); + } + // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is + // already fulfilled. Otherwise we might run out of futures on the notification queue. + if(mtca4u::TransferElement::hasActiveFuture) { + auto theFuture = mtca4u::TransferElement::activeFuture.getBoostFuture(); + auto status = theFuture.wait_for(boost::chrono::duration(0)); + if(status == boost::future_status::timeout) return false; + mtca4u::TransferElement::hasActiveFuture = false; + if(!_asyncReadTransferNeeded) return true; + _asyncReadTransferNeeded = false; + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the sender might get into + // trouble when it suddenly experiences a vector of the wrong size. + if (_sharedState->_buffers[_currentIndex].value.size() != _vectorSize) { + throw std::runtime_error("Cannot run receive operation because the size of the vector belonging to the current" + " buffer has been modified."); + } + std::size_t nextIndex; + if (_sharedState->_fullBufferQueue.pop(nextIndex)) { + // We only use the incoming update if it has a higher version number + // than the current value. This check is disabled when there is no + // version number source. + if (!_versionNumberSource || (_sharedState->_buffers[nextIndex]).versionNumber > getVersionNumber()) { + _sharedState->_emptyBufferQueue.push(_currentIndex); + _currentIndex = nextIndex; + return true; + } else { + _sharedState->_emptyBufferQueue.push(nextIndex); + return false; + } + } else { + return false; + } + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::doReadTransferLatest() { + bool hasRead = false; + while(doReadTransferNonBlocking()) hasRead = true; + return hasRead; + } + +/*********************************************************************************************************************/ + + template + mtca4u::TransferFuture& ProcessArray::readAsync() { + if (_instanceType != RECEIVER) { + throw std::logic_error("Receive operation is only allowed for a receiver process variable."); + } + ChimeraTK::ExperimentalFeatures::check("asynchronous read"); + if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; // the last future given out by this fuction is still active + + // check if we already have data + boost::promise promise; + boost::shared_future readyFuture = promise.get_future().share(); + promise.set_value(); + if(doReadTransferNonBlocking()) { + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + _asyncReadTransferNeeded = false; + return mtca4u::TransferElement::activeFuture; + } + else { + boost::shared_future future; + // obtain future from queue + bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); + assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm + (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled + // if this future is already fulfilled, obtain the next one + if(future.is_ready()) { + // before we can obtain the next future, we must check for new data once more + if(doReadTransferNonBlocking()) { + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + _asyncReadTransferNeeded = false; + return mtca4u::TransferElement::activeFuture; + } + atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); + assert(atLeastOneFuturePresent); + } + // return the future + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + _asyncReadTransferNeeded = true; + return mtca4u::TransferElement::activeFuture; + } + } + +/*********************************************************************************************************************/ + + template + void ProcessArray::postRead() { + // if this is called inside mtca4u::TransferFuture::wait(), we may still need to obtain the new data from the queue + if(_asyncReadTransferNeeded) { + _asyncReadTransferNeeded = false; + mtca4u::TransferElement::hasActiveFuture = false; // prevent doReadTransferNonBlocking() from checking the future only + bool hasNewDataAfterFuture = doReadTransferNonBlocking(); + if(!hasNewDataAfterFuture) { + // we did not receive new data despite being notified about new data... + // this either means there is a bug in the ControlSystemAdapter, or the application has called postRead() from + // a different thread while mtca4u::TransferFuture::wait() is still waiting. This is in principle violating + // the interface definition, but can be safe under certain circumstances (e.g. in ApplicationCore's testable + // mode, when a stall is detected and debugging information should be printed). Thus we do not raise an + // assertion but throw an exception which can be caught when this is known to be safe. + + /// @todo Change this back to an exception, this busy-waiting loop is only a temporary work around until the + /// problem has been fully understood and solved. + /* throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " + "ProcessArray from different threads? Variable name: "+this->getName()); */ + std::cerr << "*** ERROR: ProcessArrya::postRead() called despite no new data has arrived. Are you reading the " + "same ProcessArray from different threads? Variable name: "+this->getName() << std::endl; + std::cerr << " Using 1ms polling interval to wait for actual new data." << std::endl; + while(!doReadTransferNonBlocking()) usleep(1000); + std::cerr << " New data has arrived for " << this->getName() << ". Busy waiting stopped." << std::endl; + } + } + // swap data out of the queue buffer + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::write() { + VersionNumber newVersionNumber; + if (_versionNumberSource) { + newVersionNumber = _versionNumberSource->nextVersionNumber(); + } else { + newVersionNumber = 0; + } + return write(newVersionNumber); + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::write(VersionNumber newVersionNumber) { + return writeInternal(newVersionNumber, true); + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::writeDestructively() { + VersionNumber newVersionNumber; + if (_versionNumberSource) { + newVersionNumber = _versionNumberSource->nextVersionNumber(); + } else { + newVersionNumber = 0; + } + return writeDestructively(newVersionNumber); + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::writeDestructively(VersionNumber newVersionNumber) { + if (!_maySendDestructively) { + throw std::runtime_error( + "This process variable must not be sent destructively because the corresponding flag has not been set."); + } + return writeInternal(newVersionNumber, false); + } + +/*********************************************************************************************************************/ + + template + void ProcessArray::setPersistentDataStorage(boost::shared_ptr storage) { + if(!isWriteable()) return; + bool sendInitialValue = false; + if(!_persistentDataStorage) sendInitialValue = true; + _persistentDataStorage = storage; + _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), + mtca4u::NDRegisterAccessor::getNumberOfSamples()); + if(sendInitialValue) { + mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); + write(); + } + } + +/*********************************************************************************************************************/ + + template + bool ProcessArray::writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { + if (_instanceType != SENDER) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the receiver might get into + // trouble when it suddenly experiences a vector of the wrong size. + if (mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been " + "modified. Variable name: "+this->getName()); + } + // First update the persistent data storage, if any was associated. This cannot be done after sending, since the + // value might no longer be available within this instance. + if (_persistentDataStorage) { + _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); + } + + // Before sending the value, we have to update the associated + // time-stamp. + TimeStamp newTimeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : + TimeStamp::currentTime(); + (_sharedState->_buffers[_currentIndex]).timeStamp = newTimeStamp; + (_sharedState->_buffers[_currentIndex]).versionNumber = newVersionNumber; + if (shouldCopy) { + _sharedState->_buffers[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + } + else { + _sharedState->_buffers[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + } + std::size_t nextIndex; + bool lostData = false; + if (_sharedState->_emptyBufferQueue.pop(nextIndex)) { + _sharedState->_fullBufferQueue.push(_currentIndex); + } else { + // We can still send, but we will lose older data. + if (_sharedState->_fullBufferQueue.pop(nextIndex)) { + _sharedState->_fullBufferQueue.push(_currentIndex); + lostData = true; + } else { + // It is possible, that we did not find an empty buffer but before + // we could get a full buffer, the receiver processed all full + // buffers. In this case, the queue of empty buffers cannot be empty + // any longer because we have at least four buffers. + if (!_sharedState->_emptyBufferQueue.pop(nextIndex)) { + // This should never happen and is just an assertion for extra + // safety. + throw std::runtime_error( + "Assertion that empty-buffer queue has at least one element failed."); + } + _sharedState->_fullBufferQueue.push(_currentIndex); + } + } + if (shouldCopy) { + _sharedState->_buffers[nextIndex].timeStamp = newTimeStamp; + _sharedState->_buffers[nextIndex].versionNumber = newVersionNumber; + } + _currentIndex = nextIndex; + if (_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); + } + // Notify the receiver through the notification queue. It must be guaranteed that there is at least one + // unfulfilled future in the notification queue at all times, so we try pushing a new one first. If this + // failes, there is already one unfulfilled and one fulfilled future in the queue, so we are done. + boost::promise nextPromise; + if(_sharedState->_notificationQueue.push(nextPromise.get_future())) { + try { + _sharedState->_notificationPromise.set_value(); + } + catch(boost::promise_already_satisfied) { + // ignore -> this may happen only during shutdown + } + _sharedState->_notificationPromise = std::move(nextPromise); + } + + return lostData; + } + +/*********************************************************************************************************************/ +/*** Implementations of non-member functions below this line *********************************************************/ /*********************************************************************************************************************/ template From 82f8a45a4f0f73381cced913ce69210102750181 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 13:07:09 +0200 Subject: [PATCH 203/786] removed deprecated functions, especially get() and set(), which have been superseeded for some time now by the appropriate functions of the TransferElement interface (e.g. accessChannel() etc.). --- .../ControlSystemAdapter/ProcessArray.h | 102 --------------- tests/src/testProcessArray.cpp | 118 ++++++++---------- 2 files changed, 55 insertions(+), 165 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 4a306fc..923b90b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -101,108 +101,6 @@ namespace ChimeraTK { ~ProcessArray(); - /** - * FIXME: I think we don't want this. The base class is no copy/assignable anyway. - * - * Assignment operator for another ProcessArray of the same template type. - * It can be of a different implementation, though. The size of the assigned - * array must be smaller than or equal to the target size. - * This behaves excactly like the set(...) method. - * - * @todo THIS FUNCTION IS DEPRECTED - */ - ProcessArray & operator=(ProcessArray const & other) { - set(other); - return (*this); - } - - /** - * Assignment operator for a std::vector of the template data type. - * The size of the assigned array must be smaller than or equal to the - * target size. - * This behaves exactly like the set(...) method. - * - * @todo THIS FUNCTION IS DEPRECTED, use ScalarRegisterAccessor::operator=() instead - */ - ProcessArray & operator=(std::vector const & v) { - set(v); - return *this; - } - - /* - * @todo THIS FUNCTION IS DEPRECTED - */ - operator std::vector() const { - return get(); - } - - /** - * Updates this process variable's value with the elements from the - * specified vector. The vector's number of elements must match this process - * variable's number of elements. - * - * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead - */ - void set(std::vector const & v) { - get() = v; - } - - /** - * Updates this process variable's value with the other process variable's - * value. The other process variable's number of elements must match this - * process variable's number of elements. - * - * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead - */ - void set(ProcessArray const & other) { - set(other.get()); - } - - /** - * Swaps the vector backing this process array with a different vector. - * - * The boost::scoped_ptr passed must not be null - * and must point to a vector that has the same size as the vector it is - * swapped with. - * - * @todo THIS FUNCTION IS DEPRECTED, use accessData()[0].swap() instead - */ - void swap(std::vector & otherVector) { - if (otherVector.size() != get().size()) { - throw std::runtime_error("Vector sizes do not match"); - } - get().swap(otherVector); - } - - /** - * Returns a reference to the vector that represents the current value of - * this process array. - * - * The reference returned by this method becomes invalid when a receive, - * send, or swap operation is performed on this process variable. Use of an - * invalid reference results in undefined behavior. - * - * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead - */ - std::vector & get() { - return mtca4u::NDRegisterAccessor::buffer_2D[0]; - } - - /** - * Returns a constant reference to the vector that represents the current - * value of this process array. This version of the get method is used if - * this process array is const. - * - * The reference returned by this method becomes invalid when a receive, - * send, or swap operation is performed on this process variable. Use of an - * invalid reference results in undefined behavior. - * - * @todo THIS FUNCTION IS DEPRECTED, use accessData() instead - */ - std::vector const & get() const { - return mtca4u::NDRegisterAccessor::buffer_2D[0]; - } - bool isReadable() const override { return _instanceType == RECEIVER; } diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 1737cf2..9bbe31b 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -78,17 +78,17 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; BOOST_CHECK(sender->getName() == ""); - for (typename std::vector::iterator i = sender->get().begin(); - i != sender->get().end(); ++i) { + for (typename std::vector::iterator i = sender->accessChannel(0).begin(); + i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == 0); } - BOOST_CHECK(sender->get().size() == N_ELEMENTS); + BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); BOOST_CHECK(receiver->getName() == ""); - for (typename std::vector::iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == 0); } - BOOST_CHECK(receiver->get().size() == N_ELEMENTS); + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); BOOST_CHECK(!sender->isReadable()); BOOST_CHECK(sender->isWriteable()); BOOST_CHECK(receiver->isReadable()); @@ -98,30 +98,30 @@ namespace ChimeraTK { sender = senderReceiver.first; receiver = senderReceiver.second; BOOST_CHECK(sender->getName() == "test"); - for (typename std::vector::iterator i = sender->get().begin(); - i != sender->get().end(); ++i) { + for (typename std::vector::iterator i = sender->accessChannel(0).begin(); + i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - BOOST_CHECK(sender->get().size() == N_ELEMENTS); + BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); BOOST_CHECK(receiver->getName() == "test"); - for (typename std::vector::const_iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::const_iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - BOOST_CHECK(receiver->get().size() == N_ELEMENTS); + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); senderReceiver = createSynchronizedProcessArray(referenceVector, "test", "", "", 5, false); sender = senderReceiver.first; receiver = senderReceiver.second; BOOST_CHECK(sender->getName() == "test"); - BOOST_CHECK(sender->get().size() == 4); + BOOST_CHECK(sender->accessChannel(0).size() == 4); BOOST_CHECK( - std::equal(sender->get().begin(), sender->get().end(), + std::equal(sender->accessChannel(0).begin(), sender->accessChannel(0).end(), referenceVector.begin())); BOOST_CHECK(receiver->getName() == "test"); - BOOST_CHECK(receiver->get().size() == 4); + BOOST_CHECK(receiver->accessChannel(0).size() == 4); BOOST_CHECK( - std::equal(receiver->get().begin(), receiver->get().end(), + std::equal(receiver->accessChannel(0).begin(), receiver->accessChannel(0).end(), referenceVector.begin())); } @@ -132,10 +132,10 @@ namespace ChimeraTK { createSynchronizedProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - sender->get(); - receiver->get(); - typename std::vector & v = sender->get(); - typename std::vector const & cv = sender->get(); + sender->accessChannel(0); + receiver->accessChannel(0); + typename std::vector & v = sender->accessChannel(0); + typename std::vector const & cv = sender->accessChannel(0); for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } @@ -143,14 +143,6 @@ namespace ChimeraTK { ++i) { BOOST_CHECK(*i == SOME_NUMBER); } - // If we have a pointer to a const array, we should get a reference to a - // const vector. - typename boost::shared_ptr const> constSimpleArray = sender; - typename std::vector const & cv2 = constSimpleArray->get(); - for (typename std::vector::const_iterator i = cv2.begin(); - i != cv2.end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } } template @@ -162,12 +154,12 @@ namespace ChimeraTK { // Test the assignment of a vector. std::vector v(N_ELEMENTS, SOME_NUMBER + 1); - sender->set(v); - receiver->set(v); - for(typename std::vector::iterator i = sender->get().begin(); i != sender->get().end(); ++i) { + sender->accessChannel(0) = v; + receiver->accessChannel(0) = v; + for(typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } - for(typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { + for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } @@ -184,16 +176,16 @@ namespace ChimeraTK { // Test swapping with a vector typename std::vector v(N_ELEMENTS, SOME_NUMBER); - sender->swap(v); - for(typename std::vector::iterator i = sender->get().begin(); i != sender->get().end(); ++i) { + sender->accessChannel(0).swap(v); + for(typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { BOOST_CHECK(*i == 0); } - sender->swap(v); - receiver->swap(v); - for(typename std::vector::iterator i = receiver->get().begin(); i != receiver->get().end(); ++i) { + sender->accessChannel(0).swap(v); + receiver->accessChannel(0).swap(v); + for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { @@ -251,53 +243,53 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr receiver = senderReceiver.second; // If we send two values consecutively, they both should be received because // the number of buffers is two. - sender->get().assign(N_ELEMENTS, SOME_NUMBER); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER); sender->writeDestructively(); - sender->get().assign(N_ELEMENTS, SOME_NUMBER + 1); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 1); sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->readNonBlocking()); // Now we try to send three values consecutively. This should result in the // first value being dropped. - sender->get().assign(N_ELEMENTS, SOME_NUMBER + 2); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 2); sender->writeDestructively(); - sender->get().assign(N_ELEMENTS, SOME_NUMBER + 3); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 3); sender->writeDestructively(); - sender->get().assign(N_ELEMENTS, SOME_NUMBER + 4); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 4); sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 3); } BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 4); } // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->readNonBlocking()); // When we send non-destructively, the value should also be preserved on the // sender side. - sender->get().assign(N_ELEMENTS, SOME_NUMBER + 5); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); sender->write(); BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->get().begin(); - i != receiver->get().end(); ++i) { + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 5); } - for (typename std::vector::iterator i = sender->get().begin(); - i != sender->get().end(); ++i) { + for (typename std::vector::iterator i = sender->accessChannel(0).begin(); + i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 5); } // Calling writeDestructively() on a sender that has not the corresponding @@ -325,35 +317,35 @@ namespace ChimeraTK { // After sending destructively and receiving a value, the version number on // the receiver should be greater. VersionNumber initialVersionNumber = receiver->getVersionNumber(); - sender->get()[0] = 1; + sender->accessChannel(0)[0] = 1; sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); VersionNumber versionNumber = receiver->getVersionNumber(); BOOST_CHECK(versionNumber > initialVersionNumber); - BOOST_CHECK(receiver->get()[0] == 1); + BOOST_CHECK(receiver->accessChannel(0)[0] == 1); // When we send again specifying the same version number, there should be no // update of the receiver. - sender->get()[0] = 2; + sender->accessChannel(0)[0] = 2; sender->writeDestructively(versionNumber); BOOST_CHECK(!receiver->readNonBlocking()); BOOST_CHECK(versionNumber == receiver->getVersionNumber()); - BOOST_CHECK(receiver->get()[0] == 1); + BOOST_CHECK(receiver->accessChannel(0)[0] == 1); // When we explicitly use a greater version number, the receiver should be // updated again. - sender->get()[0] = 3; + sender->accessChannel(0)[0] = 3; sender->writeDestructively(versionNumberSource->nextVersionNumber()); BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); - BOOST_CHECK(receiver->get()[0] == 3); + BOOST_CHECK(receiver->accessChannel(0)[0] == 3); // When we send non-destructively, the version number on the sender and the // receiver should match after sending and receiving. versionNumber = receiver->getVersionNumber(); - sender->get()[0] = 4; + sender->accessChannel(0)[0] = 4; sender->write(); BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); - BOOST_CHECK(receiver->get()[0] == 4); + BOOST_CHECK(receiver->accessChannel(0)[0] == 4); } template From 048d4f0b37da1349041d7ebb5a21b10f3da81ce5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 13:39:24 +0200 Subject: [PATCH 204/786] removed some remaining uses of the removed deprecated functions from the tests --- .../Testing/IndependentTestCore.h | 10 +++--- tests/src/fullStubTest.cpp | 32 +++++++++---------- tests/src/testPVManager.cpp | 32 +++++++++---------- tests/src/testPersistentDataStorage.cc | 12 +++---- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h index ca59afe..83f14fa 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/IndependentTestCore.h @@ -59,16 +59,16 @@ struct TypedPVHolder{ // floating point dataTypeConstant->accessData(0) = 1./sizeof(DataType); } - for (size_t i = 0; i < constantArray->get().size(); ++i){ - constantArray->get()[i] = dataTypeConstant->accessData(0)*i*i; + for (size_t i = 0; i < constantArray->accessChannel(0).size(); ++i){ + constantArray->accessChannel(0)[i] = dataTypeConstant->accessData(0)*i*i; } } void inputToOutput(){ - fromDeviceScalar->set(*toDeviceScalar); + fromDeviceScalar->accessChannel(0) = toDeviceScalar->accessChannel(0); fromDeviceScalar->write(); - for (size_t i = 0; i < fromDeviceArray->get().size() && i < toDeviceArray->get().size() ; ++i){ - fromDeviceArray->get()[i] = toDeviceArray->get()[i]; + for (size_t i = 0; i < fromDeviceArray->accessChannel(0).size() && i < toDeviceArray->accessChannel(0).size() ; ++i){ + fromDeviceArray->accessChannel(0)[i] = toDeviceArray->accessChannel(0)[i]; } fromDeviceArray->write(); } diff --git a/tests/src/fullStubTest.cpp b/tests/src/fullStubTest.cpp index e2ecfd2..5e555d7 100644 --- a/tests/src/fullStubTest.cpp +++ b/tests/src/fullStubTest.cpp @@ -55,12 +55,12 @@ struct TestCoreFixture{ auto inputArray = csManager->getProcessArray(typeNamePrefix+"/CONSTANT_ARRAY"); BOOST_REQUIRE(inputArray); - for (size_t i = 0; i < inputArray->get().size(); ++i){ + for (size_t i = 0; i < inputArray->accessChannel(0).size(); ++i){ std::stringstream errorMessage; errorMessage << "check failed: " << typeNamePrefix+"/CONSTANT_ARRAY[" - << i << "] = "<< inputArray->get()[i] << ", expected " << typeConstant*i*i + << i << "] = "<< inputArray->accessChannel(0)[i] << ", expected " << typeConstant*i*i << std::endl; - BOOST_CHECK_MESSAGE(static_cast(typeConstant*i*i) == inputArray->get()[i], + BOOST_CHECK_MESSAGE(static_cast(typeConstant*i*i) == inputArray->accessChannel(0)[i], errorMessage.str()); } } @@ -73,20 +73,20 @@ struct TestCoreFixture{ BOOST_REQUIRE(fromDeviceArray); // first check that there are all zeros in (startup condition) - for ( auto & t : toDeviceArray->get() ){ + for ( auto & t : toDeviceArray->accessChannel(0) ){ BOOST_CHECK( t == 0 ); } - for (size_t i = 0; i < toDeviceArray->get().size(); ++i){ - toDeviceArray->get()[i] = 13 + i; + for (size_t i = 0; i < toDeviceArray->accessChannel(0).size(); ++i){ + toDeviceArray->accessChannel(0)[i] = 13 + i; } csSyncUtil.sendAll(); IndependentTestCore::runMainLoopOnce(); csSyncUtil.receiveAll(); - for (size_t i = 0; i < fromDeviceArray->get().size(); ++i){ - BOOST_CHECK(fromDeviceArray->get()[i] == static_cast(13 + i)); + for (size_t i = 0; i < fromDeviceArray->accessChannel(0).size(); ++i){ + BOOST_CHECK(fromDeviceArray->accessChannel(0)[i] == static_cast(13 + i)); } } @@ -96,14 +96,14 @@ BOOST_AUTO_TEST_SUITE( FullStubTestSuite ) BOOST_FIXTURE_TEST_CASE( test_read_scalar, TestCoreFixture){ // just after creation of the fixture the constants should be available to the control system - BOOST_CHECK( csManager->getProcessArray("CHAR/DATA_TYPE_CONSTANT")->get()[0] == -1 ); - BOOST_CHECK( csManager->getProcessArray("UCHAR/DATA_TYPE_CONSTANT")->get()[0] == 1 ); - BOOST_CHECK( csManager->getProcessArray("SHORT/DATA_TYPE_CONSTANT")->get()[0] == -2 ); - BOOST_CHECK( csManager->getProcessArray("USHORT/DATA_TYPE_CONSTANT")->get()[0] == 2 ); - BOOST_CHECK( csManager->getProcessArray("INT/DATA_TYPE_CONSTANT")->get()[0] == -4 ); - BOOST_CHECK( csManager->getProcessArray("UINT/DATA_TYPE_CONSTANT")->get()[0] == 4 ); - BOOST_CHECK( csManager->getProcessArray("FLOAT/DATA_TYPE_CONSTANT")->get()[0] == 1./4 ); - BOOST_CHECK( csManager->getProcessArray("DOUBLE/DATA_TYPE_CONSTANT")->get()[0] == 1./8 ); + BOOST_CHECK( csManager->getProcessArray("CHAR/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == -1 ); + BOOST_CHECK( csManager->getProcessArray("UCHAR/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == 1 ); + BOOST_CHECK( csManager->getProcessArray("SHORT/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == -2 ); + BOOST_CHECK( csManager->getProcessArray("USHORT/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == 2 ); + BOOST_CHECK( csManager->getProcessArray("INT/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == -4 ); + BOOST_CHECK( csManager->getProcessArray("UINT/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == 4 ); + BOOST_CHECK( csManager->getProcessArray("FLOAT/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == 1./4 ); + BOOST_CHECK( csManager->getProcessArray("DOUBLE/DATA_TYPE_CONSTANT")->accessChannel(0)[0] == 1./8 ); } BOOST_FIXTURE_TEST_CASE( test_write_scalar, TestCoreFixture){ diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 00e21af..95e18b8 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -302,13 +302,13 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) int32In->accessData(0) = 0; int32Out->accessData(0) = 0; - floatArrayIn->set(vector(10, 0.0)); - floatArrayOut->set(vector(10, 0.0)); + floatArrayIn->accessChannel(0) = vector(10, 0.0); + floatArrayOut->accessChannel(0) = vector(10, 0.0); stopDeviceThread->accessData(0) = 0; while (stopDeviceThread->accessData(0) == 0) { - *int32In = *int32Out; - *floatArrayIn = *floatArrayOut; + int32In->accessChannel(0) = int32Out->accessChannel(0); + floatArrayIn->accessChannel(0) = floatArrayOut->accessChannel(0); int32In->write(); floatArrayIn->write(); boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); @@ -365,9 +365,9 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) outboundProcessVariables.push_back(stopDeviceThread); int32Out->accessData(0) = 55; - floatArrayOut->get().at(0) = 1.0f; - floatArrayOut->get().at(1) = 2.0f; - floatArrayOut->get().at(2) = -1.3f; + floatArrayOut->accessChannel(0).at(0) = 1.0f; + floatArrayOut->accessChannel(0).at(1) = 2.0f; + floatArrayOut->accessChannel(0).at(2) = -1.3f; // Send the values, wait a moment for the other thread to send the updates // and then receive the new values. @@ -376,14 +376,14 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) receiveAll(inboundProcessVariables); BOOST_CHECK(int32In->accessData(0) == 55); - BOOST_CHECK(floatArrayIn->get().at(0) == 1.0f); - BOOST_CHECK(floatArrayIn->get().at(1) == 2.0f); - BOOST_CHECK(floatArrayIn->get().at(2) == -1.3f); + BOOST_CHECK(floatArrayIn->accessChannel(0).at(0) == 1.0f); + BOOST_CHECK(floatArrayIn->accessChannel(0).at(1) == 2.0f); + BOOST_CHECK(floatArrayIn->accessChannel(0).at(2) == -1.3f); int32Out->accessData(0) = -300; - floatArrayOut->get().at(0) = 15.0f; - floatArrayOut->get().at(1) = -7.2f; - floatArrayOut->get().at(9) = 120.0f; + floatArrayOut->accessChannel(0).at(0) = 15.0f; + floatArrayOut->accessChannel(0).at(1) = -7.2f; + floatArrayOut->accessChannel(0).at(9) = 120.0f; // Send the values, wait a moment for the other thread to send the updates // and then receive the new values. @@ -392,9 +392,9 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) receiveAll(inboundProcessVariables); BOOST_CHECK(int32In->accessData(0) == -300); - BOOST_CHECK(floatArrayIn->get().at(0) == 15.0f); - BOOST_CHECK(floatArrayIn->get().at(1) == -7.2f); - BOOST_CHECK(floatArrayIn->get().at(9) == 120.0f); + BOOST_CHECK(floatArrayIn->accessChannel(0).at(0) == 15.0f); + BOOST_CHECK(floatArrayIn->accessChannel(0).at(1) == -7.2f); + BOOST_CHECK(floatArrayIn->accessChannel(0).at(9) == 120.0f); stopDeviceThread->accessData(0) = 1; stopDeviceThread->write(); diff --git a/tests/src/testPersistentDataStorage.cc b/tests/src/testPersistentDataStorage.cc index efe90c6..56e203d 100644 --- a/tests/src/testPersistentDataStorage.cc +++ b/tests/src/testPersistentDataStorage.cc @@ -145,15 +145,15 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) // obtain the process variables, send some values to the variables auto v1 = csManager->getProcessArray("SomeCsToDevVar"); - for(int i=0; i<7; ++i) v1->get()[i] = i*17; + for(int i=0; i<7; ++i) v1->accessChannel(0)[i] = i*17; v1->write(); auto v2 = csManager->getProcessArray("AnotherCsToDevVar"); - for(int i=0; i<42; ++i) v2->get()[i] = i*3.1415 * 1e12; + for(int i=0; i<42; ++i) v2->accessChannel(0)[i] = i*3.1415 * 1e12; v2->write(); auto v3 = devManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored - for(int i=0; i<7; ++i) v3->get()[i] = 9*i + 666; + for(int i=0; i<7; ++i) v3->accessChannel(0)[i] = 9*i + 666; v3->write(); } @@ -183,15 +183,15 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) // obtain the process variables, send some values to the variables auto v1 = devManager->getProcessArray("SomeCsToDevVar"); v1->readNonBlocking(); - for(int i=0; i<7; ++i) BOOST_CHECK( v1->get()[i] == i*17 ); + for(int i=0; i<7; ++i) BOOST_CHECK( v1->accessChannel(0)[i] == i*17 ); auto v2 = devManager->getProcessArray("AnotherCsToDevVar"); v2->readNonBlocking(); - for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->get()[i], i*3.1415 * 1e30, 2e23); + for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->accessChannel(0)[i], i*3.1415 * 1e30, 2e23); auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored v3->readNonBlocking(); - for(int i=0; i<7; ++i) BOOST_CHECK( v3->get()[i] == 0 ); + for(int i=0; i<7; ++i) BOOST_CHECK( v3->accessChannel(0)[i] == 0 ); } From e41009dd2b20ed065afc7fc7fcd879fcbdd97521 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 13:56:36 +0200 Subject: [PATCH 205/786] - Changed the VersionNumber typedef data type into a full class which can also hold a flag whether the VersionNumber is valid or not. This will allow to make the VersionNumber argument of write() optional (in a second step). - Simplified the implementation of the VersionNumberSource by making use of the std::atomic increment operator. --- .../ControlSystemAdapter/VersionNumber.h | 49 +++++++++++++++++-- .../VersionNumberSource.h | 16 ++---- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h index 80239e0..6befd57 100644 --- a/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h +++ b/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h @@ -6,10 +6,53 @@ namespace ChimeraTK { /** - * Type alias for the version number type. The version number is used for - * resolving conflicting updates. + * Class holding a version number together with a flag whether this version number is valid. */ - typedef std::uint64_t VersionNumber; + class VersionNumber { + + public: + + typedef std::uint64_t UnderlyingDataType; + + /** Construct an invalid VersionNumber */ + VersionNumber() : _value(0), _isValid(false) {} + + /** Construct VersionNumber from a numeric value */ + VersionNumber(UnderlyingDataType value) : _value(value), _isValid(true) {} + + /** Copy constructor */ + VersionNumber(const VersionNumber &other) : _value(other._value), _isValid(other._isValid) {} + + /** Convert value of version number into uint64_t. Note that the result is undefined if the version number is not + * valid (i.e. if isValid() returns false). */ + operator UnderlyingDataType() const { + return _value; + } + + /** Check whether the version number is valid */ + bool isValid() const { + return _isValid; + } + + /** Assign number to the version number. This will intrinsically flag the version number to be valid. */ + VersionNumber& operator=(UnderlyingDataType value) { + _value = value; + _isValid = true; + return *this; + } + + /** Copy the full state of another VersionNumber object. */ + VersionNumber& operator=(const VersionNumber &other) { + _value = other._value; + _isValid = other._isValid; + return *this; + } + + private: + + UnderlyingDataType _value; + bool _isValid; + }; } // namespace ChimeraTK diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h index 1b9fd91..c4b6cfd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h +++ b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H -#include +#include #include #include "VersionNumber.h" @@ -35,17 +35,7 @@ namespace ChimeraTK { * may safely be called by any thread without any synchronization. */ inline VersionNumber nextVersionNumber() { - VersionNumber currentVersionNumber = _lastReturnedVersionNumber.load( - boost::memory_order_acquire); - // Don't call this variable nextVersionNumber (but nextVersionNr) because - // compilers might complain that it shadows the function name - VersionNumber nextVersionNr = currentVersionNumber + 1; - while (!_lastReturnedVersionNumber.compare_exchange_weak( - currentVersionNumber, nextVersionNr, boost::memory_order_acq_rel, - boost::memory_order_acquire)) { - nextVersionNr = currentVersionNumber + 1; - } - return nextVersionNr; + return ++_lastReturnedVersionNumber; } private: @@ -53,7 +43,7 @@ namespace ChimeraTK { * Last version number that was returned by a call to * {@link nextVersionNumber()}. */ - boost::atomic _lastReturnedVersionNumber; + std::atomic _lastReturnedVersionNumber; }; From 7dabe23924239985ba3200ba6d7be67637ed23c2 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 14:34:02 +0200 Subject: [PATCH 206/786] moved classes VersionNumber and VersionNumberSource from the ControlSystemAdapter to the DeviceAccess library --- .../ControlSystemAdapter/ProcessArray.h | 2 +- .../ControlSystemAdapter/VersionNumber.h | 59 ------------------- .../VersionNumberSource.h | 52 ---------------- 3 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 include/ChimeraTK/ControlSystemAdapter/VersionNumber.h delete mode 100644 include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 923b90b..a0621a2 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -15,10 +15,10 @@ #include #include +#include #include "ProcessVariableListener.h" #include "TimeStampSource.h" -#include "VersionNumberSource.h" #include "PersistentDataStorage.h" namespace ChimeraTK { diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h deleted file mode 100644 index 6befd57..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/VersionNumber.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_H -#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_H - -#include - -namespace ChimeraTK { - - /** - * Class holding a version number together with a flag whether this version number is valid. - */ - class VersionNumber { - - public: - - typedef std::uint64_t UnderlyingDataType; - - /** Construct an invalid VersionNumber */ - VersionNumber() : _value(0), _isValid(false) {} - - /** Construct VersionNumber from a numeric value */ - VersionNumber(UnderlyingDataType value) : _value(value), _isValid(true) {} - - /** Copy constructor */ - VersionNumber(const VersionNumber &other) : _value(other._value), _isValid(other._isValid) {} - - /** Convert value of version number into uint64_t. Note that the result is undefined if the version number is not - * valid (i.e. if isValid() returns false). */ - operator UnderlyingDataType() const { - return _value; - } - - /** Check whether the version number is valid */ - bool isValid() const { - return _isValid; - } - - /** Assign number to the version number. This will intrinsically flag the version number to be valid. */ - VersionNumber& operator=(UnderlyingDataType value) { - _value = value; - _isValid = true; - return *this; - } - - /** Copy the full state of another VersionNumber object. */ - VersionNumber& operator=(const VersionNumber &other) { - _value = other._value; - _isValid = other._isValid; - return *this; - } - - private: - - UnderlyingDataType _value; - bool _isValid; - }; - -} // namespace ChimeraTK - -#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_H diff --git a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h b/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h deleted file mode 100644 index c4b6cfd..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/VersionNumberSource.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H -#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H - -#include -#include - -#include "VersionNumber.h" - -namespace ChimeraTK { - - /** - * Source generating version numbers. Version numbers are used to resolve - * competing updates that are applied to the same process variable. For - * example, it they can help in breaking an infinite update loop that might - * occur when two process variables are related and update each other. - * - * Each version number returned by the same source is unique, but different - * sources may return the same version numbers. - */ - class VersionNumberSource { - - public: - /** - * Shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Returns the next version number. The next version number is determined in - * an atomic way, so that it is guaranteed that this method never returns - * the same version number twice (unless the counter overflows, which is - * very unlikely). The first version number returned by this method is one. - * The version number that is returned is guaranteed to be greater than the - * version numbers returned for earlier calls to this method. This method - * may safely be called by any thread without any synchronization. - */ - inline VersionNumber nextVersionNumber() { - return ++_lastReturnedVersionNumber; - } - - private: - /** - * Last version number that was returned by a call to - * {@link nextVersionNumber()}. - */ - std::atomic _lastReturnedVersionNumber; - - }; - -} - -#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_VERSION_NUMBER_SOURCE_H From 393c83f94e1adc311dbe6f9eb2b9a2845407c81d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 15:24:37 +0200 Subject: [PATCH 207/786] recent change in DeviceAccess: VersionNumberSource is now static, no need to create instances any more --- .../ControlSystemAdapter/PVManager.h | 12 +---- .../ControlSystemAdapter/ProcessArray.h | 53 ++++--------------- src/PVManager.cc | 2 - tests/src/testProcessArray.cpp | 9 ++-- 4 files changed, 15 insertions(+), 61 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index bfef0dd..018c986 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -362,14 +362,6 @@ namespace ChimeraTK { }; - /** - * Version number source used for all process variables that are created by - * all process-variable managers. The source is shared by all PV managers so - * that version numbers work as expected even when data is passed between - * process variables handled by different PV managers. - */ - static VersionNumberSource::SharedPtr _versionNumberSource; - /** * Map storing the process variables. */ @@ -452,7 +444,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, unit, description, numberOfBuffers, maySendDestructively, timeStampSource, - _versionNumberSource, sendNotificationListener); + sendNotificationListener); _processVariables.insert( std::make_pair(processVariableName, @@ -490,7 +482,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, unit, description, numberOfBuffers, maySendDestructively, timeStampSource, - _versionNumberSource, sendNotificationListener); + sendNotificationListener); _processVariables.insert( std::make_pair(processVariableName, diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index a0621a2..35900f7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -74,8 +74,7 @@ namespace ChimeraTK { * send/receive process and are shared with the sender. */ ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, - const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, - VersionNumberSource::SharedPtr versionNumberSource); + const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers); /** * Creates a process array that acts as a sender. A sender is intended @@ -95,7 +94,6 @@ namespace ChimeraTK { */ ProcessArray(InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener, ProcessArray::SharedPtr receiver); @@ -364,12 +362,6 @@ namespace ChimeraTK { */ boost::shared_ptr _timeStampSource; - /** - * Version number source used for getting the next version number when one - * of the {@code set} methods is called without specifying a version number. - */ - boost::shared_ptr _versionNumberSource; - /** * Listener that is notified when the process variable is sent. */ @@ -453,8 +445,6 @@ namespace ChimeraTK { const std::string &description = "", T initialValue = 0, std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - VersionNumberSource::SharedPtr versionNumberSource = - VersionNumberSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr()); @@ -505,8 +495,6 @@ namespace ChimeraTK { const std::vector& initialValue, const std::string & name = "", const std::string &unit = "", const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - VersionNumberSource::SharedPtr versionNumberSource = - VersionNumberSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr()); @@ -516,15 +504,13 @@ namespace ChimeraTK { template ProcessArray::ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, - const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, - VersionNumberSource::SharedPtr versionNumberSource) + const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers) : mtca4u::NDRegisterAccessor(name, unit, description), _instanceType(instanceType), _vectorSize(initialValue.size()), _maySendDestructively(false), _sharedState(boost::make_shared(numberOfBuffers)), - _currentIndex(0), - _versionNumberSource(versionNumberSource) + _currentIndex(0) { // allocate and initialise buffer of the base class mtca4u::NDRegisterAccessor::buffer_2D.resize(1); @@ -566,7 +552,6 @@ namespace ChimeraTK { template ProcessArray::ProcessArray(InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener, ProcessArray::SharedPtr receiver) : mtca4u::NDRegisterAccessor(receiver->getName(), receiver->getUnit(), receiver->getDescription()), @@ -577,7 +562,6 @@ namespace ChimeraTK { _currentIndex(1), _receiver(receiver), _timeStampSource(timeStampSource), - _versionNumberSource(versionNumberSource), _sendNotificationListener(sendNotificationListener) { // It would be better to do the validation before initializing, but this @@ -672,7 +656,7 @@ namespace ChimeraTK { // We only use the incoming update if it has a higher version number // than the current value. This check is disabled when there is no // version number source. - if (!_versionNumberSource || (_sharedState->_buffers[nextIndex]).versionNumber > getVersionNumber()) { + if ((_sharedState->_buffers[nextIndex]).versionNumber > getVersionNumber()) { _sharedState->_emptyBufferQueue.push(_currentIndex); _currentIndex = nextIndex; return true; @@ -776,13 +760,7 @@ namespace ChimeraTK { template bool ProcessArray::write() { - VersionNumber newVersionNumber; - if (_versionNumberSource) { - newVersionNumber = _versionNumberSource->nextVersionNumber(); - } else { - newVersionNumber = 0; - } - return write(newVersionNumber); + return write(VersionNumberSource::nextVersionNumber()); } /*********************************************************************************************************************/ @@ -796,13 +774,7 @@ namespace ChimeraTK { template bool ProcessArray::writeDestructively() { - VersionNumber newVersionNumber; - if (_versionNumberSource) { - newVersionNumber = _versionNumberSource->nextVersionNumber(); - } else { - newVersionNumber = 0; - } - return writeDestructively(newVersionNumber); + return writeDestructively(VersionNumberSource::nextVersionNumber()); } /*********************************************************************************************************************/ @@ -926,16 +898,13 @@ namespace ChimeraTK { std::size_t size, const std::string & name, const std::string &unit, const std::string &description, T initialValue, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener) { typename boost::shared_ptr > receiver = boost::make_shared< ProcessArray >(ProcessArray::RECEIVER, name, unit, description, - std::vector(size, initialValue), numberOfBuffers, - versionNumberSource); + std::vector(size, initialValue), numberOfBuffers); typename ProcessArray::SharedPtr sender = boost::make_shared< ProcessArray >(ProcessArray::SENDER, maySendDestructively, - timeStampSource, versionNumberSource, sendNotificationListener, - receiver); + timeStampSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); } @@ -948,15 +917,13 @@ namespace ChimeraTK { const std::vector& initialValue, const std::string & name, const std::string &unit, const std::string &description, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - VersionNumberSource::SharedPtr versionNumberSource, ProcessVariableListener::SharedPtr sendNotificationListener) { typename boost::shared_ptr > receiver = boost::make_shared< ProcessArray >(ProcessArray::RECEIVER, name, unit, description, initialValue, - numberOfBuffers, versionNumberSource); + numberOfBuffers); typename ProcessArray::SharedPtr sender = boost::make_shared< ProcessArray >(ProcessArray::SENDER, maySendDestructively, - timeStampSource, versionNumberSource, sendNotificationListener, - receiver); + timeStampSource, sendNotificationListener, receiver); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(sender, receiver); } diff --git a/src/PVManager.cc b/src/PVManager.cc index e35b719..6023abc 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -10,8 +10,6 @@ namespace ChimeraTK { using boost::shared_ptr; using std::list; - VersionNumberSource::SharedPtr PVManager::_versionNumberSource = boost::make_shared(); - PVManager::PVManager() : _controlSystemNotificationQueue(0), _deviceNotificationQueue(0), _automaticReferenceTimeStampMode( true), _referenceTimeStamp(0) { diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 9bbe31b..6acee50 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -200,8 +200,7 @@ namespace ChimeraTK { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, - TimeStampSource::SharedPtr(), VersionNumberSource::SharedPtr(), - sendNotificationListener); + TimeStampSource::SharedPtr(), sendNotificationListener); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; BOOST_CHECK(sendNotificationListener->count == 0); @@ -303,12 +302,10 @@ namespace ChimeraTK { template void ProcessArrayTest::testVersionNumbers() { - VersionNumberSource::SharedPtr versionNumberSource = boost::make_shared< - VersionNumberSource>(); typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true, - TimeStampSource::SharedPtr(), versionNumberSource); + TimeStampSource::SharedPtr()); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; // Initially, the version number should be zero. @@ -333,7 +330,7 @@ namespace ChimeraTK { // When we explicitly use a greater version number, the receiver should be // updated again. sender->accessChannel(0)[0] = 3; - sender->writeDestructively(versionNumberSource->nextVersionNumber()); + sender->writeDestructively(VersionNumberSource::nextVersionNumber()); BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->accessChannel(0)[0] == 3); From b44ffc96c292f82aff1ba0ec092839818845998e Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 17:02:12 +0200 Subject: [PATCH 208/786] getVersionNumber() has been moved to DeviceAccess --- .../ControlSystemAdapter/ProcessArray.h | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 35900f7..3081971 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -15,7 +15,6 @@ #include #include -#include #include "ProcessVariableListener.h" #include "TimeStampSource.h" @@ -115,26 +114,7 @@ namespace ChimeraTK { return (_sharedState->_buffers[_currentIndex]).timeStamp; } - /** - * Returns the version number that is associated with the current value. - * This is the version number that was received with the readNonBlocking() - * operation that received the respective value or the last {@link write()} - * operation. If the last send operation was destructive, the version number - * (like the current value) is undefined. - * - * The version number is used to resolve conflicting updates. When an update - * is received using the readNonBlocking() method, it is only used if its - * value has a version number that is greater than the version number of the - * current value. Initially, each process variable has a version number of - * zero. - * - * When this process variable has not been initialized with a version number - * source, its version number always stays at zero and the version-number - * logic is disabled. - */ - VersionNumber getVersionNumber() const { /// @todo FIXME this function must be present in TransferElement already! - // On the other hand, this should not matter too much because the current - // value will be in an undefined state anyway and thus we might not care. + ChimeraTK::VersionNumber getVersionNumber() const override { return (_sharedState->_buffers[_currentIndex]).versionNumber; } From 481b387e57b861b9ae0c55a06af40faa066346d5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 24 Jul 2017 17:59:43 +0200 Subject: [PATCH 209/786] Update for latest DeviceAccess change: TransferElement::write() now has the version number as an optional argument. --- .../ControlSystemAdapter/ProcessArray.h | 71 +++++-------------- 1 file changed, 16 insertions(+), 55 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 3081971..9ae636c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -128,7 +128,7 @@ namespace ChimeraTK { void postRead() override; - bool write() override; + bool write(ChimeraTK::VersionNumber versionNumber={}) override; /** * Sends the current value to the receiver. Returns true if an @@ -140,20 +140,6 @@ namespace ChimeraTK { * has a value with a version number greater than or equal to the specified * version number, it silently discards this update. * - * Throws an exception if this process variable is not a sender. - */ - bool write(VersionNumber newVersionNumber); /// @todo FIXME this function must be present in TransferElement already! - - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * If this process variable has a version-number source, a new version - * number is retrieved from this source and used for the value being sent to - * the receiver. Otherwise, a version number of zero is used. - * * This version of the send operation moves the current value from the * sender to the receiver without copying it. This means that after calling * this method, the sender's value, time stamp, and version number are @@ -163,28 +149,7 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - bool writeDestructively(); /// @todo FIXME this function must be present in TransferElement already! - - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * The specified version number is passed to the receiver. If the receiver - * has a value with a version number greater than or equal to the specified - * version number, it silently discards this update. - * - * This version of the send operation moves the current value from the - * sender to the receiver without copying it. This means that after calling - * this method, the sender's value, time stamp, and version number are - * undefined. Therefore, this method must only be used if this process - * variable is not read (on the sender side) after sending it. - * - * Throws an exception if this process variable is not a sender or if this - * process variable does not allow destructive sending. - */ - bool writeDestructively(VersionNumber newVersionNumber); /// @todo FIXME this function must be present in TransferElement already! + bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}); /// @todo FIXME this function must be present in TransferElement already! const std::type_info& getValueType() const override { return typeid(T); @@ -735,37 +700,33 @@ namespace ChimeraTK { // swap data out of the queue buffer mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); } - -/*********************************************************************************************************************/ - template - bool ProcessArray::write() { - return write(VersionNumberSource::nextVersionNumber()); - } - /*********************************************************************************************************************/ template - bool ProcessArray::write(VersionNumber newVersionNumber) { - return writeInternal(newVersionNumber, true); + bool ProcessArray::write(ChimeraTK::VersionNumber versionNumber) { + if(versionNumber.isValid()) { + return writeInternal(versionNumber, true); + } + else { + return writeInternal(VersionNumberSource::nextVersionNumber(), true); + } } - -/*********************************************************************************************************************/ - template - bool ProcessArray::writeDestructively() { - return writeDestructively(VersionNumberSource::nextVersionNumber()); - } - /*********************************************************************************************************************/ template - bool ProcessArray::writeDestructively(VersionNumber newVersionNumber) { + bool ProcessArray::writeDestructively(ChimeraTK::VersionNumber versionNumber) { if (!_maySendDestructively) { throw std::runtime_error( "This process variable must not be sent destructively because the corresponding flag has not been set."); } - return writeInternal(newVersionNumber, false); + if(versionNumber.isValid()) { + return writeInternal(versionNumber, false); + } + else { + return writeInternal(VersionNumberSource::nextVersionNumber(), false); + } } /*********************************************************************************************************************/ From 06ebdb562bc17b516cf0d756169cedaa338461cb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 27 Jul 2017 18:26:07 +0200 Subject: [PATCH 210/786] Update necessary for changes in DeviceAccess: The TransferFuture now transports a data structure containing the version number. To implement this, the internal lockfree queue of the ProcessArray has been changed to contain futures of pointers to the buffer. The buffer class is now derived from the TransferFuture::Data class, so the futures in the lockfree queue are compatible with the furure expected by the TransferFuture class. Since only spsc_queues can store even shared_futures, the fullBufferQueue had to be changed into an spsc_queue. This implies a behavioural change: When the queue is full and data has to be thrown away, the ProcessArray can no longer throw away the oldest data, since it cannot pop from the spsc_queue. To make sure that the latest data can be kept, an internal atomic triple buffer has been introduced, which holds the latest data when the queue is full. Thus, even in the case of a buffer overflow, readLatest() will still return the latest value. --- .../ControlSystemAdapter/ProcessArray.h | 426 +++++++++--------- tests/src/testProcessArray.cpp | 52 ++- 2 files changed, 245 insertions(+), 233 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 9ae636c..ec27498 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -15,6 +15,7 @@ #include #include +#include #include "ProcessVariableListener.h" #include "TimeStampSource.h" @@ -111,11 +112,11 @@ namespace ChimeraTK { } TimeStamp getTimeStamp() const override { - return (_sharedState->_buffers[_currentIndex]).timeStamp; + return _currentIndex->timeStamp; } ChimeraTK::VersionNumber getVersionNumber() const override { - return (_sharedState->_buffers[_currentIndex]).versionNumber; + return _currentIndex->_versionNumber; } void doReadTransfer() override; @@ -184,22 +185,23 @@ namespace ChimeraTK { private: /** - * Type for the individual buffers. Each buffer stores a vector (wrapped - * in a Boost scoped pointer) and a time stamp. + * Type for the individual buffers. Each buffer stores a vector (wrapped in a Boost scoped pointer) and a time + * stamp. It extends the TransferFuture::Data type (which provides the version number) so it can be transported + * by a TransferFuture. */ - struct Buffer { + struct Buffer : public TransferFuture::Data { TimeStamp timeStamp; std::vector value; - VersionNumber versionNumber; + bool _isValid{false}; /** * Default constructor. Has to be defined explicitly because we have an * non-default copy constructor. */ - Buffer() : - versionNumber(0) { - } + Buffer() + : TransferFuture::Data({}) + {} /** * Copy constructor. Some STL implementations need the copy constructor @@ -212,10 +214,11 @@ namespace ChimeraTK { * to check that the copy constructor is never used once the buffer has * been initialized with a vector. */ - Buffer(Buffer const & other) : - timeStamp(other.timeStamp), value( - other.value ? new std::vector(*(other.value)) : 0), versionNumber( - other.versionNumber) { + Buffer(Buffer const & other) + : timeStamp(other.timeStamp), + value(other.value ? new std::vector(*(other.value)) : 0), + TransferFuture::Data(other._versionNumber) + { assert(!(other.value)); } @@ -243,8 +246,10 @@ namespace ChimeraTK { struct SharedState { SharedState(size_t numberOfBuffers) - : _buffers(numberOfBuffers + 2), - _fullBufferQueue(numberOfBuffers), + : _buffers(numberOfBuffers + 2 + 3), // two additional buffers owned by sender and receiver, 3 more additional + // buffers for the atomic triple buffer + _tripleBufferIndex(&(_buffers[3])), + _fullBufferQueue(numberOfBuffers+1), _emptyBufferQueue(numberOfBuffers) {} @@ -252,7 +257,15 @@ namespace ChimeraTK { * Buffers that hold the actual values. */ std::vector _buffers; - + + /** + * Special atomic triple buffer to be filled when the queue runs over (i.e. all normal buffers are full). + * After construction, buffer 2 is owned by the sender, 3 by the shared state and 4 by the receiver. A buffer is + * considered containing valid data if its version number is valid. Default constructed version numbers are + * invalid, so all buffers are flagged as invalid upon construction. + */ + std::atomic _tripleBufferIndex; + /** * Queue holding the indices of the full buffers. Those are the buffers * that have been sent but not yet received. We do not use an spsc_queue @@ -260,32 +273,30 @@ namespace ChimeraTK { * thread, so there are two threads which might consume elements and thus * an spsc_queue is not safe. */ - boost::lockfree::queue _fullBufferQueue; + //boost::lockfree::queue _fullBufferQueue; + boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; /** * Queue holding the empty buffers. Those are the buffers that have been * returned to the sender by the receiver. */ - boost::lockfree::spsc_queue _emptyBufferQueue; + boost::lockfree::spsc_queue _emptyBufferQueue; /** * Queue holding futures for notification. The receiver can obtain a future from this queue when the * _fullBufferQueue is empty and wait on it until new data has been sent. * - * It is sufficient to have a fixed queue length of 2, since it is only important to guarantee the presence of - * at least one unfulfilled future in the queue. - * * We are using a shared future, since the spsc_queue expects a copy-constructable data type. Since the data - * type void is not particularly expensive to copy, this should have no performance impact. + * type is not particularly expensive to copy, this should have no performance impact. */ - boost::lockfree::spsc_queue< boost::shared_future, boost::lockfree::capacity<2> > _notificationQueue; + //boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _notificationQueue; /** * The promise corresponding to the unfulfilled future in the _notificationQueue. * This promise is basically only used by the sender. Only in the destructor of the receiver it is used to * shutdown properly, thus it must be in the shared state. */ - boost::promise _notificationPromise; + TransferFuture::PromiseType _notificationPromise; }; boost::shared_ptr _sharedState; @@ -294,7 +305,12 @@ namespace ChimeraTK { * Index into the _buffers array that is currently owned by this instance * and used for read and (possibly) write operations. */ - std::size_t _currentIndex; + Buffer *_currentIndex; + + /** + * Index into the _tripleBuffer array that is currently owned by this instance. + */ + Buffer *_tripleBufferIndex; /** * Pointer to the receiver associated with this sender. This field is only @@ -321,12 +337,6 @@ namespace ChimeraTK { * Variable ID for the persistent data storage */ size_t _persistentDataStorageID{0}; - - /** - * Flag set in readAsync() whether doReadTransferNonBlocking() must or must not be called in postRead() after the - * future go tready. - */ - bool _asyncReadTransferNeeded{false}; /** * Internal implementation of the various {@code send} methods. All these @@ -455,7 +465,8 @@ namespace ChimeraTK { _vectorSize(initialValue.size()), _maySendDestructively(false), _sharedState(boost::make_shared(numberOfBuffers)), - _currentIndex(0) + _currentIndex(&(_sharedState->_buffers[0])), + _tripleBufferIndex(&(_sharedState->_buffers[4])) { // allocate and initialise buffer of the base class mtca4u::NDRegisterAccessor::buffer_2D.resize(1); @@ -463,7 +474,7 @@ namespace ChimeraTK { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != RECEIVER) { - throw std::invalid_argument("This constructor may only be used for a receiver process scalar."); + throw std::invalid_argument("This constructor may only be used for a receiver process variable."); } // We need at least two buffers for the queue (so four buffers in total) // in order to guarantee that we never have to block. @@ -477,18 +488,18 @@ namespace ChimeraTK { // at all times and thus only the rest remains for the queue. We use // this definition of the number of buffers to keep it consistent with // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - 2)) { + if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { throw std::invalid_argument("The number of buffers is too large."); } // We have to initialize the buffers by copying in the initial vectors. for (auto &i : _sharedState->_buffers) { i.value = initialValue; } - // The buffer with the index 0 is assigned to the receiver and the - // buffer with the index 1 is assigned to the sender. All buffers have - // to be added to the empty-buffer queue. - for (std::size_t i = 2; i < _sharedState->_buffers.size(); ++i) { - _sharedState->_emptyBufferQueue.push(i); + // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the + // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of + // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. + for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { + _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); } } @@ -504,7 +515,8 @@ namespace ChimeraTK { _vectorSize(receiver->_vectorSize), _maySendDestructively(maySendDestructively), _sharedState(receiver->_sharedState), - _currentIndex(1), + _currentIndex(&(_sharedState->_buffers[1])), + _tripleBufferIndex(&(_sharedState->_buffers[2])), _receiver(receiver), _timeStampSource(timeStampSource), _sendNotificationListener(sendNotificationListener) @@ -512,7 +524,7 @@ namespace ChimeraTK { // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (instanceType != SENDER) { - throw std::invalid_argument("This constructor may only be used for a sender process scalar."); + throw std::invalid_argument("This constructor may only be used for a sender process variable."); } if (!receiver) { throw std::invalid_argument("The pointer to the receiver must not be null."); @@ -525,7 +537,7 @@ namespace ChimeraTK { mtca4u::NDRegisterAccessor::buffer_2D.resize(1); mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; // put future into the notification queue - _sharedState->_notificationQueue.push(_sharedState->_notificationPromise.get_future()); + _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); } /*********************************************************************************************************************/ @@ -537,7 +549,7 @@ namespace ChimeraTK { if(this->readAsyncThread.joinable()) { this->readAsyncThread.interrupt(); try { - _sharedState->_notificationPromise.set_value(); + _sharedState->_notificationPromise.set_value(0); } catch(boost::promise_already_satisfied) { // ignore -> already notified @@ -551,67 +563,25 @@ namespace ChimeraTK { template void ProcessArray::doReadTransfer() { - // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is - // already fulfilled. Otherwise we might run out of futures on the notification queue. - if(mtca4u::TransferElement::hasActiveFuture) { - mtca4u::TransferElement::activeFuture.getBoostFuture().wait(); - mtca4u::TransferElement::hasActiveFuture = false; - if(!_asyncReadTransferNeeded) return; - _asyncReadTransferNeeded = false; - } - // Obtain futures from the notification queue and wait on them until we receive data. We start with checking - // for data before obtaining a future from the notification queue, since this is faster and the notification - // queue is shorter than the data queue. - while(!doReadTransferNonBlocking()) { - boost::shared_future future; - bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); - assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm - (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled - future.wait(); - boost::this_thread::interruption_point(); - } + + // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call + // TransferFuture::wait(). + readAsync().getBoostFuture().wait(); + boost::this_thread::interruption_point(); + } /*********************************************************************************************************************/ template bool ProcessArray::doReadTransferNonBlocking() { - if (_instanceType != RECEIVER) { - throw std::logic_error("Receive operation is only allowed for a receiver process variable."); - } - // If previously a TransferFuture has been requested by readAsync(), first make sure that that future is - // already fulfilled. Otherwise we might run out of futures on the notification queue. - if(mtca4u::TransferElement::hasActiveFuture) { - auto theFuture = mtca4u::TransferElement::activeFuture.getBoostFuture(); - auto status = theFuture.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) return false; - mtca4u::TransferElement::hasActiveFuture = false; - if(!_asyncReadTransferNeeded) return true; - _asyncReadTransferNeeded = false; - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the sender might get into - // trouble when it suddenly experiences a vector of the wrong size. - if (_sharedState->_buffers[_currentIndex].value.size() != _vectorSize) { - throw std::runtime_error("Cannot run receive operation because the size of the vector belonging to the current" - " buffer has been modified."); - } - std::size_t nextIndex; - if (_sharedState->_fullBufferQueue.pop(nextIndex)) { - // We only use the incoming update if it has a higher version number - // than the current value. This check is disabled when there is no - // version number source. - if ((_sharedState->_buffers[nextIndex]).versionNumber > getVersionNumber()) { - _sharedState->_emptyBufferQueue.push(_currentIndex); - _currentIndex = nextIndex; - return true; - } else { - _sharedState->_emptyBufferQueue.push(nextIndex); - return false; - } - } else { - return false; - } + + // Obtain future and check if a transfer is complete. + auto theFuture = readAsync().getBoostFuture(); + auto status = theFuture.wait_for(boost::chrono::duration(0)); + if(status == boost::future_status::timeout) return false; + return true; + } /*********************************************************************************************************************/ @@ -627,90 +597,121 @@ namespace ChimeraTK { template mtca4u::TransferFuture& ProcessArray::readAsync() { + if (_instanceType != RECEIVER) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } - ChimeraTK::ExperimentalFeatures::check("asynchronous read"); - if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; // the last future given out by this fuction is still active - // check if we already have data - boost::promise promise; - boost::shared_future readyFuture = promise.get_future().share(); - promise.set_value(); - if(doReadTransferNonBlocking()) { - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - _asyncReadTransferNeeded = false; - return mtca4u::TransferElement::activeFuture; - } - else { - boost::shared_future future; - // obtain future from queue - bool atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); - assert(atLeastOneFuturePresent); // if this is not true, there is something wrong with the algorithm - (void)atLeastOneFuturePresent; // prevent warning in case asserts are disabled - // if this future is already fulfilled, obtain the next one - if(future.is_ready()) { - // before we can obtain the next future, we must check for new data once more - if(doReadTransferNonBlocking()) { - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(readyFuture, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - _asyncReadTransferNeeded = false; - return mtca4u::TransferElement::activeFuture; - } - atLeastOneFuturePresent = _sharedState->_notificationQueue.pop(future); - assert(atLeastOneFuturePresent); + // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. + // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this + // function even if the future has not yet been used still result in the correct behaviour. + assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic + TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); + + // if the future blocks, check for data in the tripple buffer + auto status = future.wait_for(boost::chrono::duration(0)); + if(status == boost::future_status::timeout) { + // if local buffer of triple buffer is invalid, swap with shared buffer first + if(!(_tripleBufferIndex->_isValid)) { + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + } + // if now the local buffer contains valid data, use that data first + if(_tripleBufferIndex->_isValid) { + future = boost::make_ready_future(static_cast(_tripleBufferIndex)).share(); + + auto status2 = future.wait_for(boost::chrono::duration(0)); + assert(status2 != boost::future_status::timeout); } - // return the future - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - _asyncReadTransferNeeded = true; - return mtca4u::TransferElement::activeFuture; } + + // return the future + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + return mtca4u::TransferElement::activeFuture; + } /*********************************************************************************************************************/ template void ProcessArray::postRead() { - // if this is called inside mtca4u::TransferFuture::wait(), we may still need to obtain the new data from the queue - if(_asyncReadTransferNeeded) { - _asyncReadTransferNeeded = false; - mtca4u::TransferElement::hasActiveFuture = false; // prevent doReadTransferNonBlocking() from checking the future only - bool hasNewDataAfterFuture = doReadTransferNonBlocking(); - if(!hasNewDataAfterFuture) { - // we did not receive new data despite being notified about new data... - // this either means there is a bug in the ControlSystemAdapter, or the application has called postRead() from - // a different thread while mtca4u::TransferFuture::wait() is still waiting. This is in principle violating - // the interface definition, but can be safe under certain circumstances (e.g. in ApplicationCore's testable - // mode, when a stall is detected and debugging information should be printed). Thus we do not raise an - // assertion but throw an exception which can be caught when this is known to be safe. - - /// @todo Change this back to an exception, this busy-waiting loop is only a temporary work around until the - /// problem has been fully understood and solved. - /* throw std::logic_error("postRead() called despite no new data has arrived. Are you reading the same " - "ProcessArray from different threads? Variable name: "+this->getName()); */ - std::cerr << "*** ERROR: ProcessArrya::postRead() called despite no new data has arrived. Are you reading the " - "same ProcessArray from different threads? Variable name: "+this->getName() << std::endl; - std::cerr << " Using 1ms polling interval to wait for actual new data." << std::endl; - while(!doReadTransferNonBlocking()) usleep(1000); - std::cerr << " New data has arrived for " << this->getName() << ". Busy waiting stopped." << std::endl; + mtca4u::TransferElement::hasActiveFuture = false; + + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the sender might get into + // trouble when it suddenly experiences a vector of the wrong size. + if(_currentIndex->value.size() != _vectorSize) { + throw std::runtime_error("Cannot run read operation because the size of the vector belonging to the current" + " buffer has been modified."); + } + + // If local buffer of triple buffer is invalid, swap with shared buffer first + if(!(_tripleBufferIndex->_isValid)) { + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + } + + // Determine version number of data in the triple buffer + bool tripleBufferHasData = _tripleBufferIndex->_isValid; + + // Determine version number of data in the queue + assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic + TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); + auto status = future.wait_for(boost::chrono::duration(0)); + bool queueHasData = (status != boost::future_status::timeout); + + // If postRead() has been called, there should be new data somewhere + assert(queueHasData || tripleBufferHasData); + + // Determine whether to use the data from the queue or from the triple buffer. If in both places data is present, + // use the older data (by the version number). + bool useTripleBuffer; + if(queueHasData && !tripleBufferHasData) { + // only data in queue present + useTripleBuffer = false; + } + else if(!queueHasData && tripleBufferHasData) { + // only data in atomic triple buffer present + useTripleBuffer = true; + } + else { // queueHasData && tripleBufferHasData -> see the assert above + auto tripleBufferVersion = _tripleBufferIndex->_versionNumber; + VersionNumber queueVersion = future.get()->_versionNumber; + if(tripleBufferVersion < queueVersion) { + useTripleBuffer = true; + } + else { + useTripleBuffer = false; } } + + // perform the determined action + if(!useTripleBuffer) { + // remove future from queue, since we only obtained it through front() so far + _sharedState->_fullBufferQueue.pop(); + // get buffer from future + TransferFuture::Data *nextBuffer = future.get(); + // put old buffer back on empty buffer queue and update current index + _sharedState->_emptyBufferQueue.push(_currentIndex); + _currentIndex = static_cast(nextBuffer); // static_cast should be ok, since we control the queue + } + else { + Buffer *nextBuffer = _tripleBufferIndex; + // put old buffer back into triple buffer (after invalidating int) and update current index + _currentIndex->_isValid = false; + _tripleBufferIndex = _currentIndex; + _currentIndex = nextBuffer; + } + // swap data out of the queue buffer - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( (_sharedState->_buffers[_currentIndex]).value ); + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _currentIndex->value ); + } /*********************************************************************************************************************/ template bool ProcessArray::write(ChimeraTK::VersionNumber versionNumber) { - if(versionNumber.isValid()) { - return writeInternal(versionNumber, true); - } - else { - return writeInternal(VersionNumberSource::nextVersionNumber(), true); - } + return writeInternal(versionNumber, true); } /*********************************************************************************************************************/ @@ -721,12 +722,7 @@ namespace ChimeraTK { throw std::runtime_error( "This process variable must not be sent destructively because the corresponding flag has not been set."); } - if(versionNumber.isValid()) { - return writeInternal(versionNumber, false); - } - else { - return writeInternal(VersionNumberSource::nextVersionNumber(), false); - } + return writeInternal(versionNumber, false); } /*********************************************************************************************************************/ @@ -749,82 +745,78 @@ namespace ChimeraTK { template bool ProcessArray::writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { - if (_instanceType != SENDER) { + if(_instanceType != SENDER) { throw std::logic_error( "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); } // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. - if (mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { + if(mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { throw std::runtime_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been " "modified. Variable name: "+this->getName()); } + // First update the persistent data storage, if any was associated. This cannot be done after sending, since the // value might no longer be available within this instance. - if (_persistentDataStorage) { + if(_persistentDataStorage) { _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); } - // Before sending the value, we have to update the associated - // time-stamp. - TimeStamp newTimeStamp = - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : - TimeStamp::currentTime(); - (_sharedState->_buffers[_currentIndex]).timeStamp = newTimeStamp; - (_sharedState->_buffers[_currentIndex]).versionNumber = newVersionNumber; - if (shouldCopy) { - _sharedState->_buffers[_currentIndex].value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + // Before sending the value, we have to set the associated time-stamp. + TimeStamp newTimeStamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); + _currentIndex->timeStamp = newTimeStamp; + + // set the version number + _currentIndex->_versionNumber = newVersionNumber; + + // flag current data as valid + _currentIndex->_isValid = true; + + // set the data by copying or swapping + if(shouldCopy) { + _currentIndex->value = mtca4u::NDRegisterAccessor::buffer_2D[0]; } else { - _sharedState->_buffers[_currentIndex].value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + _currentIndex->value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); } - std::size_t nextIndex; + // send the data to the queue by first obtaining a new empty buffer from the empty buffer queue + Buffer *nextIndex; bool lostData = false; - if (_sharedState->_emptyBufferQueue.pop(nextIndex)) { - _sharedState->_fullBufferQueue.push(_currentIndex); - } else { - // We can still send, but we will lose older data. - if (_sharedState->_fullBufferQueue.pop(nextIndex)) { - _sharedState->_fullBufferQueue.push(_currentIndex); + if(_sharedState->_emptyBufferQueue.pop(nextIndex)) { + + // Create a new promise first and push its future to the full buffer queue before fulfilling the current promise. + // This makes sure that always at least one unfulfilled future is available for the receier to wait on. + TransferFuture::PromiseType nextPromise; + _sharedState->_fullBufferQueue.push(nextPromise.get_future()); + _sharedState->_notificationPromise.set_value(_currentIndex); + _sharedState->_notificationPromise = std::move(nextPromise); + } + else { + // No empty buffer available: write to special atomic triple buffer + nextIndex = _tripleBufferIndex; + _tripleBufferIndex = _currentIndex; + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + // if the buffer swapped out of the shared state contained valid data, we have now lost that data + if(_tripleBufferIndex->_isValid) { lostData = true; - } else { - // It is possible, that we did not find an empty buffer but before - // we could get a full buffer, the receiver processed all full - // buffers. In this case, the queue of empty buffers cannot be empty - // any longer because we have at least four buffers. - if (!_sharedState->_emptyBufferQueue.pop(nextIndex)) { - // This should never happen and is just an assertion for extra - // safety. - throw std::runtime_error( - "Assertion that empty-buffer queue has at least one element failed."); - } - _sharedState->_fullBufferQueue.push(_currentIndex); + // mark as invalid so we can always just swap it with the shared state + _tripleBufferIndex->_isValid = false; } } - if (shouldCopy) { - _sharedState->_buffers[nextIndex].timeStamp = newTimeStamp; - _sharedState->_buffers[nextIndex].versionNumber = newVersionNumber; + + // change current index to the new empty buffer + if(shouldCopy) { + nextIndex->timeStamp = newTimeStamp; + nextIndex->_versionNumber = newVersionNumber; } _currentIndex = nextIndex; - if (_sendNotificationListener) { + + // notify send notification listener, if present + if(_sendNotificationListener) { _sendNotificationListener->notify(_receiver); } - // Notify the receiver through the notification queue. It must be guaranteed that there is at least one - // unfulfilled future in the notification queue at all times, so we try pushing a new one first. If this - // failes, there is already one unfulfilled and one fulfilled future in the queue, so we are done. - boost::promise nextPromise; - if(_sharedState->_notificationQueue.push(nextPromise.get_future())) { - try { - _sharedState->_notificationPromise.set_value(); - } - catch(boost::promise_already_satisfied) { - // ignore -> this may happen only during shutdown - } - _sharedState->_notificationPromise = std::move(nextPromise); - } return lostData; } diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 6acee50..6a676f1 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -240,12 +240,15 @@ namespace ChimeraTK { createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - // If we send two values consecutively, they both should be received because - // the number of buffers is two. + + // If we send three values consecutively, they all should be received because the queue length is two and there is + // the additional atomic triple buffer holding a third value sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER); sender->writeDestructively(); sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 1); sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 2); + sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { @@ -256,16 +259,24 @@ namespace ChimeraTK { i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 2); + } // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->readNonBlocking()); - // Now we try to send three values consecutively. This should result in the - // first value being dropped. - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 2); - sender->writeDestructively(); + + // Now we try to send four values consecutively. This should result in the last but one value being dropped. The + // latest value should be preserved, since it is in the atomic triple buffer sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 3); sender->writeDestructively(); sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 4); sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); + sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 6); + sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { @@ -276,8 +287,14 @@ namespace ChimeraTK { i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 4); } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 6); + } // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->readNonBlocking()); + // When we send non-destructively, the value should also be preserved on the // sender side. sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); @@ -291,6 +308,7 @@ namespace ChimeraTK { i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 5); } + // Calling writeDestructively() on a sender that has not the corresponding // flag set should result in an exception. senderReceiver = @@ -308,9 +326,6 @@ namespace ChimeraTK { TimeStampSource::SharedPtr()); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - // Initially, the version number should be zero. - BOOST_CHECK(sender->getVersionNumber() == 0); - BOOST_CHECK(receiver->getVersionNumber() == 0); // After sending destructively and receiving a value, the version number on // the receiver should be greater. VersionNumber initialVersionNumber = receiver->getVersionNumber(); @@ -320,29 +335,34 @@ namespace ChimeraTK { VersionNumber versionNumber = receiver->getVersionNumber(); BOOST_CHECK(versionNumber > initialVersionNumber); BOOST_CHECK(receiver->accessChannel(0)[0] == 1); - // When we send again specifying the same version number, there should be no - // update of the receiver. + // When we send again specifying the same version number, there should still be the update of the receiver. sender->accessChannel(0)[0] = 2; sender->writeDestructively(versionNumber); - BOOST_CHECK(!receiver->readNonBlocking()); + BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(versionNumber == receiver->getVersionNumber()); - BOOST_CHECK(receiver->accessChannel(0)[0] == 1); + BOOST_CHECK(receiver->accessChannel(0)[0] == 2); // When we explicitly use a greater version number, the receiver should be // updated again. sender->accessChannel(0)[0] = 3; - sender->writeDestructively(VersionNumberSource::nextVersionNumber()); + sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->accessChannel(0)[0] == 3); + // Even when sending again an update with an older version number, the update should be seen by the receiver. + sender->accessChannel(0)[0] = 4; + sender->writeDestructively(versionNumber); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(versionNumber == receiver->getVersionNumber()); + BOOST_CHECK(receiver->accessChannel(0)[0] == 4); // When we send non-destructively, the version number on the sender and the // receiver should match after sending and receiving. versionNumber = receiver->getVersionNumber(); - sender->accessChannel(0)[0] = 4; + sender->accessChannel(0)[0] = 5; sender->write(); BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); - BOOST_CHECK(receiver->accessChannel(0)[0] == 4); + BOOST_CHECK(receiver->accessChannel(0)[0] == 5); } template From 0049b1d0ca6ffb466f5d6cb212c21417ed925f8f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 28 Jul 2017 17:48:23 +0200 Subject: [PATCH 211/786] fixed a logic error in the readLatest() function, but there are still errors left! --- .../ControlSystemAdapter/ProcessArray.h | 16 ++++++--- tests/src/testProcessArray.cpp | 35 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index ec27498..5721c2d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -580,6 +580,7 @@ namespace ChimeraTK { auto theFuture = readAsync().getBoostFuture(); auto status = theFuture.wait_for(boost::chrono::duration(0)); if(status == boost::future_status::timeout) return false; + return true; } @@ -588,9 +589,11 @@ namespace ChimeraTK { template bool ProcessArray::doReadTransferLatest() { - bool hasRead = false; - while(doReadTransferNonBlocking()) hasRead = true; - return hasRead; + // as long as there is more than one element on the queue, discard it + while(_sharedState->_fullBufferQueue.read_available() > 1) { + _sharedState->_fullBufferQueue.pop(); + } + return this->doReadTransferNonBlocking(); } /*********************************************************************************************************************/ @@ -598,6 +601,11 @@ namespace ChimeraTK { template mtca4u::TransferFuture& ProcessArray::readAsync() { + // If we already have a still-active future, we can return it directly. It is not possible that this future is + // blocking and there is valid data to be read in the atomic triple buffer. + // This is only a preformance optimisation, we would generate the same future again. + if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; + if (_instanceType != RECEIVER) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } @@ -623,7 +631,7 @@ namespace ChimeraTK { assert(status2 != boost::future_status::timeout); } } - + // return the future mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 6a676f1..ec7b15d 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -29,6 +29,7 @@ namespace ChimeraTK { static void testSynchronization(); static void testVersionNumbers(); static void testBlockingRead(); + static void testReadLatest(); private: static size_t const N_ELEMENTS = 12; @@ -57,6 +58,7 @@ namespace ChimeraTK { add(BOOST_TEST_CASE(&ProcessArrayTest::testSynchronization)); add(BOOST_TEST_CASE(&ProcessArrayTest::testVersionNumbers)); add(BOOST_TEST_CASE(&ProcessArrayTest::testBlockingRead)); + add(BOOST_TEST_CASE(&ProcessArrayTest::testReadLatest)); } }; @@ -403,6 +405,39 @@ namespace ChimeraTK { backgroundTask.join(); } } +/* + template + void ProcessArrayTest::testReadLatest() { + std::cout << "testReadLatest 1" << std::endl; + auto senderReceiver = createSynchronizedProcessArray(1); + auto sender = senderReceiver.first; + auto receiver = senderReceiver.second; + + // readLatest with only one element in the queue should read that element + sender->accessData(0) = 42; + sender->write(); + std::cout << "testReadLatest 2" << std::endl; + BOOST_CHECK(receiver->readLatest()); + std::cout << "testReadLatest 3" << std::endl; + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + + // readLatest with no element in the queue will return false and not change the value + BOOST_CHECK(!receiver->readLatest()); + std::cout << "testReadLatest 4" << std::endl; + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + + // readLatest with two elements (queue is full) should return the second element + sender->accessData(0) = 66; + sender->write(); + sender->accessData(0) = 77; + sender->write(); + std::cout << "testReadLatest 5" << std::endl; + BOOST_CHECK(receiver->readLatest()); + std::cout << "testReadLatest 6" << std::endl; + BOOST_CHECK_EQUAL(receiver->accessData(0), 77); + std::cout << "testReadLatest 7" << std::endl; + } +*/ } //namespace ChimeraTK test_suite* From bb567ddee6fe52103d95d47424077a02f30272bd Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 31 Jul 2017 17:10:33 +0200 Subject: [PATCH 212/786] - Enabled testReadLatest, which was missing and making the compilation fail. - Fixed logic bug in doReadTransferLatest. --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 6 ++++-- tests/src/testProcessArray.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 5721c2d..e118cd5 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -589,8 +589,10 @@ namespace ChimeraTK { template bool ProcessArray::doReadTransferLatest() { - // as long as there is more than one element on the queue, discard it - while(_sharedState->_fullBufferQueue.read_available() > 1) { + // As long as there is more than one valid element on the queue, discard it. + // Due to our implementation there is always one unfulfilled future in the queue, so + // we must pop until there are two elements left in order not to flush out the newest valid value. + while(_sharedState->_fullBufferQueue.read_available() > 2) { _sharedState->_fullBufferQueue.pop(); } return this->doReadTransferNonBlocking(); diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index ec7b15d..a16ad77 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -405,7 +405,7 @@ namespace ChimeraTK { backgroundTask.join(); } } -/* + template void ProcessArrayTest::testReadLatest() { std::cout << "testReadLatest 1" << std::endl; @@ -437,7 +437,7 @@ namespace ChimeraTK { BOOST_CHECK_EQUAL(receiver->accessData(0), 77); std::cout << "testReadLatest 7" << std::endl; } -*/ + } //namespace ChimeraTK test_suite* From 53e5eaca6dc614c3e941af008a2b089bfb7ee0a1 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 09:38:25 +0200 Subject: [PATCH 213/786] changed minor version number because the interface is going to change --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c89447..4ce7747 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 05) -set(${PROJECT_NAME}_PATCH_VERSION 01) +set(${PROJECT_NAME}_MINOR_VERSION 06) +set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From 144eebbf66ad313cf98bd78392dfb43428e73c16 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 10:55:56 +0200 Subject: [PATCH 214/786] changed PVManager to use RegisterPath instead of string, which ensures a / at the beginning. Compiles, but breaks the tests. --- .../ControlSystemAdapter/PVManager.h | 20 ++++++++++--------- src/PVManager.cc | 7 ++++++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 018c986..8b22585 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -8,9 +8,10 @@ #include #include #include -#include +#include #include "ProcessArray.h" +#include #include "PVManagerDecl.h" @@ -56,8 +57,9 @@ namespace ChimeraTK { /** * Type alias for the process variable map. Useful for getting related types * (e.g. an iterator). + * @todo FIXME: This was an unordered_map of strings, but it was changed to register path. The register path does not have a hash, thus we fall back to std::map. Write a std::hash if you cant to improve the performance. */ - typedef boost::unordered_map ProcessVariableMap; + typedef std::map ProcessVariableMap; /** * Creates a new process array for transferring data from the device library @@ -88,7 +90,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( - const std::string& processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 2); @@ -121,7 +123,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( - const std::string& processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 2); @@ -141,7 +143,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> getProcessArray( - const std::string& processVariableName) const; + mtca4u::RegisterPath processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier @@ -155,7 +157,7 @@ namespace ChimeraTK { * device library. */ std::pair getProcessVariable( - const std::string& processVariableName) const; + mtca4u::RegisterPath processVariableName) const; /** * Returns the map containing all process variables, using the names as @@ -424,7 +426,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( - const std::string& processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) @@ -461,7 +463,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( - const std::string& processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) @@ -499,7 +501,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::getProcessArray( - const std::string& processVariableName) const { + mtca4u::RegisterPath processVariableName) const { ProcessVariableSharedPtrPair processVariable = getProcessVariable( processVariableName); if (processVariable.first && processVariable.second) { diff --git a/src/PVManager.cc b/src/PVManager.cc index 6023abc..3b9e680 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -13,14 +13,19 @@ namespace ChimeraTK { PVManager::PVManager() : _controlSystemNotificationQueue(0), _deviceNotificationQueue(0), _automaticReferenceTimeStampMode( true), _referenceTimeStamp(0) { + /// @todo FIXME I keep this part of the code commented out, although it is not the best programming style. + /// This performance optimisation has been turned off when changing the map key from string to RegisterPath, which does not have a hash function. + /// We had to change to std::map, which does not have the load factor. To re-enable the performance optimisation write a std::hash for RegisterPath. + /* // We choose the load factor very small. This will increase the memory // footprint, however because of frequent lookups we want to get the // performance as close to O(1) as reasonably possible. _processVariables.max_load_factor(0.5); + */ } std::pair PVManager::getProcessVariable( - const std::string& processVariableName) const { + mtca4u::RegisterPath processVariableName) const { ProcessVariableMap::const_iterator i = _processVariables.find( processVariableName); if (i != _processVariables.end()) { From ad5c865f0310802eed6b281d305ea0df0fec8ed1 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 11:10:03 +0200 Subject: [PATCH 215/786] adapted test for PVManager to the changed behaviour. --- tests/src/testPVManager.cpp | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 95e18b8..37b790c 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -54,35 +54,37 @@ static void testCreateProcessVariables(const string& name, shared_ptr > createdPV = devManager->createProcessArray( deviceToControlSystem, name + "In", 1, "kindOfAUnit","any description"); - BOOST_CHECK(createdPV->getName() == name + "In"); + // Although process variables are/ can be created without a leading '/', the + // RegisterPath which is used assures that it is there. We have to test with '/'. + BOOST_CHECK(createdPV->getName() == "/"+name + "In"); BOOST_CHECK(createdPV->getUnit() == "kindOfAUnit"); BOOST_CHECK(createdPV->getDescription() == "any description"); shared_ptr > devPV = devManager->getProcessArray( name + "In"); - BOOST_CHECK(devPV->getName() == name + "In"); + BOOST_CHECK(devPV->getName() == "/"+name + "In"); BOOST_CHECK(devPV->getUnit() == "kindOfAUnit"); BOOST_CHECK(devPV->getDescription() == "any description"); shared_ptr > csPV = csManager->getProcessArray( name + "In"); - BOOST_CHECK(csPV->getName() == name + "In"); + BOOST_CHECK(csPV->getName() == "/"+name + "In"); BOOST_CHECK(csPV->getUnit() == "kindOfAUnit"); BOOST_CHECK(csPV->getDescription() == "any description"); createdPV = devManager->createProcessArray(controlSystemToDevice, name + "Out", 1, "anotherUnit","something"); - BOOST_CHECK(createdPV->getName() == name + "Out"); + BOOST_CHECK(createdPV->getName() == "/"+ name + "Out"); BOOST_CHECK(createdPV->getUnit() == "anotherUnit"); BOOST_CHECK(createdPV->getDescription() == "something"); devPV = devManager->getProcessArray(name + "Out"); - BOOST_CHECK(devPV->getName() == name + "Out"); + BOOST_CHECK(devPV->getName() == "/" + name + "Out"); BOOST_CHECK(devPV->getUnit() == "anotherUnit"); BOOST_CHECK(devPV->getDescription() == "something"); csPV = csManager->getProcessArray(name + "Out"); - BOOST_CHECK(csPV->getName() == name + "Out"); + BOOST_CHECK(csPV->getName() == "/" + name + "Out"); BOOST_CHECK(csPV->getUnit() == "anotherUnit"); BOOST_CHECK(csPV->getDescription() == "something"); @@ -93,23 +95,22 @@ static void testCreateProcessVariables(const string& name, string arrayName = name + "Array"; shared_ptr > createdPA = devManager->createProcessArray( deviceToControlSystem, arrayName + "In", 5); - BOOST_CHECK(createdPA->getName() == arrayName + "In"); + BOOST_CHECK(createdPA->getName() == "/" + arrayName + "In"); BOOST_CHECK(createdPA->getUnit() == "n./a."); BOOST_CHECK(createdPA->getDescription() == ""); - BOOST_CHECK(createdPA->getName() == arrayName + "In"); shared_ptr > devPA = devManager->getProcessArray( arrayName + "In"); - BOOST_CHECK(devPA->getName() == arrayName + "In"); + BOOST_CHECK(devPA->getName() == "/" + arrayName + "In"); shared_ptr > csPA = csManager->getProcessArray( arrayName + "In"); - BOOST_CHECK(csPA->getName() == arrayName + "In"); + BOOST_CHECK(csPA->getName() == "/" + arrayName + "In"); createdPA = devManager->createProcessArray(controlSystemToDevice, arrayName + "Out", 5); - BOOST_CHECK(createdPA->getName() == arrayName + "Out"); + BOOST_CHECK(createdPA->getName() == "/" + arrayName + "Out"); devPA = devManager->getProcessArray(arrayName + "Out"); - BOOST_CHECK(devPA->getName() == arrayName + "Out"); + BOOST_CHECK(devPA->getName() == "/" + arrayName + "Out"); csPA = csManager->getProcessArray(arrayName + "Out"); - BOOST_CHECK(csPA->getName() == arrayName + "Out"); + BOOST_CHECK(csPA->getName() == "/" + arrayName + "Out"); BOOST_CHECK_THROW( devManager->createProcessArray( static_cast(-1),arrayName + "ShouldFail", 5), @@ -200,21 +201,21 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) i != processVariables.end(); i++) { T pv = *i; const std::string& name = pv->getName(); - if (name == "double") { + if (name == "/double") { BOOST_CHECK(pv->getValueType() == typeid(double)); auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); BOOST_CHECK(pvc->isArray() == true); BOOST_CHECK(pvc->getNumberOfSamples() == 1); BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundDouble = true; - } else if (name == "int32") { + } else if (name == "/int32") { BOOST_CHECK(pv->getValueType() == typeid(int32_t)); auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); BOOST_CHECK(pvc->isArray() == true); BOOST_CHECK(pvc->getNumberOfSamples() == 1); BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundInt32 = true; - } else if (name == "floatArray") { + } else if (name == "/floatArray") { BOOST_CHECK(pv->getValueType() == typeid(float)); auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); BOOST_CHECK(pvc->isArray() == true); @@ -236,21 +237,21 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) i != processVariables.end(); i++) { T pv = *i; const std::string& name = pv->getName(); - if (name == "double") { + if (name == "/double") { BOOST_CHECK(pv->getValueType() == typeid(double)); auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); BOOST_CHECK(pvc->isArray() == true); BOOST_CHECK(pvc->getNumberOfSamples() == 1); BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundDouble = true; - } else if (name == "int32") { + } else if (name == "/int32") { BOOST_CHECK(pv->getValueType() == typeid(int32_t)); auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); BOOST_CHECK(pvc->isArray() == true); BOOST_CHECK(pvc->getNumberOfSamples() == 1); BOOST_CHECK(pvc->getNumberOfChannels() == 1); foundInt32 = true; - } else if (name == "floatArray") { + } else if (name == "/floatArray") { BOOST_CHECK(pv->isArray() == true); boost::dynamic_pointer_cast, typename T::element_type>(pv); auto pvc = boost::dynamic_pointer_cast, typename T::element_type>(pv); @@ -478,9 +479,9 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) ProcessVariable::SharedPtr pv = pvManager->nextNotification(); if (pv) { const std::string& name = pv->getName(); - if (name == "int32In") { + if (name == "/int32In") { ++int32NotificationCount; - } else if (name == "doubleIn") { + } else if (name == "/doubleIn") { ++doubleNotificationCount; } else { BOOST_FAIL("Unexpected notification."); @@ -517,9 +518,9 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) ProcessVariable::SharedPtr pv = pvManager->nextNotification(); if (pv) { const std::string& name = pv->getName(); - if (name == "int32Out") { + if (name == "/int32Out") { ++int32NotificationCount; - } else if (name == "doubleOut") { + } else if (name == "/doubleOut") { ++doubleNotificationCount; } else { BOOST_FAIL("Unexpected notification."); From eda5f4a3d82208acefab9aaa065fe087564e7314 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 13:22:44 +0200 Subject: [PATCH 216/786] changed interface of ControlsystemSyncUtil to use RegisterPath instead of string --- .../ControlSystemSynchronizationUtility.h | 14 +++++++------- src/ControlSystemSynchronizationUtility.cc | 7 +++---- .../testControlSystemSynchronizationUtility.cpp | 5 +++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index 88928f9..177f53b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_CONTROL_SYSTEM_SYNCHRONIZATION_UTILITY -#include +#include #include "ControlSystemPVManager.h" @@ -31,18 +31,18 @@ namespace ChimeraTK { * If another listener has previously been registered for the named process * variable, that listener is replaced with the new listener. */ - void addReceiveNotificationListener(std::string const & processVariableName, + void addReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener); /** * Removes a receive notification listener that has previously been * registered with - * {@link #addReceiveNotificationListener(std::string const &, ProcessVariableListener::SharedPtr)}. + * {@link #addReceiveNotificationListener(mtca4u::RegisterPath const &, ProcessVariableListener::SharedPtr)}. * If no listener is registered for the specified process variable name, * this method does nothing. */ void removeReceiveNotificationListener( - std::string const & processVariableName); + mtca4u::RegisterPath const & processVariableName); /** * Runs receive on all process variables that have data pending. This method @@ -124,7 +124,8 @@ namespace ChimeraTK { * Map storing the receive listeners. The maps uses the process variable * as the key and the corresponding receive listener as the value. */ - boost::unordered_map _receiveNotificationListeners; + std::unordered_map _receiveNotificationListeners; + // std::map _receiveNotificationListeners; // Disable copy construction and assignment. ControlSystemSynchronizationUtility( @@ -143,8 +144,7 @@ namespace ChimeraTK { T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { if ((*i)->readNonBlocking()) { - boost::unordered_map::iterator listenerIterator = - _receiveNotificationListeners.find((*i)->getName()); + auto listenerIterator = _receiveNotificationListeners.find((*i)->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index 9fbab1c..9a376b1 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -14,7 +14,7 @@ namespace ChimeraTK { } void ControlSystemSynchronizationUtility::addReceiveNotificationListener( - std::string const & processVariableName, + mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { _receiveNotificationListeners.erase(processVariableName); _receiveNotificationListeners.insert( @@ -22,7 +22,7 @@ namespace ChimeraTK { } void ControlSystemSynchronizationUtility::removeReceiveNotificationListener( - std::string const & processVariableName) { + mtca4u::RegisterPath const & processVariableName) { _receiveNotificationListeners.erase(processVariableName); } @@ -30,8 +30,7 @@ namespace ChimeraTK { ProcessVariable::SharedPtr pv; while ((pv = _pvManager->nextNotification())) { if (pv->readNonBlocking()) { - boost::unordered_map::iterator listenerIterator = - _receiveNotificationListeners.find(pv->getName()); + auto listenerIterator = _receiveNotificationListeners.find(pv->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); diff --git a/tests/src/testControlSystemSynchronizationUtility.cpp b/tests/src/testControlSystemSynchronizationUtility.cpp index ff27c8a..30c851d 100644 --- a/tests/src/testControlSystemSynchronizationUtility.cpp +++ b/tests/src/testControlSystemSynchronizationUtility.cpp @@ -241,8 +241,9 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) devFloatIn->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 1); + // the pv name has a '/' because it is a register path BOOST_CHECK( - receiveNotificationListener->lastProcessVariable->getName() == "intIn"); + receiveNotificationListener->lastProcessVariable->getName() == "/intIn"); devIntIn->write(); std::vector pvList(1); @@ -250,7 +251,7 @@ BOOST_AUTO_TEST_SUITE( ControlSystemSynchronizationUtilityTestSuite ) syncUtil.receive(pvList); BOOST_CHECK(receiveNotificationListener->count == 2); BOOST_CHECK( - receiveNotificationListener->lastProcessVariable->getName() == "intIn"); + receiveNotificationListener->lastProcessVariable->getName() == "/intIn"); syncUtil.removeReceiveNotificationListener("intIn"); devIntIn->write(); From c75f6c83afa5f3a8bec5f52caa57172bc65bf0cd Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 13:31:11 +0200 Subject: [PATCH 217/786] changed interface of DeviceSyncUtil to use RegisterPath instead of string --- .../DeviceSynchronizationUtility.h | 13 ++++++------- src/DeviceSynchronizationUtility.cc | 7 +++---- tests/src/testDeviceSynchronizationUtility.cpp | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h index 005c0d4..b49a155 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_SYNCHRONIZATION_UTILITY #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_DEVICE_SYNCHRONIZATION_UTILITY -#include +#include #include "DevicePVManager.h" @@ -31,18 +31,18 @@ namespace ChimeraTK { * If another listener has previously been registered for the named process * variable, that listener is replaced with the new listener. */ - void addReceiveNotificationListener(std::string const & processVariableName, + void addReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener); /** * Removes a receive notification listener that has previously been * registered with - * {@link #addReceiveNotificationListener(std::string const &, ProcessVariableListener::SharedPtr)}. + * {@link #addReceiveNotificationListener(mtca4u::RegisterPath const &, ProcessVariableListener::SharedPtr)}. * If no listener is registered for the specified process variable name, * this method does nothing. */ void removeReceiveNotificationListener( - std::string const & processVariableName); + mtca4u::RegisterPath const & processVariableName); /** * Runs receive on all process variables that have data pending. This method @@ -124,7 +124,7 @@ namespace ChimeraTK { * Map storing the receive listeners. The maps uses the process variable * as the key and the corresponding receive listener as the value. */ - boost::unordered_map _receiveNotificationListeners; + std::unordered_map _receiveNotificationListeners; // Disable copy construction and assignment. DeviceSynchronizationUtility(DeviceSynchronizationUtility const &); @@ -142,8 +142,7 @@ namespace ChimeraTK { T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { if ((*i)->readNonBlocking()) { - boost::unordered_map::iterator listenerIterator = - _receiveNotificationListeners.find((*i)->getName()); + auto listenerIterator = _receiveNotificationListeners.find((*i)->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index d4dca92..995b2ef 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -12,7 +12,7 @@ namespace ChimeraTK { } void DeviceSynchronizationUtility::addReceiveNotificationListener( - std::string const & processVariableName, + mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { _receiveNotificationListeners.erase(processVariableName); _receiveNotificationListeners.insert( @@ -20,7 +20,7 @@ namespace ChimeraTK { } void DeviceSynchronizationUtility::removeReceiveNotificationListener( - std::string const & processVariableName) { + mtca4u::RegisterPath const & processVariableName) { _receiveNotificationListeners.erase(processVariableName); } @@ -28,8 +28,7 @@ namespace ChimeraTK { ProcessVariable::SharedPtr pv; while ((pv = _pvManager->nextNotification())) { if (pv->readNonBlocking()) { - boost::unordered_map::iterator listenerIterator = - _receiveNotificationListeners.find(pv->getName()); + auto listenerIterator = _receiveNotificationListeners.find(pv->getName()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); diff --git a/tests/src/testDeviceSynchronizationUtility.cpp b/tests/src/testDeviceSynchronizationUtility.cpp index 00ff582..0a7b828 100644 --- a/tests/src/testDeviceSynchronizationUtility.cpp +++ b/tests/src/testDeviceSynchronizationUtility.cpp @@ -220,14 +220,14 @@ BOOST_AUTO_TEST_SUITE( DeviceSynchronizationUtilityTestSuite ) csFloatOut->write(); syncUtil.receiveAll(); BOOST_CHECK(receiveNotificationListener->count == 1); - BOOST_CHECK(receiveNotificationListener->lastProcessVariable->getName() == "intOut"); + BOOST_CHECK(receiveNotificationListener->lastProcessVariable->getName() == "/intOut"); csIntOut->write(); std::vector pvList(1); pvList[0] = devIntOut; syncUtil.receive(pvList); BOOST_CHECK(receiveNotificationListener->count == 2); - BOOST_CHECK(receiveNotificationListener->lastProcessVariable->getName() == "intOut"); + BOOST_CHECK(receiveNotificationListener->lastProcessVariable->getName() == "/intOut"); syncUtil.removeReceiveNotificationListener("intOut"); csIntOut->write(); From eea8ceaf2973002e9ac9e0f2e5f22e073b0e1db4 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 13:58:57 +0200 Subject: [PATCH 218/786] Changed PVManager to use 'const RegisterPath &' instead of copying the object --- include/ChimeraTK/ControlSystemAdapter/PVManager.h | 14 +++++++------- src/PVManager.cc | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 8b22585..9bf8198 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -90,7 +90,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( - mtca4u::RegisterPath processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 2); @@ -123,7 +123,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( - mtca4u::RegisterPath processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 2); @@ -143,7 +143,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> getProcessArray( - mtca4u::RegisterPath processVariableName) const; + mtca4u::RegisterPath const & processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier @@ -157,7 +157,7 @@ namespace ChimeraTK { * device library. */ std::pair getProcessVariable( - mtca4u::RegisterPath processVariableName) const; + mtca4u::RegisterPath const & processVariableName) const; /** * Returns the map containing all process variables, using the names as @@ -426,7 +426,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( - mtca4u::RegisterPath processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) @@ -463,7 +463,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( - mtca4u::RegisterPath processVariableName, const std::vector& initialValue, + mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) @@ -501,7 +501,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::getProcessArray( - mtca4u::RegisterPath processVariableName) const { + mtca4u::RegisterPath const & processVariableName) const { ProcessVariableSharedPtrPair processVariable = getProcessVariable( processVariableName); if (processVariable.first && processVariable.second) { diff --git a/src/PVManager.cc b/src/PVManager.cc index 3b9e680..bea818c 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -25,7 +25,7 @@ namespace ChimeraTK { } std::pair PVManager::getProcessVariable( - mtca4u::RegisterPath processVariableName) const { + mtca4u::RegisterPath const & processVariableName) const { ProcessVariableMap::const_iterator i = _processVariables.find( processVariableName); if (i != _processVariables.end()) { From f9726b1182a701656deea5312c7218164f191988 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 15 Aug 2017 13:58:57 +0200 Subject: [PATCH 219/786] added work-around for missing front() function in older BOOST versions like 1.54 --- .../ControlSystemAdapter/ProcessArray.h | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index e118cd5..ee0659f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -266,6 +266,46 @@ namespace ChimeraTK { */ std::atomic _tripleBufferIndex; + /** + * Define a special spsc_queue which allows to obtain the front element of the queue without popping it. This + * feature was added to later versions of the boost::lockfree::spsc_queue, but we like to be compatible with + * BOOST 1.54 (as used on Ubuntu 14.04 and SUSE 42.2). When this compatibility requirement has been dropped, + * th use of this class can simply be replaced by a boost::lockfree::spsc_queue. + */ + template + class spsc_queue : public boost::lockfree::spsc_queue { + public: + using boost::lockfree::spsc_queue::spsc_queue; + X& front() { + if(!frontValid) { + frontValid = true; + boost::lockfree::spsc_queue::pop(frontElement); + } + return frontElement; + } + void pop(X &value) { + if(frontValid) { + frontValid = false; + value = frontElement; + return; + } + boost::lockfree::spsc_queue::pop(value); + } + void pop() { + if(frontValid) { + frontValid = false; + return; + } + boost::lockfree::spsc_queue::pop(); + } + size_t read_available() { + return boost::lockfree::spsc_queue::read_available() + ( frontValid ? 1 : 0 ); + } + private: + bool frontValid{false}; + X frontElement; + }; + /** * Queue holding the indices of the full buffers. Those are the buffers * that have been sent but not yet received. We do not use an spsc_queue @@ -273,8 +313,7 @@ namespace ChimeraTK { * thread, so there are two threads which might consume elements and thus * an spsc_queue is not safe. */ - //boost::lockfree::queue _fullBufferQueue; - boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; + spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; /** * Queue holding the empty buffers. Those are the buffers that have been From 863837c723d349c3fd08c03d4fd9ff23c67afdaa Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 14:46:14 +0200 Subject: [PATCH 220/786] changed ControlSystemPVManager to use RegisterPath instead of string in the interface. --- .../ControlSystemPVManager.h | 18 +++++++++--------- src/ControlSystemPVManager.cc | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 4d6cee5..3b34f53 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -56,9 +56,9 @@ namespace ChimeraTK { /** * Returns a reference to a process array that has been created earlier * using the - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type @@ -66,14 +66,14 @@ namespace ChimeraTK { */ template typename ProcessArray::SharedPtr getProcessArray( - const std::string& processVariableName) const; + const mtca4u::RegisterPath& processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. */ @@ -81,14 +81,14 @@ namespace ChimeraTK { * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid * non-working links: * - * The {@link getProcessArray(const std:.string&)} methods should be preferred + * The {@link getProcessArray(const mtca4u::RegisterPath&)} methods should be preferred */ - /** The \c getProcessScalar(const std::string&) and + /** The \c getProcessScalar(const mtca4u::RegisterPath&) and * \c getProcessArray(const std:.string&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( - const std::string& processVariableName) const; + const mtca4u::RegisterPath& processVariableName) const; /** * Returns a vector containing all process variables that are registered @@ -140,7 +140,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr ControlSystemPVManager::getProcessArray( - const std::string& processVariableName) const { + const mtca4u::RegisterPath& processVariableName) const { auto pv = _pvManager->getProcessArray(processVariableName).first; if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); return pv; diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index 44ee194..a9e1f4e 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -8,7 +8,7 @@ namespace ChimeraTK { } ProcessVariable::SharedPtr ControlSystemPVManager::getProcessVariable( - const std::string & processVariableName) const { + const mtca4u::RegisterPath & processVariableName) const { auto pv = _pvManager->getProcessVariable(processVariableName).first; if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); return pv; From f31ab348673f7f5ecce5b59409dc7c1c9131b556 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 15:48:44 +0200 Subject: [PATCH 221/786] changed DevicePVManager to use RegisterPath instead of string in the interface. --- .../ControlSystemAdapter/DevicePVManager.h | 26 +++++++++---------- src/DevicePVManager.cc | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 9dc9a86..682ab63 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -80,7 +80,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, std::size_t size, + const mtca4u::RegisterPath& processVariableName, std::size_t size, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", T initialValue = T(), bool maySendDestructively = false, std::size_t numberOfBuffers = 2); @@ -111,30 +111,30 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, const std::vector& initialValue, + const mtca4u::RegisterPath& processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 2); /** * Returns a reference to a process array that has been created earlier * using the - * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} * or - * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type * does not match. */ template typename ProcessArray::SharedPtr getProcessArray( - const std::string& processVariableName) const; + const mtca4u::RegisterPath& processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link createProcessArray(SynchronizationDirection, const std::string&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, + * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, * or - * {@link createProcessArray(SynchronizationDirection, const std::string&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. */ @@ -142,15 +142,15 @@ namespace ChimeraTK { * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid * non-working links: * - * The {@link getProcessScalar(const std::string&)} and + * The {@link getProcessScalar(const mtca4u::RegisterPath&)} and * {@link getProcessArray(const std:.string&)} methods should be preferred */ - /** The \c getProcessScalar(const std::string&) and + /** The \c getProcessScalar(const mtca4u::RegisterPath&) and * \c getProcessArray(const std:.string&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( - const std::string& processVariableName) const; + const mtca4u::RegisterPath& processVariableName) const; /** * Returns a vector containing all process variables that are registered @@ -224,7 +224,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, std::size_t size, + const mtca4u::RegisterPath& processVariableName, std::size_t size, const std::string& unit, const std::string& description, T initialValue, bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { @@ -244,7 +244,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, - const std::string& processVariableName, const std::vector& initialValue, + const mtca4u::RegisterPath& processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers) { switch (synchronizationDirection) { @@ -263,7 +263,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::getProcessArray( - const std::string& processVariableName) const { + const mtca4u::RegisterPath& processVariableName) const { return _pvManager->getProcessArray(processVariableName).second; } diff --git a/src/DevicePVManager.cc b/src/DevicePVManager.cc index e4bd211..388215a 100644 --- a/src/DevicePVManager.cc +++ b/src/DevicePVManager.cc @@ -9,7 +9,7 @@ namespace ChimeraTK { } ProcessVariable::SharedPtr DevicePVManager::getProcessVariable( - const std::string & processVariableName) const { + const mtca4u::RegisterPath & processVariableName) const { return _pvManager->getProcessVariable(processVariableName).second; } From 9a4a08790e4888340114c86c7cd7d39ec2eb1a4d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 15 Aug 2017 15:54:00 +0200 Subject: [PATCH 222/786] reverted the last commit, since it still does not work with the older BOOST versions due to the missing function boost::lockfree::spsc_queue::read_available(), which cannot be implemented by ourselves. --- .../ControlSystemAdapter/ProcessArray.h | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index ee0659f..ad67865 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -266,46 +266,6 @@ namespace ChimeraTK { */ std::atomic _tripleBufferIndex; - /** - * Define a special spsc_queue which allows to obtain the front element of the queue without popping it. This - * feature was added to later versions of the boost::lockfree::spsc_queue, but we like to be compatible with - * BOOST 1.54 (as used on Ubuntu 14.04 and SUSE 42.2). When this compatibility requirement has been dropped, - * th use of this class can simply be replaced by a boost::lockfree::spsc_queue. - */ - template - class spsc_queue : public boost::lockfree::spsc_queue { - public: - using boost::lockfree::spsc_queue::spsc_queue; - X& front() { - if(!frontValid) { - frontValid = true; - boost::lockfree::spsc_queue::pop(frontElement); - } - return frontElement; - } - void pop(X &value) { - if(frontValid) { - frontValid = false; - value = frontElement; - return; - } - boost::lockfree::spsc_queue::pop(value); - } - void pop() { - if(frontValid) { - frontValid = false; - return; - } - boost::lockfree::spsc_queue::pop(); - } - size_t read_available() { - return boost::lockfree::spsc_queue::read_available() + ( frontValid ? 1 : 0 ); - } - private: - bool frontValid{false}; - X frontElement; - }; - /** * Queue holding the indices of the full buffers. Those are the buffers * that have been sent but not yet received. We do not use an spsc_queue @@ -313,7 +273,7 @@ namespace ChimeraTK { * thread, so there are two threads which might consume elements and thus * an spsc_queue is not safe. */ - spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; + boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; /** * Queue holding the empty buffers. Those are the buffers that have been From c9980bad0558d3d445e3f52f3169869e3dec4788 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 16:49:34 +0200 Subject: [PATCH 223/786] fixed docu --- include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 3b34f53..d6f4001 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -84,7 +84,7 @@ namespace ChimeraTK { * The {@link getProcessArray(const mtca4u::RegisterPath&)} methods should be preferred */ /** The \c getProcessScalar(const mtca4u::RegisterPath&) and - * \c getProcessArray(const std:.string&) methods should be preferred + * \c getProcessArray(const mtca4u::RegisterPath&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( From 489ec4111c677416325cc6ffc710e1a8e02725a1 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 17:02:59 +0200 Subject: [PATCH 224/786] changed interface of PersistentDataStorage to use RegisterPath instead of string for variable names --- .../ControlSystemAdapter/ControlSystemPVManager.h | 1 - .../ControlSystemSynchronizationUtility.h | 1 - include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h | 4 ++-- .../ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h | 7 ++++--- src/PersistentDataStorage.cc | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index d6f4001..06b916f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -3,7 +3,6 @@ #include #include -#include #include diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index 177f53b..123413c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -125,7 +125,6 @@ namespace ChimeraTK { * as the key and the corresponding receive listener as the value. */ std::unordered_map _receiveNotificationListeners; - // std::map _receiveNotificationListeners; // Disable copy construction and assignment. ControlSystemSynchronizationUtility( diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 682ab63..67f23b5 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -143,10 +143,10 @@ namespace ChimeraTK { * non-working links: * * The {@link getProcessScalar(const mtca4u::RegisterPath&)} and - * {@link getProcessArray(const std:.string&)} methods should be preferred + * {@link getProcessArray(const mtca4u::RegisterPath&)} methods should be preferred */ /** The \c getProcessScalar(const mtca4u::RegisterPath&) and - * \c getProcessArray(const std:.string&) methods should be preferred + * \c getProcessArray(const mtca4u::RegisterPath&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index 975be0f..27d7c69 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace xmlpp { class Element; @@ -45,7 +46,7 @@ namespace ChimeraTK { /** Register a variable to be stored to and retrieved from the data storage. The returned value is the ID which * must be passed to the other functions. */ template - size_t registerVariable(std::string const &name, size_t nElements); + size_t registerVariable(mtca4u::RegisterPath const &name, size_t nElements); /** Retrieve the current value for the variable with the given ID */ template @@ -79,7 +80,7 @@ namespace ChimeraTK { std::string _filename; /** Vector of variable names. The index is the ID of the variable. */ - std::vector _variableNames; + std::vector _variableNames; /** Vector of data types. The index is the ID of the variable. */ std::vector _variableTypes; @@ -96,7 +97,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - size_t PersistentDataStorage::registerVariable(std::string const &name, size_t nElements) { + size_t PersistentDataStorage::registerVariable(mtca4u::RegisterPath const &name, size_t nElements) { // check if already existing auto position = std::find(_variableNames.begin(), _variableNames.end(), name); diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index c788e78..5b765a9 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -41,7 +41,7 @@ namespace ChimeraTK { // create XML element for the variable and set name attribute xmlpp::Element *variable = rootElement->add_child("variable"); - variable->set_attribute("name",_variableNames[i]); + variable->set_attribute("name",static_cast(_variableNames[i])); // generate value XML tags and set type name as a string std::string dataTypeName{"unknown"}; From 0599d5acf27201b7549c27af7f917b17e230e07e Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 17:15:49 +0200 Subject: [PATCH 225/786] changed ProcessArray to use RegisterPath as name in the interface. PVs will allways have a / at the beginning in future. --- .../ChimeraTK/ControlSystemAdapter/ProcessArray.h | 12 ++++++------ tests/src/testProcessArray.cpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index e118cd5..6214f97 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -73,7 +73,7 @@ namespace ChimeraTK { * This constructor creates the buffers and queues that are needed for the * send/receive process and are shared with the sender. */ - ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, + ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers); /** @@ -396,7 +396,7 @@ namespace ChimeraTK { */ template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const std::string & name = "", const std::string &unit = "", + std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", T initialValue = 0, std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), @@ -447,7 +447,7 @@ namespace ChimeraTK { */ template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const std::string & name = "", const std::string &unit = "", + const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = @@ -458,7 +458,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - ProcessArray::ProcessArray(InstanceType instanceType, const std::string& name, const std::string &unit, + ProcessArray::ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers) : mtca4u::NDRegisterAccessor(name, unit, description), _instanceType(instanceType), @@ -838,7 +838,7 @@ namespace ChimeraTK { template typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const std::string & name, const std::string &unit, const std::string &description, T initialValue, + std::size_t size, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener) { @@ -857,7 +857,7 @@ namespace ChimeraTK { template typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const std::string & name, const std::string &unit, const std::string &description, + const std::vector& initialValue, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener) { diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index a16ad77..489b5a0 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -79,13 +79,13 @@ namespace ChimeraTK { createSynchronizedProcessArray(N_ELEMENTS); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == ""); + BOOST_CHECK(sender->getName() == "/"); for (typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == 0); } BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); - BOOST_CHECK(receiver->getName() == ""); + BOOST_CHECK(receiver->getName() == "/"); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == 0); @@ -99,13 +99,13 @@ namespace ChimeraTK { SOME_NUMBER, 5); sender = senderReceiver.first; receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == "test"); + BOOST_CHECK(sender->getName() == "/test"); for (typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); - BOOST_CHECK(receiver->getName() == "test"); + BOOST_CHECK(receiver->getName() == "/test"); for (typename std::vector::const_iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); @@ -115,12 +115,12 @@ namespace ChimeraTK { 5, false); sender = senderReceiver.first; receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == "test"); + BOOST_CHECK(sender->getName() == "/test"); BOOST_CHECK(sender->accessChannel(0).size() == 4); BOOST_CHECK( std::equal(sender->accessChannel(0).begin(), sender->accessChannel(0).end(), referenceVector.begin())); - BOOST_CHECK(receiver->getName() == "test"); + BOOST_CHECK(receiver->getName() == "/test"); BOOST_CHECK(receiver->accessChannel(0).size() == 4); BOOST_CHECK( std::equal(receiver->accessChannel(0).begin(), receiver->accessChannel(0).end(), From d4dd7163fb4822232483b2a31170969f202274e5 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 17:31:41 +0200 Subject: [PATCH 226/786] changed ControlSystemSyncUtil to use raw pointers instead of name strings for the internal map. Should improve performance. --- .../ControlSystemSynchronizationUtility.h | 9 +++++---- src/ControlSystemSynchronizationUtility.cc | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index 123413c..ba93c11 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -121,10 +121,11 @@ namespace ChimeraTK { ControlSystemPVManager::SharedPtr _pvManager; /** - * Map storing the receive listeners. The maps uses the process variable - * as the key and the corresponding receive listener as the value. + * Map storing the receive listeners. The maps uses the raw pointer to the process variable + * as the key and the corresponding receive listener as the value. (Using the pointer instead + * of the name allows faster hashing). */ - std::unordered_map _receiveNotificationListeners; + std::unordered_map _receiveNotificationListeners; // Disable copy construction and assignment. ControlSystemSynchronizationUtility( @@ -143,7 +144,7 @@ namespace ChimeraTK { T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { if ((*i)->readNonBlocking()) { - auto listenerIterator = _receiveNotificationListeners.find((*i)->getName()); + auto listenerIterator = _receiveNotificationListeners.find(i->get()); // get the raw pointer from the shared pointer which is iterated if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index 9a376b1..bde343e 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -16,21 +16,22 @@ namespace ChimeraTK { void ControlSystemSynchronizationUtility::addReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { - _receiveNotificationListeners.erase(processVariableName); - _receiveNotificationListeners.insert( - std::make_pair(processVariableName, receiveNotificationListener)); + auto pv = _pvManager->getProcessVariable(processVariableName); + _receiveNotificationListeners.erase(pv.get()); // get the raw pointer + _receiveNotificationListeners.insert(std::make_pair(pv.get(), receiveNotificationListener)); } void ControlSystemSynchronizationUtility::removeReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName) { - _receiveNotificationListeners.erase(processVariableName); + auto pv = _pvManager->getProcessVariable(processVariableName); + _receiveNotificationListeners.erase(pv.get()); // get the raw pointer } void ControlSystemSynchronizationUtility::receiveAll() { ProcessVariable::SharedPtr pv; while ((pv = _pvManager->nextNotification())) { if (pv->readNonBlocking()) { - auto listenerIterator = _receiveNotificationListeners.find(pv->getName()); + auto listenerIterator = _receiveNotificationListeners.find(pv.get()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); From f4837fe0724e40f0249ef43e6ec02c7c45f8df42 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 15 Aug 2017 17:39:18 +0200 Subject: [PATCH 227/786] added minimum boost version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c89447..d9b5310 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow -pedantic -Wunini set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 --coverage") #boost as such is required for shared pointers e.g. -FIND_PACKAGE(Boost REQUIRED) +FIND_PACKAGE(Boost 1.58 REQUIRED) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) #boost chrono is needed for measuring time intervals. From 3e0fb92cef9da1098b3fd35f410558dc94c1d328 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 18:17:17 +0200 Subject: [PATCH 228/786] extended the ControlSystemSyncUtil with functions to add and remove listeners that take PVs as argument, and deprecated the original version with variable names --- .../ControlSystemSynchronizationUtility.h | 20 +++++++++++++++---- src/ControlSystemSynchronizationUtility.cc | 20 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index ba93c11..9a54345 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -28,21 +28,33 @@ namespace ChimeraTK { * listener is triggered every time after calling receive on * the process variable if the receive operation returned true. * - * If another listener has previously been registered for the named process + * If another listener has previously been registered for the process * variable, that listener is replaced with the new listener. */ + void addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, + ProcessVariableListener::SharedPtr receiveNotificationListener); + + /** + * @deprecated Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead. + */ void addReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener); + /** * Removes a receive notification listener that has previously been * registered with * {@link #addReceiveNotificationListener(mtca4u::RegisterPath const &, ProcessVariableListener::SharedPtr)}. - * If no listener is registered for the specified process variable name, + * If no listener is registered for the specified process variable, * this method does nothing. */ - void removeReceiveNotificationListener( - mtca4u::RegisterPath const & processVariableName); + void removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable); + + /** + * @deprecated Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead. + */ + void removeReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName); + /** * Runs receive on all process variables that have data pending. This method diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index bde343e..cdbc67b 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -16,15 +16,29 @@ namespace ChimeraTK { void ControlSystemSynchronizationUtility::addReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { + std::cout << "Use of deprecated function: Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); - _receiveNotificationListeners.erase(pv.get()); // get the raw pointer - _receiveNotificationListeners.insert(std::make_pair(pv.get(), receiveNotificationListener)); + addReceiveNotificationListener(pv, receiveNotificationListener); + } + + void ControlSystemSynchronizationUtility::addReceiveNotificationListener( + ProcessVariable::SharedPtr const & processVariable, + ProcessVariableListener::SharedPtr receiveNotificationListener) { + _receiveNotificationListeners.erase(processVariable.get()); // get the raw pointer + _receiveNotificationListeners.insert(std::make_pair(processVariable.get(), receiveNotificationListener)); } void ControlSystemSynchronizationUtility::removeReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName) { + std::cout << "Use of deprecated function: Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); - _receiveNotificationListeners.erase(pv.get()); // get the raw pointer + // althougt it's just one line: call the new interface so the old test covers both + removeReceiveNotificationListener(pv); // get the raw pointer + } + + void ControlSystemSynchronizationUtility::removeReceiveNotificationListener( + ProcessVariable::SharedPtr const & processVariable) { + _receiveNotificationListeners.erase(processVariable.get()); // get the raw pointer } void ControlSystemSynchronizationUtility::receiveAll() { From aa2bf70059af8c15a2a599df82017a1d3a1b6a59 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 18:24:59 +0200 Subject: [PATCH 229/786] changed DeviceSyncUtil to use raw pointers instead of name strings for the internal map. Should improve performance. --- .../DeviceSynchronizationUtility.h | 4 ++-- src/DeviceSynchronizationUtility.cc | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h index b49a155..abd3f33 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h @@ -124,7 +124,7 @@ namespace ChimeraTK { * Map storing the receive listeners. The maps uses the process variable * as the key and the corresponding receive listener as the value. */ - std::unordered_map _receiveNotificationListeners; + std::unordered_map _receiveNotificationListeners; // Disable copy construction and assignment. DeviceSynchronizationUtility(DeviceSynchronizationUtility const &); @@ -142,7 +142,7 @@ namespace ChimeraTK { T processVariablesEnd) { for (T i = processVariablesBegin; i != processVariablesEnd; ++i) { if ((*i)->readNonBlocking()) { - auto listenerIterator = _receiveNotificationListeners.find((*i)->getName()); + auto listenerIterator = _receiveNotificationListeners.find(i->get()); // find the raw pointer if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 995b2ef..9f8feb2 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -14,21 +14,22 @@ namespace ChimeraTK { void DeviceSynchronizationUtility::addReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { - _receiveNotificationListeners.erase(processVariableName); - _receiveNotificationListeners.insert( - std::make_pair(processVariableName, receiveNotificationListener)); + auto pv = _pvManager->getProcessVariable(processVariableName); + _receiveNotificationListeners.erase(pv.get()); + _receiveNotificationListeners.insert(std::make_pair(pv.get(), receiveNotificationListener)); } void DeviceSynchronizationUtility::removeReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName) { - _receiveNotificationListeners.erase(processVariableName); + auto pv = _pvManager->getProcessVariable(processVariableName); + _receiveNotificationListeners.erase(pv.get()); } void DeviceSynchronizationUtility::receiveAll() { ProcessVariable::SharedPtr pv; while ((pv = _pvManager->nextNotification())) { if (pv->readNonBlocking()) { - auto listenerIterator = _receiveNotificationListeners.find(pv->getName()); + auto listenerIterator = _receiveNotificationListeners.find(pv.get()); if (listenerIterator != _receiveNotificationListeners.end()) { ProcessVariableListener::SharedPtr receiveListener( listenerIterator->second); From 8fae6f2bdd4d2fb8289ce4287375c6c70b1b8094 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 18:29:48 +0200 Subject: [PATCH 230/786] extended the DeviceSyncUtil with functions to add and remove listeners that take PVs as argument, and deprecated the original version with variable names --- .../DeviceSynchronizationUtility.h | 11 +++++++++++ src/DeviceSynchronizationUtility.cc | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h index abd3f33..d3b4fa8 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h @@ -31,6 +31,12 @@ namespace ChimeraTK { * If another listener has previously been registered for the named process * variable, that listener is replaced with the new listener. */ + void addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, + ProcessVariableListener::SharedPtr receiveNotificationListener); + + /** + * @deprecated Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead. + */ void addReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener); @@ -41,6 +47,11 @@ namespace ChimeraTK { * If no listener is registered for the specified process variable name, * this method does nothing. */ + void removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable); + + /** + * @deprecated Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead. + */ void removeReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName); diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 9f8feb2..89b5bcc 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -14,15 +14,30 @@ namespace ChimeraTK { void DeviceSynchronizationUtility::addReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { + std::cout << "Use of deprecated function: Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); _receiveNotificationListeners.erase(pv.get()); - _receiveNotificationListeners.insert(std::make_pair(pv.get(), receiveNotificationListener)); + addReceiveNotificationListener(pv, receiveNotificationListener); + } + + void DeviceSynchronizationUtility::addReceiveNotificationListener( + ProcessVariable::SharedPtr const & processVariable, + ProcessVariableListener::SharedPtr receiveNotificationListener) { + _receiveNotificationListeners.erase(processVariable.get()); + _receiveNotificationListeners.insert(std::make_pair(processVariable.get(), receiveNotificationListener)); } void DeviceSynchronizationUtility::removeReceiveNotificationListener( mtca4u::RegisterPath const & processVariableName) { + std::cout << "Use of deprecated function: Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); - _receiveNotificationListeners.erase(pv.get()); + // althougt it's just one line: call the new interface so the old test covers both + removeReceiveNotificationListener(pv); // get the raw pointer + } + + void DeviceSynchronizationUtility::removeReceiveNotificationListener( + ProcessVariable::SharedPtr const & processVariable) { + _receiveNotificationListeners.erase(processVariable.get()); // get the raw pointer } void DeviceSynchronizationUtility::receiveAll() { From 72819ea087cbd8901706acffed37ffd408f02079 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 15 Aug 2017 18:47:00 +0200 Subject: [PATCH 231/786] removed some leftover debug output which causes compiler warnings in release mode --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 5d638fb..05b352e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -627,9 +627,6 @@ namespace ChimeraTK { // if now the local buffer contains valid data, use that data first if(_tripleBufferIndex->_isValid) { future = boost::make_ready_future(static_cast(_tripleBufferIndex)).share(); - - auto status2 = future.wait_for(boost::chrono::duration(0)); - assert(status2 != boost::future_status::timeout); } } From bdeaac372d1bf4a3b97826c154141dfbda269c68 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 16 Aug 2017 14:30:36 +0200 Subject: [PATCH 232/786] don't reuse all the time the same numbers in the tests --- tests/src/testAsyncRead.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index c1c3fb8..5d94a1f 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -365,16 +365,16 @@ void AsyncReadTest::testMixedRead() { BOOST_CHECK( accessor.readNonBlocking() == false ); // readAsync with data already present, then discard the future, with more data - senderAccessor = 15; - senderAccessor.write(); senderAccessor = 16; senderAccessor.write(); + senderAccessor = 17; + senderAccessor.write(); future = &(accessor.readAsync()); usleep(10000); accessor.read(); - BOOST_CHECK( accessor == 15 ); - accessor.read(); BOOST_CHECK( accessor == 16 ); + accessor.read(); + BOOST_CHECK( accessor == 17 ); BOOST_CHECK( accessor.readNonBlocking() == false ); @@ -383,17 +383,17 @@ void AsyncReadTest::testMixedRead() { usleep(10000); BOOST_CHECK(future->hasNewData() == false); - senderAccessor = 13; + senderAccessor = 18; senderAccessor.write(); - senderAccessor = 14; + senderAccessor = 19; senderAccessor.write(); usleep(10000); BOOST_CHECK( accessor.readNonBlocking() == true ); - BOOST_CHECK( accessor == 13 ); + BOOST_CHECK( accessor == 18 ); BOOST_CHECK( accessor.readNonBlocking() == true ); - BOOST_CHECK( accessor == 14 ); + BOOST_CHECK( accessor == 19 ); BOOST_CHECK( accessor.readNonBlocking() == false ); @@ -404,27 +404,27 @@ void AsyncReadTest::testMixedRead() { BOOST_CHECK( accessor.readNonBlocking() == false ); - senderAccessor = 13; + senderAccessor = 20; senderAccessor.write(); - senderAccessor = 14; + senderAccessor = 21; senderAccessor.write(); usleep(10000); BOOST_CHECK( accessor.readNonBlocking() == true ); - BOOST_CHECK( accessor == 13 ); + BOOST_CHECK( accessor == 20 ); BOOST_CHECK( accessor.readNonBlocking() == true ); - BOOST_CHECK( accessor == 14 ); + BOOST_CHECK( accessor == 21 ); BOOST_CHECK( accessor.readNonBlocking() == false ); // readAsync with data already present, then discard the future - readNonBlocking() version - senderAccessor = 15; + senderAccessor = 22; senderAccessor.write(); future = &(accessor.readAsync()); usleep(10000); BOOST_CHECK( accessor.readNonBlocking() == true ); - BOOST_CHECK( accessor == 15 ); + BOOST_CHECK( accessor == 22 ); BOOST_CHECK( accessor.readNonBlocking() == false ); From 41da1edb08931694b794af6014d41a81fa27d728 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 17 Aug 2017 09:37:39 +0200 Subject: [PATCH 233/786] extended the test for readLatest (test now fails, since it shows a bug in the implementation) --- tests/src/testProcessArray.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index 489b5a0..aeb2d4b 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -408,7 +408,6 @@ namespace ChimeraTK { template void ProcessArrayTest::testReadLatest() { - std::cout << "testReadLatest 1" << std::endl; auto senderReceiver = createSynchronizedProcessArray(1); auto sender = senderReceiver.first; auto receiver = senderReceiver.second; @@ -416,14 +415,11 @@ namespace ChimeraTK { // readLatest with only one element in the queue should read that element sender->accessData(0) = 42; sender->write(); - std::cout << "testReadLatest 2" << std::endl; BOOST_CHECK(receiver->readLatest()); - std::cout << "testReadLatest 3" << std::endl; BOOST_CHECK_EQUAL(receiver->accessData(0), 42); // readLatest with no element in the queue will return false and not change the value BOOST_CHECK(!receiver->readLatest()); - std::cout << "testReadLatest 4" << std::endl; BOOST_CHECK_EQUAL(receiver->accessData(0), 42); // readLatest with two elements (queue is full) should return the second element @@ -431,11 +427,26 @@ namespace ChimeraTK { sender->write(); sender->accessData(0) = 77; sender->write(); - std::cout << "testReadLatest 5" << std::endl; BOOST_CHECK(receiver->readLatest()); - std::cout << "testReadLatest 6" << std::endl; BOOST_CHECK_EQUAL(receiver->accessData(0), 77); - std::cout << "testReadLatest 7" << std::endl; + + // readLatest with three elements (queue is full and atomic buffer is filled) should return the third element + sender->accessData(0) = 33; + sender->write(); + sender->accessData(0) = 44; + sender->write(); + sender->accessData(0) = 55; + sender->write(); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 55); + + // readLatest with 20 elements (certain queue overflow) should still return the last element + for(int i=0; i<20; ++i) { + sender->accessData(0) = i; + sender->write(); + } + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 19); } } //namespace ChimeraTK From a861347107c0ff313232138a45b649a9570e6230 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 17 Aug 2017 14:24:04 +0200 Subject: [PATCH 234/786] extended the test for readLatest() a bit --- tests/src/testProcessArray.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index aeb2d4b..f415047 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -429,6 +429,8 @@ namespace ChimeraTK { sender->write(); BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 77); + BOOST_CHECK(!receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 77); // readLatest with three elements (queue is full and atomic buffer is filled) should return the third element sender->accessData(0) = 33; @@ -447,6 +449,14 @@ namespace ChimeraTK { } BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 19); + + // redo last step to make sure no buffers are lost when having a queue overflow + for(int i=0; i<20; ++i) { + sender->accessData(0) = 100+i; + sender->write(); + } + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 119); } } //namespace ChimeraTK From b7d1caf70d15cc0f826294bd8e05e09c54cbb250 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 17 Aug 2017 14:24:22 +0200 Subject: [PATCH 235/786] bug fix: readLatest() was not properly dealing with the atomic triple buffer in case of queue overruns --- .../ControlSystemAdapter/ProcessArray.h | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 05b352e..66186d1 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -578,7 +578,10 @@ namespace ChimeraTK { // Obtain future and check if a transfer is complete. auto theFuture = readAsync().getBoostFuture(); auto status = theFuture.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) return false; + if(status == boost::future_status::timeout) { + /// @todo FIXME: check triple buffer!? + return false; + } return true; @@ -591,8 +594,30 @@ namespace ChimeraTK { // As long as there is more than one valid element on the queue, discard it. // Due to our implementation there is always one unfulfilled future in the queue, so // we must pop until there are two elements left in order not to flush out the newest valid value. - while(_sharedState->_fullBufferQueue.read_available() > 2) { + auto theFuture = readAsync().getBoostFuture(); + while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout + && _sharedState->_fullBufferQueue.read_available() > 2 ) { + // discard data by moving the buffer to the empty buffer queue + TransferFuture::Data *discardedBuffer = theFuture.get(); _sharedState->_fullBufferQueue.pop(); + _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue + theFuture = readAsync().getBoostFuture(); + } + // check whether newer data is present in the atomic triple buffer, in which case we also need to discard the last + // valid element in the queue + // first update the triple buffer if it is not valid on our side + if(!(_tripleBufferIndex->_isValid)) { + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + } + // if it now contains a valid element, check if it is newer + if(_tripleBufferIndex->_isValid) { + VersionNumber tripleBufferVersion = _tripleBufferIndex->_versionNumber; + VersionNumber queueVersion = theFuture.get()->_versionNumber; + if(tripleBufferVersion > queueVersion) { + TransferFuture::Data *discardedBuffer = theFuture.get(); + _sharedState->_fullBufferQueue.pop(); + _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); + } } return this->doReadTransferNonBlocking(); } @@ -791,7 +816,6 @@ namespace ChimeraTK { Buffer *nextIndex; bool lostData = false; if(_sharedState->_emptyBufferQueue.pop(nextIndex)) { - // Create a new promise first and push its future to the full buffer queue before fulfilling the current promise. // This makes sure that always at least one unfulfilled future is available for the receier to wait on. TransferFuture::PromiseType nextPromise; From bd44c4382c6361e79ecf03478f563b725ad9dab0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 17 Aug 2017 16:40:59 +0200 Subject: [PATCH 236/786] bug fix in doReadTransferLatest: when discarding a buffer, mtca4u::TransferElement::hasActiveFuture has to be set to false to make sure to really get a new buffer on the next readAsync(). --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 66186d1..ddb5d3f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -600,6 +600,7 @@ namespace ChimeraTK { // discard data by moving the buffer to the empty buffer queue TransferFuture::Data *discardedBuffer = theFuture.get(); _sharedState->_fullBufferQueue.pop(); + mtca4u::TransferElement::hasActiveFuture = false; _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue theFuture = readAsync().getBoostFuture(); } From e54238a3a8a2ed9417b5c9ed8efe94c12fe3e7ef Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 17 Aug 2017 16:51:52 +0200 Subject: [PATCH 237/786] removed a fixme/todo comment, which is not needed: readAsync() already obeys the triple buffer properly --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index ddb5d3f..1194ad7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -578,11 +578,7 @@ namespace ChimeraTK { // Obtain future and check if a transfer is complete. auto theFuture = readAsync().getBoostFuture(); auto status = theFuture.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) { - /// @todo FIXME: check triple buffer!? - return false; - } - + if(status == boost::future_status::timeout) return false; return true; } From bceee1211ca99bba534499b7fe26474ba4cc1d09 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 17 Aug 2017 16:54:36 +0200 Subject: [PATCH 238/786] Extended the tests with mixed reading and writing in the conjuction of queue overflows. The tests again uncover a bug which will be fixed in the next commit. --- tests/src/testProcessArray.cpp | 63 +++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/src/testProcessArray.cpp b/tests/src/testProcessArray.cpp index f415047..d34a95a 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testProcessArray.cpp @@ -297,6 +297,29 @@ namespace ChimeraTK { // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->readNonBlocking()); + // Same as before but this time with more values + for(int i=0; i<10; ++i) { + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + i); + sender->writeDestructively(); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 0); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 1); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 9); + } + // We have received all values, so no more values should be available. + BOOST_CHECK(!receiver->readNonBlocking()); + // When we send non-destructively, the value should also be preserved on the // sender side. sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); @@ -365,6 +388,24 @@ namespace ChimeraTK { BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); BOOST_CHECK(receiver->accessChannel(0)[0] == 5); + + // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element + // into the queue + for(int i=0; i<10; ++i) { + sender->accessData(0) = 33+i; + sender->write(); + } + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 33); + BOOST_CHECK(receiver->readNonBlocking()); // after this line the buffer should be empty + BOOST_CHECK_EQUAL(receiver->accessData(0), 34); + sender->accessData(0) = 12; + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 12); + BOOST_CHECK(!(receiver->readNonBlocking())); } template @@ -429,7 +470,7 @@ namespace ChimeraTK { sender->write(); BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 77); - BOOST_CHECK(!receiver->readNonBlocking()); + BOOST_CHECK(!receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 77); // readLatest with three elements (queue is full and atomic buffer is filled) should return the third element @@ -449,6 +490,7 @@ namespace ChimeraTK { } BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 19); + BOOST_CHECK(!(receiver->readNonBlocking())); // redo last step to make sure no buffers are lost when having a queue overflow for(int i=0; i<20; ++i) { @@ -457,6 +499,25 @@ namespace ChimeraTK { } BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 119); + BOOST_CHECK(!(receiver->readNonBlocking())); + + // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element + // into the queue - which should then be read by readLatest + for(int i=0; i<10; ++i) { + sender->accessData(0) = 33+i; + sender->write(); + } + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 33); + BOOST_CHECK(receiver->readNonBlocking()); // after this line the buffer should be empty + BOOST_CHECK_EQUAL(receiver->accessData(0), 34); + sender->accessData(0) = 12; + sender->write(); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 12); + BOOST_CHECK(!(receiver->readLatest())); + + } } //namespace ChimeraTK From 5ae3994ebf2c900e001116384ed6891b36cb62e0 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 18 Aug 2017 12:30:06 +0200 Subject: [PATCH 239/786] Add LibXML++_LDFLAGS to target_link_libraries. This ensures that the linker can find libxml++ even if it is installed in a non-default location. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d87f5a6..959740d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,7 @@ endif(TESTING_IS_ENABLED) add_library(${PROJECT_NAME} SHARED ${library_sources} ) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_FULL_LIBRARY_VERSION} SOVERSION ${${PROJECT_NAME}_SOVERSION}) -target_link_libraries(${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} ${LibXML++_LIBRARIES} ${glib_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} ${LibXML++_LDFLAGS} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} ${LibXML++_LIBRARIES} ${glib_LIBRARIES}) #change the install prefix to the source directory in case the user has not specified a destination #i. e. CMAKE_INSTALL_PREFIX is not set manually From a99c36713fae4b009911cb14633cd8f5afe2d9e7 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 18 Aug 2017 14:12:01 +0200 Subject: [PATCH 240/786] bug fix for readLatest(): If the data in the atomic triple buffer is older than in the queue, we need to discard it, since postRead() will always pick the oldest available data. --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 1194ad7..f7e41bd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -600,9 +600,9 @@ namespace ChimeraTK { _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue theFuture = readAsync().getBoostFuture(); } - // check whether newer data is present in the atomic triple buffer, in which case we also need to discard the last - // valid element in the queue - // first update the triple buffer if it is not valid on our side + // Check whether data is present in the atomic triple buffer. If it is newer, we also need to discard the last + // valid element in the queue. If it is older, we need to discard the data in the triple buffer. + // First update the triple buffer if it is not valid on our side if(!(_tripleBufferIndex->_isValid)) { _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); } @@ -615,6 +615,9 @@ namespace ChimeraTK { _sharedState->_fullBufferQueue.pop(); _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); } + else { + _tripleBufferIndex->_isValid = false; + } } return this->doReadTransferNonBlocking(); } From ceeca63d2678291e05bea076954282094ad948a3 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 18 Aug 2017 15:16:32 +0200 Subject: [PATCH 241/786] Increase min. required CMake version to 3.0.2. By setting this CMake version we can avoid a warning about CMP0042 when building on macOS. The impact of this change should be minimal as DeviceAccess already needs this CMake version. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 959740d..77e7d7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.0) +cmake_minimum_required(VERSION 3.0.2) project(ChimeraTK-ControlSystemAdapter) From 55f0796c3e983bd3836fb019dcf1e149f9c023b5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 18 Aug 2017 17:43:20 +0200 Subject: [PATCH 242/786] Switched to an internal version number for deciding the order of values between the queue and the atomic triple buffer. See code comment for the reason. --- .../ControlSystemAdapter/ProcessArray.h | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index f7e41bd..c41352c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -194,6 +194,17 @@ namespace ChimeraTK { TimeStamp timeStamp; std::vector value; bool _isValid{false}; + + /** + * Extra version number used to sort the values within the process variable (so this version is not globally + * valid). This number is needed in addition to the globally valid _versionNumber defined in + * TransferFuture::Data, because the globally valid version numebr can be controlled by the application and + * values with an older version number might be written to this process variable later. That later data with + * the older version number shall still be considered as last data e.g. in the context of readLatest(), i.e. + * whatever has been written to the process variable last should be read with readLatest(), even if it has + * the older version number. + */ + uint64_t _internalVersionNumber; /** * Default constructor. Has to be defined explicitly because we have an @@ -239,6 +250,11 @@ namespace ChimeraTK { * on this process array. */ bool _maySendDestructively; + + /** + * Last internal version number written. See Buffer::_internalVersionNumber for more details. + */ + uint64_t _lastInternalVersionNumber{0}; /** * The state shared between the sender and the receiver @@ -608,8 +624,8 @@ namespace ChimeraTK { } // if it now contains a valid element, check if it is newer if(_tripleBufferIndex->_isValid) { - VersionNumber tripleBufferVersion = _tripleBufferIndex->_versionNumber; - VersionNumber queueVersion = theFuture.get()->_versionNumber; + uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; + uint64_t queueVersion = static_cast(theFuture.get())->_internalVersionNumber; if(tripleBufferVersion > queueVersion) { TransferFuture::Data *discardedBuffer = theFuture.get(); _sharedState->_fullBufferQueue.pop(); @@ -705,8 +721,8 @@ namespace ChimeraTK { useTripleBuffer = true; } else { // queueHasData && tripleBufferHasData -> see the assert above - auto tripleBufferVersion = _tripleBufferIndex->_versionNumber; - VersionNumber queueVersion = future.get()->_versionNumber; + uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; + uint64_t queueVersion = static_cast(future.get())->_internalVersionNumber; if(tripleBufferVersion < queueVersion) { useTripleBuffer = true; } @@ -799,8 +815,10 @@ namespace ChimeraTK { TimeStamp newTimeStamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); _currentIndex->timeStamp = newTimeStamp; - // set the version number + // set the version numbers _currentIndex->_versionNumber = newVersionNumber; + _currentIndex->_internalVersionNumber = _lastInternalVersionNumber; + ++_lastInternalVersionNumber; // flag current data as valid _currentIndex->_isValid = true; From 033b84eb4ca4c666579adbbe275371dff4eb8e04 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 21 Aug 2017 16:31:43 +0200 Subject: [PATCH 243/786] extended test for readAsync() to include the guarantee for receiving data sending order --- tests/src/testAsyncRead.cpp | 70 +++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index 5d94a1f..a978bb4 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -263,6 +263,76 @@ void AsyncReadTest::testReadAny() { thread.join(); BOOST_CHECK( a3 == 122 ); } + + // Test1 and then Test2 (order should be guaranteed) - this time write first to check if order is properly kept + { + s1 = 666; + s1.write(); + s2 = 777; + s2.write(); + // no point to use a thread here + auto r = TransferElement::readAny({a1,a2,a3,a4}); + BOOST_CHECK(a1.isSameRegister(r)); + BOOST_CHECK(a1 == 666); + BOOST_CHECK(a2 == 123); + + r = TransferElement::readAny({a1,a2,a3,a4}); + BOOST_CHECK(a2.isSameRegister(r)); + BOOST_CHECK(a1 == 666); + BOOST_CHECK(a2 == 777); + + BOOST_CHECK(a1.readNonBlocking() == false); + BOOST_CHECK(a2.readNonBlocking() == false); + BOOST_CHECK(a3.readNonBlocking() == false); + BOOST_CHECK(a4.readNonBlocking() == false); + } + + // In order: Test4, Test2, Test3 and Test1 + { + s4 = 111; + s4.write(); + s2 = 222; + s2.write(); + s3 = 333; + s3.write(); + s1 = 444; + s1.write(); + + // no point to use a thread here + auto r = TransferElement::readAny({a1,a2,a3,a4}); + BOOST_CHECK(a4.isSameRegister(r)); + BOOST_CHECK(a1 == 666); + BOOST_CHECK(a2 == 777); + BOOST_CHECK(a3 == 122); + BOOST_CHECK(a4 == 111); + + r = TransferElement::readAny({a1,a2,a3,a4}); + BOOST_CHECK(a2.isSameRegister(r)); + BOOST_CHECK(a1 == 666); + BOOST_CHECK(a2 == 222); + BOOST_CHECK(a3 == 122); + BOOST_CHECK(a4 == 111); + + r = TransferElement::readAny({a1,a2,a3,a4}); + BOOST_CHECK(a3.isSameRegister(r)); + BOOST_CHECK(a1 == 666); + BOOST_CHECK(a2 == 222); + BOOST_CHECK(a3 == 333); + BOOST_CHECK(a4 == 111); + + r = TransferElement::readAny({a1,a2,a3,a4}); + BOOST_CHECK(a1.isSameRegister(r)); + BOOST_CHECK(a1 == 444); + BOOST_CHECK(a2 == 222); + BOOST_CHECK(a3 == 333); + BOOST_CHECK(a4 == 111); + + BOOST_CHECK(a1.readNonBlocking() == false); + BOOST_CHECK(a2.readNonBlocking() == false); + BOOST_CHECK(a3.readNonBlocking() == false); + BOOST_CHECK(a4.readNonBlocking() == false); + + } } From d4eb9c51039fb37b4ed6a0568a004cd4db46cf0b Mon Sep 17 00:00:00 2001 From: Nadeem Shehzad Date: Thu, 24 Aug 2017 16:21:30 +0200 Subject: [PATCH 244/786] added: MAKE_INSTALL_RPATH_USE_LINK_PATH so that local installation has runtime path. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77e7d7e..9bbdc64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,9 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) message("Boost unit_test_framework not found, disabling testing") ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) +#turn on the rpath so libmtca4u-deviceaccess.so is found +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + # DeviceAccess is needed due to the common interface classes add_dependency(mtca4u-deviceaccess 00.24 REQUIRED) From ab372767b4334a30824af4cab65995fe3308c225 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 28 Aug 2017 12:52:00 +0200 Subject: [PATCH 245/786] made applicationName of the ReferenceTestApplication configurable via constructor. --- .../ControlSystemAdapter/Testing/ReferenceTestApplication.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h index dda738f..fcf281d 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h @@ -99,7 +99,7 @@ class ReferenceTestApplication : public ChimeraTK::ApplicationBase{ // It guarantees that the main body has completed execution and has been run exactly one. static void runMainLoopOnce(); - ReferenceTestApplication(); + ReferenceTestApplication(std::string const & applicationName_ = "ReferenceTest"); ~ReferenceTestApplication(); /// Inherited from ApplicationBase @@ -145,9 +145,9 @@ class ReferenceTestApplication : public ChimeraTK::ApplicationBase{ }; -inline ReferenceTestApplication::ReferenceTestApplication() +inline ReferenceTestApplication::ReferenceTestApplication(std::string const & applicationName_) //initialise all process variables, using the factory - : ApplicationBase("ReferenceTest"){ + : ApplicationBase(applicationName_){ } inline void ReferenceTestApplication::initialise(){ From 2cce2acef05ac2d63e0331f8af1933fa21461e93 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 31 Aug 2017 18:05:53 +0200 Subject: [PATCH 246/786] PVManager throws if a process variable is not found, instead of returning a null pointer --- .../ControlSystemAdapter/PVManager.h | 39 ++++++++----------- src/PVManager.cc | 3 +- tests/src/testPVManager.cpp | 4 +- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 9bf8198..3372faa 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -148,7 +148,7 @@ namespace ChimeraTK { /** * Returns a reference to a process scalar or array that has been created earlier * using one of the createProcessScalar... or - * createProcessArray... methods. Returns a pointer to null if there is no process scalar or array + * createProcessArray... methods. Throws std::logic_error if there is no process scalar or array * with the specified name. * * A pair of two process variables is returned: The first member of the pair @@ -504,28 +504,23 @@ namespace ChimeraTK { mtca4u::RegisterPath const & processVariableName) const { ProcessVariableSharedPtrPair processVariable = getProcessVariable( processVariableName); - if (processVariable.first && processVariable.second) { - typename ProcessArray::SharedPtr csPV = boost::dynamic_pointer_cast< - ProcessArray, ProcessVariable>(processVariable.first); - typename ProcessArray::SharedPtr devPV = boost::dynamic_pointer_cast< - ProcessArray, ProcessVariable>(processVariable.second); - if (!csPV || !devPV) { - struct bad_cast : std::bad_cast { - bad_cast(const std::string &what) : _what(what) {} - const char* what() const noexcept override { - return _what.c_str(); - } - std::string _what; - }; - throw bad_cast("PVManager::getProcessArray() called for variable '" + processVariableName +"' with type " - +typeid(T).name()+" which is not the original type " + processVariable.first->getValueType().name() - + " of this process variable."); - } - return std::make_pair(csPV, devPV); - } else { - return std::make_pair(typename ProcessArray::SharedPtr(), - typename ProcessArray::SharedPtr()); + typename ProcessArray::SharedPtr csPV = boost::dynamic_pointer_cast< + ProcessArray, ProcessVariable>(processVariable.first); + typename ProcessArray::SharedPtr devPV = boost::dynamic_pointer_cast< + ProcessArray, ProcessVariable>(processVariable.second); + if (!csPV || !devPV) { + struct bad_cast : std::bad_cast { + bad_cast(const std::string &what) : _what(what) {} + const char* what() const noexcept override { + return _what.c_str(); + } + std::string _what; + }; + throw bad_cast("PVManager::getProcessArray() called for variable '" + processVariableName +"' with type " + +typeid(T).name()+" which is not the original type " + processVariable.first->getValueType().name() + + " of this process variable."); } + return std::make_pair(csPV, devPV); } } diff --git a/src/PVManager.cc b/src/PVManager.cc index bea818c..392e4fe 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -31,8 +31,7 @@ namespace ChimeraTK { if (i != _processVariables.end()) { return i->second; } else { - return std::make_pair(ProcessVariable::SharedPtr(), - ProcessVariable::SharedPtr()); + throw std::logic_error("ChimeraTK::ControlSystemAdapter: Error in PVManager. Unknown process variable '"+(processVariableName)+"'"); } } diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 37b790c..ab31a2f 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -169,8 +169,8 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) shared_ptr csManager = pvManagers.first; shared_ptr devManager = pvManagers.second; - BOOST_CHECK(!devManager->getProcessArray("foo")); - BOOST_CHECK(!csManager->getProcessArray("foo")); + BOOST_CHECK_THROW(devManager->getProcessArray("foo"), std::logic_error); + BOOST_CHECK_THROW(csManager->getProcessArray("foo"), std::logic_error); } BOOST_AUTO_TEST_CASE( testInvalidCast ) { From 7ed96801a6812b7798908e0ab3a06b33fa627a43 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 5 Sep 2017 19:13:26 +0200 Subject: [PATCH 247/786] adapted recent backend interface change by DeviceAccess --- CMakeLists.txt | 4 +- .../ControlSystemAdapter/ProcessArray.h | 111 ++++++++++-------- 2 files changed, 64 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77e7d7e..ec5d2cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 06) +set(${PROJECT_NAME}_MINOR_VERSION 07) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) @@ -43,7 +43,7 @@ ELSE(Boost_UNIT_TEST_FRAMEWORK_FOUND) ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.24 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.25 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index c41352c..18be46e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -483,38 +483,44 @@ namespace ChimeraTK { _currentIndex(&(_sharedState->_buffers[0])), _tripleBufferIndex(&(_sharedState->_buffers[4])) { - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument("This constructor may only be used for a receiver process variable."); - } - // We need at least two buffers for the queue (so four buffers in total) - // in order to guarantee that we never have to block. - if (numberOfBuffers < 2) { - throw std::invalid_argument("The number of buffers must be at least two."); - } - // We have to limit the number of buffers because we cannot allocate - // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { - throw std::invalid_argument("The number of buffers is too large."); - } - // We have to initialize the buffers by copying in the initial vectors. - for (auto &i : _sharedState->_buffers) { - i.value = initialValue; - } - // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the - // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of - // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. - for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { - _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); + try { + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != RECEIVER) { + throw std::invalid_argument("This constructor may only be used for a receiver process variable."); + } + // We need at least two buffers for the queue (so four buffers in total) + // in order to guarantee that we never have to block. + if (numberOfBuffers < 2) { + throw std::invalid_argument("The number of buffers must be at least two."); + } + // We have to limit the number of buffers because we cannot allocate + // more buffers than can be represented by size_t and the total number + // of buffers is the specified number plus two. These extra two buffers + // are needed because the sender and the receiver each hold one buffer + // at all times and thus only the rest remains for the queue. We use + // this definition of the number of buffers to keep it consistent with + // what ProcessScalarImpl uses. + if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { + throw std::invalid_argument("The number of buffers is too large."); + } + // We have to initialize the buffers by copying in the initial vectors. + for (auto &i : _sharedState->_buffers) { + i.value = initialValue; + } + // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the + // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of + // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. + for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { + _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); + } + } + catch(...) { + this->shutdown(); + throw; } } @@ -536,23 +542,29 @@ namespace ChimeraTK { _timeStampSource(timeStampSource), _sendNotificationListener(sendNotificationListener) { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument("This constructor may only be used for a sender process variable."); - } - if (!receiver) { - throw std::invalid_argument("The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; - // put future into the notification queue - _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); + try { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (instanceType != SENDER) { + throw std::invalid_argument("This constructor may only be used for a sender process variable."); + } + if (!receiver) { + throw std::invalid_argument("The pointer to the receiver must not be null."); + } + if (receiver->_instanceType != RECEIVER) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; + // put future into the notification queue + _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); + } + catch(...) { + this->shutdown(); + throw; + } } /*********************************************************************************************************************/ @@ -572,6 +584,7 @@ namespace ChimeraTK { this->readAsyncThread.join(); } } + this->shutdown(); } /*********************************************************************************************************************/ From 7b1984716f059c88129ea305e7b5417845d8939f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 6 Sep 2017 10:24:10 +0200 Subject: [PATCH 248/786] fixed a warning on some gcc versions --- include/ChimeraTK/ControlSystemAdapter/PVManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 3372faa..cea215a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -510,7 +510,7 @@ namespace ChimeraTK { ProcessArray, ProcessVariable>(processVariable.second); if (!csPV || !devPV) { struct bad_cast : std::bad_cast { - bad_cast(const std::string &what) : _what(what) {} + bad_cast(const std::string &description) : _what(description) {} const char* what() const noexcept override { return _what.c_str(); } From 644ec74ccb4c4d60c3b16d0a0d6ca8367fee38ac Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 6 Sep 2017 15:38:19 +0200 Subject: [PATCH 249/786] decreased minor version to 00.06, since this version was not yet released --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f1381d..fd54800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 07) +set(${PROJECT_NAME}_MINOR_VERSION 06) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) From b437abb98663a8d5b0769828cf7906191ced6cf1 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 6 Sep 2017 16:26:48 +0200 Subject: [PATCH 250/786] stated work on TypeChangingDecorator. Compiles, but test is empty yet. Not used anywhere. --- CMakeLists.txt | 1 + .../TypeChangingDecorator.h | 136 ++++++++++++++++++ tests/decoratorTest.map | 3 + tests/src/testTypeChangingDecorator.cpp | 21 +++ 4 files changed, 161 insertions(+) create mode 100644 include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h create mode 100644 tests/decoratorTest.map create mode 100644 tests/src/testTypeChangingDecorator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f1381d..7fe3bbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ if(TESTING_IS_ENABLED) ) ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") + FILE(COPY tests/decoratorTest.map DESTINATION ${PROJECT_BINARY_DIR}) endif(TESTING_IS_ENABLED) add_library(${PROJECT_NAME} SHARED ${library_sources} ) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h new file mode 100644 index 0000000..ab1ade4 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -0,0 +1,136 @@ +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H + +//#include +//#include +//#include +//#include +//#include +// +//#include +//#include +//#include +//#include +//#include +//#include +// +#include +//#include +// +//#include "ProcessVariableListener.h" +//#include "TimeStampSource.h" +//#include "PersistentDataStorage.h" + +namespace ChimeraTK { + + /** + * Strictly this is not a real decorator. It provides an NDRegisterAccessor of a + * different template type and accesses the original one as "backend" under the hood. + * + * This class is not thread-safe and should only be used from a single thread. + * At the moment, it is only returned by the ControlSystemPVManager. + */ + template + class TypeChangingDecorator : public mtca4u::NDRegisterAccessor { + + public: + + TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > & impl) noexcept; + + ~TypeChangingDecorator(); + + bool isReadable() const override { + return _impl->isReadable(); + } + + bool isWriteable() const override { + return _impl->isWriteable(); + } + + bool isReadOnly() const override { + return _impl->isReadOnly(); + } + + mtca4u::TimeStamp getTimeStamp() const override { + return _impl->getTimeStamp(); + } + + ChimeraTK::VersionNumber getVersionNumber() const override { + return _impl->getVersionNumber; + } + + void doReadTransfer() override{ + _impl->doReadTransfer(); + } + + bool doReadTransferNonBlocking() override{ + return _impl->doReadTransferNonBlocking(); + } + + bool doReadTransferLatest() override{ + return _impl->doReadTransferLatest(); + } + + void postRead() override; + void preWrite() override; + + bool isArray() const override { + return _impl->isArray(); + } + + /// @todo FIXME: can we unite stuff in a transfer group? Compare with impl? + bool isSameRegister(const boost::shared_ptr& e) const override { + return _impl->isSameRegister(e); + } + + std::vector > getHardwareAccessingElements() override { + return _impl->getHarwareAccessingElements(); + } + + void replaceTransferElement(boost::shared_ptr e) override { + _impl->replaceTransferElement(e); + } + + void setPersistentDataStorage(boost::shared_ptr storage) override{ + _impl->setPersistentDataStorage(storage); + } + + protected: + boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; + }; + +/*********************************************************************************************************************/ +/*** Implementations of member functions below this line *************************************************************/ +/*********************************************************************************************************************/ + + template + TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > & impl) noexcept + : _impl(impl) + { + // FIXME: resize the buffers + // update the internal data buffer with the values from the impl + postRead(); + } + +/*********************************************************************************************************************/ + + template + TypeChangingDecorator::~TypeChangingDecorator() { + this->shutdown(); + } + +/*********************************************************************************************************************/ + + template + void TypeChangingDecorator::postRead() { + //FIXME: copy the buffers + } + + template + void TypeChangingDecorator::preWrite() { + //FIXME: copy the buffers + } + +} // namespace ChimeraTK + +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H diff --git a/tests/decoratorTest.map b/tests/decoratorTest.map new file mode 100644 index 0000000..da62e58 --- /dev/null +++ b/tests/decoratorTest.map @@ -0,0 +1,3 @@ +# name nr of elements address size bar width fracbits signed +SOME.SCALAR 0x00000001 0x00000000 0x00000004 0x0 32 16 1 +SOME.ARRAY 0x00000010 0x00000000 0x00000040 0x2 32 16 1 diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp new file mode 100644 index 0000000..7a84d38 --- /dev/null +++ b/tests/src/testTypeChangingDecorator.cpp @@ -0,0 +1,21 @@ +#define BOOST_TEST_MODULE TypeChangingDecoratorTest + +#include +using namespace boost::unit_test_framework; + +#include "TypeChangingDecorator.h" +#include + +BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) + +BOOST_AUTO_TEST_CASE( testCreation ){ + mtca4u::Device d; + d.open("sdm://./dummy=decoratorTest.map"); + auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); + + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_SUITE_END() + + From e9069bb38404fdcd9709634d0b2bd86e0ea9cc3b Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 6 Sep 2017 17:09:12 +0200 Subject: [PATCH 251/786] first successful instantiation of TypeChangingDecorator --- .../TypeChangingDecorator.h | 35 +++++++++++++++---- tests/src/testTypeChangingDecorator.cpp | 7 ++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index ab1ade4..407da68 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -56,7 +56,7 @@ namespace ChimeraTK { } ChimeraTK::VersionNumber getVersionNumber() const override { - return _impl->getVersionNumber; + return _impl->getVersionNumber(); } void doReadTransfer() override{ @@ -71,10 +71,31 @@ namespace ChimeraTK { return _impl->doReadTransferLatest(); } - void postRead() override; - void preWrite() override; + void convertAndCopyFromImpl(); + void convertAndCopyToImpl(); + + void postRead() override{ + _impl->postRead(); + convertAndCopyFromImpl(); + } + + void preWrite() override{ + convertAndCopyToImpl(); + _impl->preWrite(); + } + + void postWrite() override{ + _impl->postWrite(); + } - bool isArray() const override { + virtual bool write(ChimeraTK::VersionNumber versionNumber={}){ + // don't call preWrite() here. It would trigger the preWrite on the impl, which is also + // happening when _impl()->write() is called. + convertAndCopyToImpl(); + return _impl->write(versionNumber); + } + + bool isArray() const override { return _impl->isArray(); } @@ -84,7 +105,7 @@ namespace ChimeraTK { } std::vector > getHardwareAccessingElements() override { - return _impl->getHarwareAccessingElements(); + return _impl->getHardwareAccessingElements(); } void replaceTransferElement(boost::shared_ptr e) override { @@ -122,12 +143,12 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - void TypeChangingDecorator::postRead() { + void TypeChangingDecorator::convertAndCopyFromImpl() { //FIXME: copy the buffers } template - void TypeChangingDecorator::preWrite() { + void TypeChangingDecorator::convertAndCopyToImpl() { //FIXME: copy the buffers } diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 7a84d38..44ae84f 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -4,6 +4,8 @@ using namespace boost::unit_test_framework; #include "TypeChangingDecorator.h" +using namespace ChimeraTK; + #include BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) @@ -12,6 +14,11 @@ BOOST_AUTO_TEST_CASE( testCreation ){ mtca4u::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); + + // don't change the type to start with, use int,int + auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); + TypeChangingDecorator decoratedScalar(ndAccessor); BOOST_CHECK(true); } From f85ec3ae51eaff843403a84a2365c34ec9bf0bae Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 7 Sep 2017 15:50:37 +0200 Subject: [PATCH 252/786] first successful read/write test of TypeChangingDecorator. Only expecting tests to be extended, but imlementation should be complete. --- .../TypeChangingDecorator.h | 38 +++++++++++++++--- tests/src/testTypeChangingDecorator.cpp | 40 +++++++++++++++---- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 407da68..5128de4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -118,6 +118,21 @@ namespace ChimeraTK { protected: boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; + + template + struct Round { + static S nearbyint ( S s ){ + return round(s); + } + + typedef boost::mpl::integral_c round_style ; + }; + + typedef boost::numeric::converter, + boost::numeric::def_overflow_handler, Round > FromImplConverter; + typedef boost::numeric::converter, + boost::numeric::def_overflow_handler, Round > ToImplConverter; + }; /*********************************************************************************************************************/ @@ -128,6 +143,10 @@ namespace ChimeraTK { TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > & impl) noexcept : _impl(impl) { + this->buffer_2D.resize(impl->getNumberOfChannels()); + for (auto & channel : this->buffer_2D){ + channel.resize(impl->getNumberOfSamples()); + } // FIXME: resize the buffers // update the internal data buffer with the values from the impl postRead(); @@ -136,20 +155,29 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - TypeChangingDecorator::~TypeChangingDecorator() { + TypeChangingDecorator::~TypeChangingDecorator() { this->shutdown(); } /*********************************************************************************************************************/ template - void TypeChangingDecorator::convertAndCopyFromImpl() { - //FIXME: copy the buffers + void TypeChangingDecorator::convertAndCopyFromImpl() { + //fixme: are iterartors more efficient? + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = FromImplConverter::convert(_impl->accessChannel(i)[j]); + } + } } template - void TypeChangingDecorator::convertAndCopyToImpl() { - //FIXME: copy the buffers + void TypeChangingDecorator::convertAndCopyToImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + _impl->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); + } + } } } // namespace ChimeraTK diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 44ae84f..cf5813a 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -10,17 +10,43 @@ using namespace ChimeraTK; BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) -BOOST_AUTO_TEST_CASE( testCreation ){ +template +void testDecorator(IMPL_T startReadValue, T expectedReadValue, T startWriteValue, IMPL_T exprectedWriteValue){ mtca4u::Device d; d.open("sdm://./dummy=decoratorTest.map"); - auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); - auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); + + auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); + TypeChangingDecorator decoratedScalar(ndAccessor); - // don't change the type to start with, use int,int - auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); - TypeChangingDecorator decoratedScalar(ndAccessor); + BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); + BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); - BOOST_CHECK(true); + anotherScalarAccessor = startReadValue; + anotherScalarAccessor.write(); + BOOST_CHECK(scalar == 0); + BOOST_CHECK(decoratedScalar.accessData(0) == 0); + decoratedScalar.read(); + BOOST_CHECK( decoratedScalar.accessData(0) == expectedReadValue); + + decoratedScalar.accessData(0) = startWriteValue; + decoratedScalar.write(); + anotherScalarAccessor.read(); + BOOST_CHECK( anotherScalarAccessor == exprectedWriteValue ); +} + +BOOST_AUTO_TEST_CASE( testAllDecorators ){ + testDecorator(12,12, 22, 22); + testDecorator(13,13, 23, 23); + testDecorator(14,14, 24, 24); + testDecorator(15,15, 25, 25); + testDecorator(16,16, 26, 26); + testDecorator(17,17, 27, 27); + // only use values which habe an exact representation in float/fixed point + testDecorator(18.5,19, 28, 28); + testDecorator(19.5,20, 29, 29); + // testDecorator(); } BOOST_AUTO_TEST_SUITE_END() From d4f7a21d8c7564e7b169a28b0c9a119e856202e9 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 7 Sep 2017 18:01:20 +0200 Subject: [PATCH 253/786] playing a bit with strings. not really useful --- .../TypeChangingDecorator.h | 45 ++++++++++++++++--- tests/src/testTypeChangingDecorator.cpp | 39 +++++++++++++--- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 5128de4..0f5f5bf 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -128,11 +128,6 @@ namespace ChimeraTK { typedef boost::mpl::integral_c round_style ; }; - typedef boost::numeric::converter, - boost::numeric::def_overflow_handler, Round > FromImplConverter; - typedef boost::numeric::converter, - boost::numeric::def_overflow_handler, Round > ToImplConverter; - }; /*********************************************************************************************************************/ @@ -163,6 +158,8 @@ namespace ChimeraTK { template void TypeChangingDecorator::convertAndCopyFromImpl() { + typedef boost::numeric::converter, + boost::numeric::def_overflow_handler, Round > FromImplConverter; //fixme: are iterartors more efficient? for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ @@ -173,6 +170,8 @@ namespace ChimeraTK { template void TypeChangingDecorator::convertAndCopyToImpl() { + typedef boost::numeric::converter, + boost::numeric::def_overflow_handler, Round > ToImplConverter; for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ _impl->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); @@ -180,6 +179,42 @@ namespace ChimeraTK { } } + template + T stringToT(std::string const & input){ + std::stringstream s; + s << input; + T t; + s >> t; + return t; + } + + template + std::string T_ToString(T input){ + std::stringstream s; + s << input; + std::string output; + s >> output; + return output; + } + + template<> + void TypeChangingDecorator::convertAndCopyFromImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = stringToT(_impl->accessChannel(i)[j]); + } + } + } + template<> + void TypeChangingDecorator::convertAndCopyToImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + _impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); + } + } + } + + } // namespace ChimeraTK #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index cf5813a..ab1aa04 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -25,17 +25,46 @@ void testDecorator(IMPL_T startReadValue, T expectedReadValue, T startWriteValue anotherScalarAccessor = startReadValue; anotherScalarAccessor.write(); - BOOST_CHECK(scalar == 0); - BOOST_CHECK(decoratedScalar.accessData(0) == 0); + // BOOST_CHECK(scalar == 0); + // BOOST_CHECK(decoratedScalar.accessData(0) == 0); decoratedScalar.read(); - BOOST_CHECK( decoratedScalar.accessData(0) == expectedReadValue); + BOOST_CHECK( expectedReadValue == decoratedScalar.accessData(0) ); decoratedScalar.accessData(0) = startWriteValue; decoratedScalar.write(); anotherScalarAccessor.read(); - BOOST_CHECK( anotherScalarAccessor == exprectedWriteValue ); + BOOST_CHECK( exprectedWriteValue == anotherScalarAccessor ); } +template<> +void testDecorator(std::string startReadValue, int expectedReadValue, int startWriteValue, std::string exprectedWriteValue){ + mtca4u::Device d; + d.open("sdm://./dummy=decoratorTest.map"); + auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); + + auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); + TypeChangingDecorator decoratedScalar(ndAccessor); + + BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); + BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); + + anotherScalarAccessor = startReadValue; + anotherScalarAccessor.write(); + // BOOST_CHECK(scalar == 0); + // BOOST_CHECK(decoratedScalar.accessData(0) == 0); + decoratedScalar.read(); + BOOST_CHECK( expectedReadValue == decoratedScalar.accessData(0) ); + + decoratedScalar.accessData(0) = startWriteValue; + decoratedScalar.write(); + anotherScalarAccessor.read(); + std::string readResponse = anotherScalarAccessor; + BOOST_CHECK( readResponse == exprectedWriteValue ); +} + + + BOOST_AUTO_TEST_CASE( testAllDecorators ){ testDecorator(12,12, 22, 22); testDecorator(13,13, 23, 23); @@ -46,7 +75,7 @@ BOOST_AUTO_TEST_CASE( testAllDecorators ){ // only use values which habe an exact representation in float/fixed point testDecorator(18.5,19, 28, 28); testDecorator(19.5,20, 29, 29); - // testDecorator(); + testDecorator(std::string("101"), 101, 102, std::string("102.000000")); } BOOST_AUTO_TEST_SUITE_END() From 90761a0be012fc947fb46348b9c5614696f68650 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 8 Sep 2017 10:27:47 +0200 Subject: [PATCH 254/786] refactored some tests for simpler code and better stability --- tests/src/testTypeChangingDecorator.cpp | 48 +++++++------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index ab1aa04..a010e95 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -8,14 +8,18 @@ using namespace ChimeraTK; #include +bool test_close(double a, double b, double tolerance = 0.0001){ + return (std::fabs(a - b) < tolerance ); +} + BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) template -void testDecorator(IMPL_T startReadValue, T expectedReadValue, T startWriteValue, IMPL_T exprectedWriteValue){ +void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue, double expectedWriteValue){ mtca4u::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); - auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); TypeChangingDecorator decoratedScalar(ndAccessor); @@ -25,46 +29,18 @@ void testDecorator(IMPL_T startReadValue, T expectedReadValue, T startWriteValue anotherScalarAccessor = startReadValue; anotherScalarAccessor.write(); - // BOOST_CHECK(scalar == 0); - // BOOST_CHECK(decoratedScalar.accessData(0) == 0); - decoratedScalar.read(); - BOOST_CHECK( expectedReadValue == decoratedScalar.accessData(0) ); - - decoratedScalar.accessData(0) = startWriteValue; - decoratedScalar.write(); - anotherScalarAccessor.read(); - BOOST_CHECK( exprectedWriteValue == anotherScalarAccessor ); -} - -template<> -void testDecorator(std::string startReadValue, int expectedReadValue, int startWriteValue, std::string exprectedWriteValue){ - mtca4u::Device d; - d.open("sdm://./dummy=decoratorTest.map"); - auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); - auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); - - auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); - TypeChangingDecorator decoratedScalar(ndAccessor); - - BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); - BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); - - anotherScalarAccessor = startReadValue; - anotherScalarAccessor.write(); - // BOOST_CHECK(scalar == 0); - // BOOST_CHECK(decoratedScalar.accessData(0) == 0); + // check that the values are different at start so we know the test is sensitive + BOOST_CHECK( !test_close(decoratedScalar.accessData(0), expectedReadValue) ); decoratedScalar.read(); - BOOST_CHECK( expectedReadValue == decoratedScalar.accessData(0) ); + // internal precision of the register is 16 fractional bits fixed point + BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue) ); decoratedScalar.accessData(0) = startWriteValue; decoratedScalar.write(); anotherScalarAccessor.read(); - std::string readResponse = anotherScalarAccessor; - BOOST_CHECK( readResponse == exprectedWriteValue ); + BOOST_CHECK( test_close(anotherScalarAccessor, expectedWriteValue) ); } - - BOOST_AUTO_TEST_CASE( testAllDecorators ){ testDecorator(12,12, 22, 22); testDecorator(13,13, 23, 23); @@ -75,7 +51,7 @@ BOOST_AUTO_TEST_CASE( testAllDecorators ){ // only use values which habe an exact representation in float/fixed point testDecorator(18.5,19, 28, 28); testDecorator(19.5,20, 29, 29); - testDecorator(std::string("101"), 101, 102, std::string("102.000000")); + testDecorator(101, 101, 102, 102); } BOOST_AUTO_TEST_SUITE_END() From 848d6b2080376513dd92e473a3dfa824c48da8f1 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 8 Sep 2017 10:47:56 +0200 Subject: [PATCH 255/786] added tests for floating point types in TypeChangingDecorator --- .../TypeChangingDecorator.h | 32 +++++++++++++++++++ tests/src/testTypeChangingDecorator.cpp | 23 ++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 0f5f5bf..020c269 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -213,6 +213,38 @@ namespace ChimeraTK { } } } + template<> + void TypeChangingDecorator::convertAndCopyFromImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = stringToT(_impl->accessChannel(i)[j]); + } + } + } + template<> + void TypeChangingDecorator::convertAndCopyToImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + _impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); + } + } + } + template<> + void TypeChangingDecorator::convertAndCopyFromImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = stringToT(_impl->accessChannel(i)[j]); + } + } + } + template<> + void TypeChangingDecorator::convertAndCopyToImpl() { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + _impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); + } + } + } } // namespace ChimeraTK diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index a010e95..50cb969 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -8,12 +8,14 @@ using namespace ChimeraTK; #include +// always test close for floating point values. Don't rely on exact binary representations bool test_close(double a, double b, double tolerance = 0.0001){ return (std::fabs(a - b) < tolerance ); } BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) +// the startReadValue and expectedWriteValue are from the register in the dummy device, which is 32 bit fixed point singed with 16 fractional bits, thus we talk to it as double from the test template void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue, double expectedWriteValue){ mtca4u::Device d; @@ -48,10 +50,29 @@ BOOST_AUTO_TEST_CASE( testAllDecorators ){ testDecorator(15,15, 25, 25); testDecorator(16,16, 26, 26); testDecorator(17,17, 27, 27); - // only use values which habe an exact representation in float/fixed point testDecorator(18.5,19, 28, 28); + testDecorator(18.4,18, 28, 28); testDecorator(19.5,20, 29, 29); + testDecorator(19.4,19, 29, 29); testDecorator(101, 101, 102, 102); + + testDecorator(112,112, -122.4, -122); + testDecorator(112,112, -122.5, -123); + testDecorator(113,113, 123.4, 123); + testDecorator(113,113, 123.5, 124); + testDecorator(114,114, -124.4, -124); + testDecorator(114,114, -124.5, -125); + testDecorator(115,115, 125.4, 125); + testDecorator(115,115, 125.5, 126); + testDecorator(116,116, -126.4, -126); + testDecorator(116,116, -126.5, -127); + testDecorator(117,117, 127.4, 127); + testDecorator(117,117, 127.5, 128); + testDecorator(118.5,118.5, 128.6, 128.6); + testDecorator(119.5,119.5, 129.6, 129.6); + testDecorator(101.1, 101.1, 102.2, 102.2); + testDecorator(201.1, 201.1, 202.2, 202.2); + } BOOST_AUTO_TEST_SUITE_END() From 42fda5a3a8dd24dad5ed8aa27c31fb842ab5b563 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 8 Sep 2017 15:23:48 +0200 Subject: [PATCH 256/786] improved test coverage of TypeChangingDecorator as much as reasonable with low effort --- tests/decoratorTest.map | 5 ++ tests/src/testTypeChangingDecorator.cpp | 89 +++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/tests/decoratorTest.map b/tests/decoratorTest.map index da62e58..2420f52 100644 --- a/tests/decoratorTest.map +++ b/tests/decoratorTest.map @@ -1,3 +1,8 @@ # name nr of elements address size bar width fracbits signed SOME.SCALAR 0x00000001 0x00000000 0x00000004 0x0 32 16 1 SOME.ARRAY 0x00000010 0x00000000 0x00000040 0x2 32 16 1 +#use BAR1 for 2d +SOME.AREA_MULTIPLEXED_SEQUENCE_TWO_D 6 0 24 1 +SOME.SEQUENCE_TWO_D_0 1 0 4 1 +SOME.SEQUENCE_TWO_D_1 1 4 4 1 +SOME.SEQUENCE_TWO_D_2 1 8 4 1 diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 50cb969..be8d05e 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -22,12 +22,17 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue d.open("sdm://./dummy=decoratorTest.map"); auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto anotherImplTAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); TypeChangingDecorator decoratedScalar(ndAccessor); BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); + + BOOST_CHECK( decoratedScalar.isReadable() ); + BOOST_CHECK( decoratedScalar.isWriteable() ); + BOOST_CHECK( !decoratedScalar.isReadOnly() ); anotherScalarAccessor = startReadValue; anotherScalarAccessor.write(); @@ -41,6 +46,41 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue decoratedScalar.write(); anotherScalarAccessor.read(); BOOST_CHECK( test_close(anotherScalarAccessor, expectedWriteValue) ); + + // repeat the read / write tests with all different functions + + // 1. The way a transfer group would call it: + anotherScalarAccessor = startReadValue+1; + anotherScalarAccessor.write(); + for (auto & hwAccessor : decoratedScalar.getHardwareAccessingElements()){ + hwAccessor->read(); + } + // still nothing has changed on the user buffer + BOOST_CHECK( test_close(decoratedScalar.accessData(0), startWriteValue) ); + decoratedScalar.postRead(); + BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue+1) ); + + decoratedScalar.accessData(0) = startWriteValue + 1 ; + decoratedScalar.preWrite(); + // nothing changed on the device yet + anotherScalarAccessor.read(); + BOOST_CHECK( test_close(anotherScalarAccessor, startReadValue+1) ); + for (auto & hwAccessor : decoratedScalar.getHardwareAccessingElements()){ + hwAccessor->write(); + } + decoratedScalar.postWrite(); + + anotherScalarAccessor.read(); + BOOST_CHECK( test_close(anotherScalarAccessor, expectedWriteValue+1) ); + + // FIXME: We cannot test that the decorator is relaying doReadTransfer, doReadTransferLatest and + // do readTransferLatest correctly with the dummy backend because they all point to the same + // implementation. This we intentionally do not call them to indicate them uncovered. + // We would have to use the control system adapter implementations with the queues to test it. + + BOOST_CHECK( decoratedScalar.isSameRegister( boost::dynamic_pointer_cast(anotherImplTAccessor.getHighLevelImplElement()) ) ); + + // OK, I give up. I would have to repeat all tests ever written for a decorator, incl. transfer group, persistentDataStorage and everything. All I can do is leave the stuff intentionally uncovered so a reviewer can find the places. } BOOST_AUTO_TEST_CASE( testAllDecorators ){ @@ -72,7 +112,56 @@ BOOST_AUTO_TEST_CASE( testAllDecorators ){ testDecorator(119.5,119.5, 129.6, 129.6); testDecorator(101.1, 101.1, 102.2, 102.2); testDecorator(201.1, 201.1, 202.2, 202.2); +} + +BOOST_AUTO_TEST_CASE( testLoops ){ + // Test loops for numeric data types. One type is enough because it's template code + // We don't have to test the string specialisations, arrays of strings are not allowed + mtca4u::Device d; + d.open("sdm://./dummy=decoratorTest.map"); + auto twoD = d.getTwoDRegisterAccessor("/SOME/TWO_D"); + auto anotherAccessor = d.getTwoDRegisterAccessor("/SOME/TWO_D"); + + anotherAccessor[0][0] = 100.0; + anotherAccessor[0][1] = 101.1; + anotherAccessor[1][0] = 110.0; + anotherAccessor[1][1] = 111.6; + anotherAccessor[2][0] = 120.0; + anotherAccessor[2][1] = 121.6; + anotherAccessor.write(); + + twoD.read(); + // device under test (dut) + auto impl = boost::dynamic_pointer_cast >(twoD.getHighLevelImplElement()); + TypeChangingDecorator dut( impl ); + + BOOST_REQUIRE( dut.getNumberOfChannels()==3); + BOOST_REQUIRE( dut.getNumberOfSamples()==2); + // check that constructor fills the buffers. Already is sufficuent as read test + BOOST_CHECK( dut.accessData(0,0) == 100); + BOOST_CHECK( dut.accessData(0,1) == 101); + BOOST_CHECK( dut.accessData(1,0) == 110); + BOOST_CHECK( dut.accessData(1,1) == 112); + BOOST_CHECK( dut.accessData(2,0) == 120); + BOOST_CHECK( dut.accessData(2,1) == 122); + + dut.accessData(0,0) = 200; + dut.accessData(0,1) = 201; + dut.accessData(1,0) = 210; + dut.accessData(1,1) = 212; + dut.accessData(2,0) = 220; + dut.accessData(2,1) = 222; + dut.write(); + + anotherAccessor.read(); + + BOOST_CHECK( test_close( anotherAccessor[0][0], 200 )); + BOOST_CHECK( test_close( anotherAccessor[0][1], 201 )); + BOOST_CHECK( test_close( anotherAccessor[1][0], 210 )); + BOOST_CHECK( test_close( anotherAccessor[1][1], 212 )); + BOOST_CHECK( test_close( anotherAccessor[2][0], 220 )); + BOOST_CHECK( test_close( anotherAccessor[2][1], 222 )); } BOOST_AUTO_TEST_SUITE_END() From 304b8ae0633a61960849b8d0ffccd18be25ad06a Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 8 Sep 2017 17:06:36 +0200 Subject: [PATCH 257/786] work around for a assertion sometimes failing. might actually be a bug in the llrfctlhzdr server --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 18be46e..c6b98e9 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -720,7 +720,12 @@ namespace ChimeraTK { bool queueHasData = (status != boost::future_status::timeout); // If postRead() has been called, there should be new data somewhere - assert(queueHasData || tripleBufferHasData); + /// @todo FIXME find this problem and make an assert again! + //assert(queueHasData || tripleBufferHasData); + if(!(queueHasData || tripleBufferHasData)) { + std::cout << "ERROR: assert(queueHasData || tripleBufferHasData) failed! Waiting for new data..." << std::endl; + future.wait(); + } // Determine whether to use the data from the queue or from the triple buffer. If in both places data is present, // use the older data (by the version number). From 9075799fab12459590c886e3475cdec1a6789fa0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 8 Sep 2017 17:10:59 +0200 Subject: [PATCH 258/786] increased patch level --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd54800..7966e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 06) -set(${PROJECT_NAME}_PATCH_VERSION 00) +set(${PROJECT_NAME}_PATCH_VERSION 01) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From 5a296eb1aab4cb4eed4f9f68f1c76f34757883f5 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 11 Sep 2017 15:59:37 +0200 Subject: [PATCH 259/786] added decorator with direct C-type conversion --- .../TypeChangingDecorator.h | 61 +++++++++++++++++-- tests/decoratorTest.map | 2 + tests/src/testTypeChangingDecorator.cpp | 55 ++++++++++++++++- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 020c269..6c9236c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -71,8 +71,8 @@ namespace ChimeraTK { return _impl->doReadTransferLatest(); } - void convertAndCopyFromImpl(); - void convertAndCopyToImpl(); + virtual void convertAndCopyFromImpl(); + virtual void convertAndCopyToImpl(); void postRead() override{ _impl->postRead(); @@ -117,6 +117,23 @@ namespace ChimeraTK { } protected: + /** Internal exceptions to overload the what() function of the boost exceptions in order to fill in the variable name. + * These exceptions are not part of the external interface and cannot be caught explicitly because they are protected. + * Catch boost::numeric::bad_numeric_cast and derrivatives if you want to do error handling. + */ + template + struct BadNumericCastException: public BOOST_EXCEPTION_T{ + BadNumericCastException(std::string variableName) : + errorMessage("Exception during type changing conversion in " + variableName + ": " + BOOST_EXCEPTION_T().what()){ + } + std::string errorMessage; + virtual const char* what() const throw() override{ + return errorMessage.c_str(); + } + }; + + + boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; template @@ -136,7 +153,7 @@ namespace ChimeraTK { template TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > & impl) noexcept - : _impl(impl) + : mtca4u::NDRegisterAccessor(impl->getName(), impl->getUnit(), impl->getDescription()), _impl(impl) { this->buffer_2D.resize(impl->getNumberOfChannels()); for (auto & channel : this->buffer_2D){ @@ -161,10 +178,18 @@ namespace ChimeraTK { typedef boost::numeric::converter, boost::numeric::def_overflow_handler, Round > FromImplConverter; //fixme: are iterartors more efficient? - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = FromImplConverter::convert(_impl->accessChannel(i)[j]); + try{ + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = FromImplConverter::convert(_impl->accessChannel(i)[j]); + } } + }catch(boost::numeric::positive_overflow &){ + throw( BadNumericCastException( this->getName() ) ); + }catch(boost::numeric::negative_overflow &){ + throw( BadNumericCastException( this->getName() ) ); + }catch(boost::numeric::bad_numeric_cast & boostException){ + throw( BadNumericCastException( this->getName() ) ); } } @@ -246,6 +271,30 @@ namespace ChimeraTK { } } + //Does not do proper type conversion but directly assigns the data types (C-style direct conversion). + //Probably the only useful screnario is the conversion int/uint if negative data should be + //displayed as the last half of the dynamic range, or vice versa. (e.g. 0xFFFFFFFF <-> -1). + template + class TypeChangingDirectCastDecorator : public TypeChangingDecorator { + using TypeChangingDecorator::TypeChangingDecorator; + + void convertAndCopyFromImpl() override { + //fixme: are iterartors more efficient? + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = this->_impl->accessChannel(i)[j]; + } + } + } + + void convertAndCopyToImpl() override { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->_impl->accessChannel(i)[j] = this->buffer_2D[i][j]; + } + } + } + }; } // namespace ChimeraTK diff --git a/tests/decoratorTest.map b/tests/decoratorTest.map index 2420f52..db2ccb8 100644 --- a/tests/decoratorTest.map +++ b/tests/decoratorTest.map @@ -1,5 +1,7 @@ # name nr of elements address size bar width fracbits signed SOME.SCALAR 0x00000001 0x00000000 0x00000004 0x0 32 16 1 +SOME.INT 0x00000001 0x00000004 0x00000004 0x0 32 0 1 +SOME.UINT 0x00000001 0x00000008 0x00000004 0x0 32 0 0 SOME.ARRAY 0x00000010 0x00000000 0x00000040 0x2 32 16 1 #use BAR1 for 2d SOME.AREA_MULTIPLEXED_SEQUENCE_TWO_D 6 0 24 1 diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index be8d05e..02c4acc 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -30,6 +30,8 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); + BOOST_CHECK( decoratedScalar.getName() == "/SOME/SCALAR" ); + BOOST_CHECK( decoratedScalar.isReadable() ); BOOST_CHECK( decoratedScalar.isWriteable() ); BOOST_CHECK( !decoratedScalar.isReadOnly() ); @@ -83,7 +85,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue // OK, I give up. I would have to repeat all tests ever written for a decorator, incl. transfer group, persistentDataStorage and everything. All I can do is leave the stuff intentionally uncovered so a reviewer can find the places. } -BOOST_AUTO_TEST_CASE( testAllDecorators ){ +BOOST_AUTO_TEST_CASE( testAllDecoratorConversions ){ testDecorator(12,12, 22, 22); testDecorator(13,13, 23, 23); testDecorator(14,14, 24, 24); @@ -164,6 +166,57 @@ BOOST_AUTO_TEST_CASE( testLoops ){ BOOST_CHECK( test_close( anotherAccessor[2][1], 222 )); } +#define CHECK_THROW_PRINT( command , exception_type) \ +try{ \ + command;\ + BOOST_ERROR( std::string(# command) + " did not throw as excepted." ); \ + }catch( exception_type & e){\ + std::cout << "** For manually checking the exeption message of " << # command <<":\n"\ + << " " << e.what() << std::endl; \ + }\ + + +BOOST_AUTO_TEST_CASE( testRangeChecks ){ + // Just a few tests where the type changing decorator with conversion should throw + // where the direct cast decorator changes the interpretation + mtca4u::Device d; + d.open("sdm://./dummy=decoratorTest.map"); + auto myInt = d.getScalarRegisterAccessor("/SOME/INT"); + auto myIntDummy = d.getScalarRegisterAccessor("/SOME/INT"); // the second accessor for the test + auto myUInt = d.getScalarRegisterAccessor("/SOME/UINT"); + auto myUIntDummy = d.getScalarRegisterAccessor("/SOME/UINT"); + + auto intNDAccessor = boost::dynamic_pointer_cast >(myInt.getHighLevelImplElement()); + std::cout << "the name is " << intNDAccessor->getName() << std::endl; + TypeChangingDecorator u2i(intNDAccessor); + TypeChangingDirectCastDecorator directU2i(intNDAccessor); // don't try this at home: putting the same NDAccessor into different decorators can cause trouble + + auto uintNDAccessor = boost::dynamic_pointer_cast >(myUInt.getHighLevelImplElement()); + TypeChangingDecorator i2u(uintNDAccessor); + TypeChangingDirectCastDecorator directI2u(uintNDAccessor); // don't try this at home: putting the same NDAccessor into different decorators can cause trouble + + // the bit content is the same, but the interpretation is different. + myIntDummy = 0xFFFFFFFF; + myIntDummy.write(); + myUIntDummy = 0xFFFFFFFF; + myUIntDummy.write(); + + CHECK_THROW_PRINT( u2i.read(), boost::numeric::negative_overflow ); + CHECK_THROW_PRINT( i2u.read(), boost::numeric::positive_overflow ); + BOOST_CHECK_NO_THROW( directI2u.read() ); + BOOST_CHECK_NO_THROW( directU2i.read() ); + BOOST_CHECK( directU2i.accessData(0) == 0xFFFFFFFF ); + BOOST_CHECK( directI2u.accessData(0) == -1 ); + + i2u.accessData(0) = 0xFFFFFFFE; + u2i.accessData(0) = 0xFFFFFFFE; + + CHECK_THROW_PRINT( i2u.write(), boost::numeric::negative_overflow ); + CHECK_THROW_PRINT( u2i.write(), boost::numeric::positive_overflow ); +} + + + BOOST_AUTO_TEST_SUITE_END() From 45dde33ab0c9acf65690d1fe51d53c2f89b3213f Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Tue, 12 Sep 2017 18:38:48 +0200 Subject: [PATCH 260/786] refactored the decorators to make the factory work without unnecessary code duplication --- .../TypeChangingDecorator.h | 264 +++++++++++------- tests/src/testTypeChangingDecorator.cpp | 33 ++- 2 files changed, 190 insertions(+), 107 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 6c9236c..e2e07fd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -20,24 +20,55 @@ //#include "ProcessVariableListener.h" //#include "TimeStampSource.h" //#include "PersistentDataStorage.h" +#include +#include namespace ChimeraTK { + template + T stringToT(std::string const & input){ + std::stringstream s; + s << input; + T t; + s >> t; + return t; + } + + template + std::string T_ToString(T input){ + std::stringstream s; + s << input; + std::string output; + s >> output; + return output; + } + template + struct Round { + static S nearbyint ( S s ){ + return round(s); + } + + typedef boost::mpl::integral_c round_style ; + }; + /** * Strictly this is not a real decorator. It provides an NDRegisterAccessor of a * different template type and accesses the original one as "backend" under the hood. * + * This is the base class which has pure virtual convertAndCopyFromImpl and convertAndCopyToImpl functions. + * Apart from this it implements all decorating functions which just pass through to the impl. + * It intentionally does not call shutdown in the constructor in order not to shadow the + * shutdown detection for the child classes, so it is not forgotten there. + * * This class is not thread-safe and should only be used from a single thread. - * At the moment, it is only returned by the ControlSystemPVManager. + * */ template class TypeChangingDecorator : public mtca4u::NDRegisterAccessor { public: - TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > & impl) noexcept; - - ~TypeChangingDecorator(); + TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept; bool isReadable() const override { return _impl->isReadable(); @@ -71,8 +102,8 @@ namespace ChimeraTK { return _impl->doReadTransferLatest(); } - virtual void convertAndCopyFromImpl(); - virtual void convertAndCopyToImpl(); + virtual void convertAndCopyFromImpl() = 0; + virtual void convertAndCopyToImpl() = 0; void postRead() override{ _impl->postRead(); @@ -117,6 +148,51 @@ namespace ChimeraTK { } protected: + + boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; + }; + + /** This class is intended as a base class and intentionally does not call shutdown in the destructor in order not to shadow the + * shutdown detection for the child classes, so it is not forgotten there. + * It provides only partial template specialisations for the string stuff. Don't directly instantiate this class. The factory would + * fail when looping over all implementation types. + */ + template + class TypeChangingStringImplDecorator: public TypeChangingDecorator{ + public: + using TypeChangingDecorator::TypeChangingDecorator; + }; + + template + class TypeChangingStringImplDecorator: public TypeChangingDecorator{ + public: + using TypeChangingDecorator::TypeChangingDecorator; + virtual void convertAndCopyFromImpl(){ + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = stringToT(this->_impl->accessChannel(i)[j]); + } + } + } + virtual void convertAndCopyToImpl(){ + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->_impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); + } + } + } + }; + + template + class TypeChangingRangeCheckingDecorator: public TypeChangingStringImplDecorator{ + public: + using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; + virtual void convertAndCopyFromImpl(); + virtual void convertAndCopyToImpl(); + virtual ~TypeChangingRangeCheckingDecorator(){ + this->shutdown(); + } + private: /** Internal exceptions to overload the what() function of the boost exceptions in order to fill in the variable name. * These exceptions are not part of the external interface and cannot be caught explicitly because they are protected. * Catch boost::numeric::bad_numeric_cast and derrivatives if you want to do error handling. @@ -133,55 +209,49 @@ namespace ChimeraTK { }; - - boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; - - template - struct Round { - static S nearbyint ( S s ){ - return round(s); - } - - typedef boost::mpl::integral_c round_style ; - }; + }; + template + class TypeChangingRangeCheckingDecorator: public TypeChangingStringImplDecorator{ + public: + using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; + virtual void convertAndCopyFromImpl(){ + TypeChangingStringImplDecorator::convertAndCopyFromImpl(); + } + virtual void convertAndCopyToImpl(){ + TypeChangingStringImplDecorator::convertAndCopyToImpl(); + } + virtual ~TypeChangingRangeCheckingDecorator(){ + this->shutdown(); + } }; + /*********************************************************************************************************************/ /*** Implementations of member functions below this line *************************************************************/ /*********************************************************************************************************************/ template - TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > & impl) noexcept + TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept : mtca4u::NDRegisterAccessor(impl->getName(), impl->getUnit(), impl->getDescription()), _impl(impl) { this->buffer_2D.resize(impl->getNumberOfChannels()); for (auto & channel : this->buffer_2D){ channel.resize(impl->getNumberOfSamples()); } - // FIXME: resize the buffers - // update the internal data buffer with the values from the impl - postRead(); } /*********************************************************************************************************************/ template - TypeChangingDecorator::~TypeChangingDecorator() { - this->shutdown(); - } - -/*********************************************************************************************************************/ - - template - void TypeChangingDecorator::convertAndCopyFromImpl() { + void TypeChangingRangeCheckingDecorator::convertAndCopyFromImpl() { typedef boost::numeric::converter, boost::numeric::def_overflow_handler, Round > FromImplConverter; //fixme: are iterartors more efficient? try{ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = FromImplConverter::convert(_impl->accessChannel(i)[j]); + this->buffer_2D[i][j] = FromImplConverter::convert(this->_impl->accessChannel(i)[j]); } } }catch(boost::numeric::positive_overflow &){ @@ -194,79 +264,12 @@ namespace ChimeraTK { } template - void TypeChangingDecorator::convertAndCopyToImpl() { + void TypeChangingRangeCheckingDecorator::convertAndCopyToImpl() { typedef boost::numeric::converter, boost::numeric::def_overflow_handler, Round > ToImplConverter; for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - _impl->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); - } - } - } - - template - T stringToT(std::string const & input){ - std::stringstream s; - s << input; - T t; - s >> t; - return t; - } - - template - std::string T_ToString(T input){ - std::stringstream s; - s << input; - std::string output; - s >> output; - return output; - } - - template<> - void TypeChangingDecorator::convertAndCopyFromImpl() { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = stringToT(_impl->accessChannel(i)[j]); - } - } - } - template<> - void TypeChangingDecorator::convertAndCopyToImpl() { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - _impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); - } - } - } - template<> - void TypeChangingDecorator::convertAndCopyFromImpl() { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = stringToT(_impl->accessChannel(i)[j]); - } - } - } - template<> - void TypeChangingDecorator::convertAndCopyToImpl() { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - _impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); - } - } - } - template<> - void TypeChangingDecorator::convertAndCopyFromImpl() { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = stringToT(_impl->accessChannel(i)[j]); - } - } - } - template<> - void TypeChangingDecorator::convertAndCopyToImpl() { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - _impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); + this->_impl->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); } } } @@ -276,6 +279,7 @@ namespace ChimeraTK { //displayed as the last half of the dynamic range, or vice versa. (e.g. 0xFFFFFFFF <-> -1). template class TypeChangingDirectCastDecorator : public TypeChangingDecorator { + public: using TypeChangingDecorator::TypeChangingDecorator; void convertAndCopyFromImpl() override { @@ -294,8 +298,70 @@ namespace ChimeraTK { } } } + ~TypeChangingDirectCastDecorator(){ + this->shutdown(); + } }; + template + class TypeChangingDirectCastDecorator: public TypeChangingStringImplDecorator{ + public: + using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; + virtual void convertAndCopyFromImpl(){ + TypeChangingStringImplDecorator::convertAndCopyFromImpl(); + } + virtual void convertAndCopyToImpl(){ + TypeChangingStringImplDecorator::convertAndCopyToImpl(); + } + virtual ~TypeChangingDirectCastDecorator(){ + this->shutdown(); + } + }; + + enum class DecoratorType{ range_checking, limiting, C_style_conversion}; + template + class DecoratorFactory{ + public: + DecoratorFactory(boost::shared_ptr< mtca4u::TransferElement > theImpl_, DecoratorType wantedDecoratorType_) + : theImpl(theImpl_), wantedDecoratorType(wantedDecoratorType_){} + boost::shared_ptr< mtca4u::TransferElement > theImpl; + DecoratorType wantedDecoratorType; + mutable boost::shared_ptr > createdDecorator; + + template + void operator()(PAIR&) const { + typedef typename PAIR::first_type TargetImplType; + if(typeid(TargetImplType) != theImpl->getValueType() ) return; + + if (wantedDecoratorType == DecoratorType::range_checking){ + std::cout << "creating TypeChangingRangeCheckingDecorator " << typeid(UserType).name() + << " " << typeid(TargetImplType).name() << std::endl; + createdDecorator.reset( new TypeChangingRangeCheckingDecorator< UserType, TargetImplType> ( + boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); + }else if( wantedDecoratorType == DecoratorType::C_style_conversion){ + std::cout << "creating TypeChangingDirectCastDecorator " << typeid(UserType).name() + << " " << typeid(TargetImplType).name() << std::endl; + createdDecorator.reset( new TypeChangingDirectCastDecorator< UserType, TargetImplType> ( + boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); + }else{ + std::cout << "whoopsy" << std::endl; + } + + + std::cout << "UserType id: " << typeid(UserType).name() << ", TargetType id is " << typeid(TargetImplType).name() << std::endl; + } + + }; + + + // the factory function. You don't have to care about the IMPL_Type when requesting a decorator + template + boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType = DecoratorType::range_checking ){ + DecoratorFactory< UserType > factory( transferElement.getHighLevelImplElement(), decoratorType ); + boost::fusion::for_each(mtca4u::userTypeMap(), factory); + return factory.createdDecorator; + } + } // namespace ChimeraTK #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 02c4acc..7fbd080 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -25,7 +25,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue auto anotherImplTAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); - TypeChangingDecorator decoratedScalar(ndAccessor); + TypeChangingRangeCheckingDecorator decoratedScalar(ndAccessor); BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); @@ -132,15 +132,15 @@ BOOST_AUTO_TEST_CASE( testLoops ){ anotherAccessor[2][1] = 121.6; anotherAccessor.write(); - twoD.read(); // device under test (dut) auto impl = boost::dynamic_pointer_cast >(twoD.getHighLevelImplElement()); - TypeChangingDecorator dut( impl ); + TypeChangingRangeCheckingDecorator dut( impl ); BOOST_REQUIRE( dut.getNumberOfChannels()==3); BOOST_REQUIRE( dut.getNumberOfSamples()==2); - - // check that constructor fills the buffers. Already is sufficuent as read test + + dut.read(); + BOOST_CHECK( dut.accessData(0,0) == 100); BOOST_CHECK( dut.accessData(0,1) == 101); BOOST_CHECK( dut.accessData(1,0) == 110); @@ -187,12 +187,11 @@ BOOST_AUTO_TEST_CASE( testRangeChecks ){ auto myUIntDummy = d.getScalarRegisterAccessor("/SOME/UINT"); auto intNDAccessor = boost::dynamic_pointer_cast >(myInt.getHighLevelImplElement()); - std::cout << "the name is " << intNDAccessor->getName() << std::endl; - TypeChangingDecorator u2i(intNDAccessor); + TypeChangingRangeCheckingDecorator u2i(intNDAccessor); TypeChangingDirectCastDecorator directU2i(intNDAccessor); // don't try this at home: putting the same NDAccessor into different decorators can cause trouble auto uintNDAccessor = boost::dynamic_pointer_cast >(myUInt.getHighLevelImplElement()); - TypeChangingDecorator i2u(uintNDAccessor); + TypeChangingRangeCheckingDecorator i2u(uintNDAccessor); TypeChangingDirectCastDecorator directI2u(uintNDAccessor); // don't try this at home: putting the same NDAccessor into different decorators can cause trouble // the bit content is the same, but the interpretation is different. @@ -215,7 +214,25 @@ BOOST_AUTO_TEST_CASE( testRangeChecks ){ CHECK_THROW_PRINT( u2i.write(), boost::numeric::positive_overflow ); } +BOOST_AUTO_TEST_CASE( testFactory ){ + mtca4u::Device d; + d.open("sdm://./dummy=decoratorTest.map"); + auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); + mtca4u::TransferElement & transferElement = scalar; + auto decoratedScalar = getDecorator(transferElement); + BOOST_CHECK( decoratedScalar ); + // factory must detect the type of scalar and create the correct decorator + auto castedScalar = boost::dynamic_pointer_cast< TypeChangingRangeCheckingDecorator< int, double> >( decoratedScalar) ; + BOOST_CHECK( castedScalar ); + + auto decoratedDirectConvertingScalar = getDecorator(transferElement, DecoratorType::C_style_conversion); + BOOST_CHECK( decoratedDirectConvertingScalar ); + + auto castedDCScalar = boost::dynamic_pointer_cast< TypeChangingDirectCastDecorator< int, double> >( decoratedDirectConvertingScalar) ; + BOOST_CHECK( castedDCScalar ); + +} BOOST_AUTO_TEST_SUITE_END() From 21ae20f79fc20c7d054d050b87c4ebe98c555932 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 13 Sep 2017 13:36:48 +0200 Subject: [PATCH 261/786] testing transfer group, and improved coverage for direct converting accessor --- .../TypeChangingDecorator.h | 4 +- tests/src/testTypeChangingDecorator.cpp | 45 ++++++++++++++++++- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index e2e07fd..53c5bf4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -344,10 +344,8 @@ namespace ChimeraTK { createdDecorator.reset( new TypeChangingDirectCastDecorator< UserType, TargetImplType> ( boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); }else{ - std::cout << "whoopsy" << std::endl; + throw mtca4u::NotImplementedException("TypeChangingDecorator with range limitation is not implemented yet."); } - - std::cout << "UserType id: " << typeid(UserType).name() << ", TargetType id is " << typeid(TargetImplType).name() << std::endl; } diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 7fbd080..36b1fac 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -7,6 +7,7 @@ using namespace boost::unit_test_framework; using namespace ChimeraTK; #include +#include // always test close for floating point values. Don't rely on exact binary representations bool test_close(double a, double b, double tolerance = 0.0001){ @@ -116,7 +117,8 @@ BOOST_AUTO_TEST_CASE( testAllDecoratorConversions ){ testDecorator(201.1, 201.1, 202.2, 202.2); } -BOOST_AUTO_TEST_CASE( testLoops ){ +template class DECORATOR_TYPE> +void loopTest(){ // Test loops for numeric data types. One type is enough because it's template code // We don't have to test the string specialisations, arrays of strings are not allowed mtca4u::Device d; @@ -134,7 +136,7 @@ BOOST_AUTO_TEST_CASE( testLoops ){ // device under test (dut) auto impl = boost::dynamic_pointer_cast >(twoD.getHighLevelImplElement()); - TypeChangingRangeCheckingDecorator dut( impl ); + DECORATOR_TYPE dut( impl ); BOOST_REQUIRE( dut.getNumberOfChannels()==3); BOOST_REQUIRE( dut.getNumberOfSamples()==2); @@ -166,6 +168,11 @@ BOOST_AUTO_TEST_CASE( testLoops ){ BOOST_CHECK( test_close( anotherAccessor[2][1], 222 )); } +BOOST_AUTO_TEST_CASE( testLoops ){ + loopTest(); + loopTest(); +} + #define CHECK_THROW_PRINT( command , exception_type) \ try{ \ command;\ @@ -214,6 +221,37 @@ BOOST_AUTO_TEST_CASE( testRangeChecks ){ CHECK_THROW_PRINT( u2i.write(), boost::numeric::positive_overflow ); } +BOOST_AUTO_TEST_CASE( testTransferGroup ){ + mtca4u::Device d; + d.open("sdm://./dummy=decoratorTest.map"); + auto partial0 = d.getScalarRegisterAccessor("/SOME/ARRAY"); + auto partial1 = d.getScalarRegisterAccessor("/SOME/ARRAY",1); + + auto wholeArray = d.getOneDRegisterAccessor("/SOME/ARRAY"); + wholeArray[0]=12345; + wholeArray[1]=12346; + wholeArray.write(); + + auto decorated0 = getDecorator(partial0); + auto decorated1 = getDecorator(partial1); + + TransferGroup group; + group.addAccessor(*decorated0); + group.addAccessor(*decorated1); + group.read(); + BOOST_CHECK( decorated0->accessData(0) == 12345 ); + BOOST_CHECK( decorated1->accessData(0) == 12346 ); + + decorated0->accessData(0)=4321; + decorated1->accessData(0)=4322; + group.write(); + + wholeArray.read(); + std::cout << "whole Array " << wholeArray[0] << " " << wholeArray[1] << std::endl; + BOOST_CHECK( test_close( wholeArray[0], 4321) ); + BOOST_CHECK( test_close( wholeArray[1], 4322) ); +} + BOOST_AUTO_TEST_CASE( testFactory ){ mtca4u::Device d; d.open("sdm://./dummy=decoratorTest.map"); @@ -232,6 +270,9 @@ BOOST_AUTO_TEST_CASE( testFactory ){ auto castedDCScalar = boost::dynamic_pointer_cast< TypeChangingDirectCastDecorator< int, double> >( decoratedDirectConvertingScalar) ; BOOST_CHECK( castedDCScalar ); + + // fixme: at the moment we are throwing if a limiting decorator is requested + CHECK_THROW_PRINT( getDecorator(transferElement, DecoratorType::limiting), mtca4u::NotImplementedException ); } BOOST_AUTO_TEST_SUITE_END() From 7ae8bd3835e70e65028075867f69f3fa7a078091 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 13 Sep 2017 13:56:34 +0200 Subject: [PATCH 262/786] testing string template specialisation for TypeChangingDirectCastDecorator --- tests/src/testTypeChangingDecorator.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 36b1fac..5c808d9 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -17,7 +17,7 @@ bool test_close(double a, double b, double tolerance = 0.0001){ BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) // the startReadValue and expectedWriteValue are from the register in the dummy device, which is 32 bit fixed point singed with 16 fractional bits, thus we talk to it as double from the test -template +template class DECORATOR_TYPE = TypeChangingRangeCheckingDecorator> void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue, double expectedWriteValue){ mtca4u::Device d; d.open("sdm://./dummy=decoratorTest.map"); @@ -26,7 +26,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue auto anotherImplTAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); - TypeChangingRangeCheckingDecorator decoratedScalar(ndAccessor); + DECORATOR_TYPE decoratedScalar(ndAccessor); BOOST_REQUIRE( decoratedScalar.getNumberOfChannels()==1); BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); @@ -115,6 +115,13 @@ BOOST_AUTO_TEST_CASE( testAllDecoratorConversions ){ testDecorator(119.5,119.5, 129.6, 129.6); testDecorator(101.1, 101.1, 102.2, 102.2); testDecorator(201.1, 201.1, 202.2, 202.2); + + testDecorator(201, 201, 202, 202); + testDecorator(201, 201, 202, 202); + testDecorator(201, 201, 202, 202); + testDecorator(218.5,218, 228, 228); + testDecorator(228, 228, 229.6, 229); + } template class DECORATOR_TYPE> From 11094e8be54f37ea0fa9ec95643b05c77aa25ab5 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 13 Sep 2017 15:21:37 +0200 Subject: [PATCH 263/786] write out persistency file every 30 seconds - this is currently a dirty hack and has to be fixed and tested! --- .../PersistentDataStorage.h | 6 ++++++ src/PersistentDataStorage.cc | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index 27d7c69..1c9b550 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,11 @@ namespace ChimeraTK { /** boost::fusion::map of the data type to the DataMap holding the values for the type */ mtca4u::TemplateUserTypeMap _dataMap; + + /** */ + boost::thread writerThread; + + void writerThreadFunction(); }; diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index 5b765a9..c8d031e 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -20,16 +20,33 @@ namespace ChimeraTK { _filename = applicationName+".persist"; _applicationName = applicationName; readFromFile(); + + writerThread = boost::thread([this]{this->writerThreadFunction();}); } /*********************************************************************************************************************/ PersistentDataStorage::~PersistentDataStorage() { + writerThread.interrupt(); + writerThread.join(); writeToFile(); } /*********************************************************************************************************************/ + void PersistentDataStorage::writerThreadFunction() { + while(true) { + for(int i=0; i<30; ++i) { + sleep(1); + boost::this_thread::interruption_point(); + } + /// @todo FIXME make the variable access proper for a multi-threaded environment!!! + writeToFile(); + } + } + + /*********************************************************************************************************************/ + void PersistentDataStorage::writeToFile() { // create XML document with root node and a flat list of variables below this root From 6288b476a2403e5ba8dd06811d2c3b92ee462df7 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 13 Sep 2017 15:24:33 +0200 Subject: [PATCH 264/786] increased minor version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cfc712..7fe3bbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 06) -set(${PROJECT_NAME}_PATCH_VERSION 01) +set(${PROJECT_NAME}_MINOR_VERSION 07) +set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From ccad5047e85f3ec32b7910904d546d7eaf5d745a Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 13 Sep 2017 15:38:08 +0200 Subject: [PATCH 265/786] improved documentation and fixed intended inheritance of the decorators --- .../TypeChangingDecorator.h | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 53c5bf4..eb49279 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -163,6 +163,7 @@ namespace ChimeraTK { using TypeChangingDecorator::TypeChangingDecorator; }; + /** The actual partial implementation for strings as impl type **/ template class TypeChangingStringImplDecorator: public TypeChangingDecorator{ public: @@ -182,7 +183,12 @@ namespace ChimeraTK { } } }; - + + /** This decorator uses the boost numeric converter which performs two tasks: + * + * - a range check (throws boost::numeric::bad_numeric_cast if out of range) + * - the rounding floating point -> int is done mathematically (not just cut off the bits) + */ template class TypeChangingRangeCheckingDecorator: public TypeChangingStringImplDecorator{ public: @@ -274,13 +280,15 @@ namespace ChimeraTK { } } - //Does not do proper type conversion but directly assigns the data types (C-style direct conversion). - //Probably the only useful screnario is the conversion int/uint if negative data should be - //displayed as the last half of the dynamic range, or vice versa. (e.g. 0xFFFFFFFF <-> -1). + /** This decorator does not do mathematical rounding and range checking, but + * directly assigns the data types (C-style direct conversion). + * Probably the only useful screnario is the conversion int/uint if negative data should be + * displayed as the last half of the dynamic range, or vice versa. (e.g. 0xFFFFFFFF <-> -1). + */ template - class TypeChangingDirectCastDecorator : public TypeChangingDecorator { + class TypeChangingDirectCastDecorator : public TypeChangingStringImplDecorator { public: - using TypeChangingDecorator::TypeChangingDecorator; + using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; void convertAndCopyFromImpl() override { //fixme: are iterartors more efficient? @@ -303,6 +311,8 @@ namespace ChimeraTK { } }; + /** Partial template specialisation for strings as impl type. + */ template class TypeChangingDirectCastDecorator: public TypeChangingStringImplDecorator{ public: From 8a80bffb28a12888b4ffcb7506a1632bc5b38c10 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 14 Sep 2017 18:18:36 +0200 Subject: [PATCH 266/786] removed debug output --- .../ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index eb49279..ce96484 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -344,19 +344,14 @@ namespace ChimeraTK { if(typeid(TargetImplType) != theImpl->getValueType() ) return; if (wantedDecoratorType == DecoratorType::range_checking){ - std::cout << "creating TypeChangingRangeCheckingDecorator " << typeid(UserType).name() - << " " << typeid(TargetImplType).name() << std::endl; createdDecorator.reset( new TypeChangingRangeCheckingDecorator< UserType, TargetImplType> ( boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); }else if( wantedDecoratorType == DecoratorType::C_style_conversion){ - std::cout << "creating TypeChangingDirectCastDecorator " << typeid(UserType).name() - << " " << typeid(TargetImplType).name() << std::endl; createdDecorator.reset( new TypeChangingDirectCastDecorator< UserType, TargetImplType> ( boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); }else{ throw mtca4u::NotImplementedException("TypeChangingDecorator with range limitation is not implemented yet."); } - std::cout << "UserType id: " << typeid(UserType).name() << ", TargetType id is " << typeid(TargetImplType).name() << std::endl; } }; From 1101d42f03aab775b152851d0c90e1ac8b43edb6 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 18 Sep 2017 11:45:07 +0200 Subject: [PATCH 267/786] added two missing functions which need to be decorated. readAsync still missing, also needs decoration --- .../TypeChangingDecorator.h | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index ce96484..7eaa669 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -70,52 +70,52 @@ namespace ChimeraTK { TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept; - bool isReadable() const override { + virtual bool isReadable() const override { return _impl->isReadable(); } - bool isWriteable() const override { + virtual bool isWriteable() const override { return _impl->isWriteable(); } - bool isReadOnly() const override { + virtual bool isReadOnly() const override { return _impl->isReadOnly(); } - mtca4u::TimeStamp getTimeStamp() const override { + virtual mtca4u::TimeStamp getTimeStamp() const override { return _impl->getTimeStamp(); } - ChimeraTK::VersionNumber getVersionNumber() const override { + virtual ChimeraTK::VersionNumber getVersionNumber() const override { return _impl->getVersionNumber(); } - void doReadTransfer() override{ + virtual void doReadTransfer() override{ _impl->doReadTransfer(); } - bool doReadTransferNonBlocking() override{ + virtual bool doReadTransferNonBlocking() override{ return _impl->doReadTransferNonBlocking(); } - bool doReadTransferLatest() override{ + virtual bool doReadTransferLatest() override{ return _impl->doReadTransferLatest(); } virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; - void postRead() override{ + virtual void postRead() override{ _impl->postRead(); convertAndCopyFromImpl(); } - void preWrite() override{ + virtual void preWrite() override{ convertAndCopyToImpl(); _impl->preWrite(); } - void postWrite() override{ + virtual void postWrite() override{ _impl->postWrite(); } @@ -126,27 +126,35 @@ namespace ChimeraTK { return _impl->write(versionNumber); } - bool isArray() const override { + virtual bool isArray() const override { return _impl->isArray(); } /// @todo FIXME: can we unite stuff in a transfer group? Compare with impl? - bool isSameRegister(const boost::shared_ptr& e) const override { + virtual bool isSameRegister(const boost::shared_ptr& e) const override { return _impl->isSameRegister(e); } - std::vector > getHardwareAccessingElements() override { + virtual std::vector > getHardwareAccessingElements() override { return _impl->getHardwareAccessingElements(); } - void replaceTransferElement(boost::shared_ptr e) override { + virtual void replaceTransferElement(boost::shared_ptr e) override { _impl->replaceTransferElement(e); } - void setPersistentDataStorage(boost::shared_ptr storage) override{ + virtual void setPersistentDataStorage(boost::shared_ptr storage) override{ _impl->setPersistentDataStorage(storage); } + virtual unsigned int getNInputQueueElements() const override { + return _impl->getNInputQueueElements(); + } + + virtual mtca4u::FixedPointConverter getFixedPointConverter() const override { + return _impl->getFixedPointConverter(); + } + protected: boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; From ae7e7a9494776ae0acdf5467ac8aa6a0cdf117b8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 9 Oct 2017 11:01:45 +0200 Subject: [PATCH 268/786] fixed indentation --- .../Testing/ReferenceTestApplication.h | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h index fcf281d..b277b34 100644 --- a/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h +++ b/tests/include/ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h @@ -48,22 +48,22 @@ struct TypedPVHolder{ constantArray( processVariableManager->createProcessArray( ChimeraTK::deviceToControlSystem, typeNamePrefix + "/CONSTANT_ARRAY",10) ) { - if (std::numeric_limits::is_integer){ - if (std::numeric_limits::is_signed){ - // signed int - dataTypeConstant->accessData(0) = static_cast(-sizeof(DataType)); - }else{ - // unsigned int - dataTypeConstant->accessData(0) = sizeof(DataType); - } + if (std::numeric_limits::is_integer){ + if (std::numeric_limits::is_signed){ + // signed int + dataTypeConstant->accessData(0) = static_cast(-sizeof(DataType)); }else{ - // floating point - dataTypeConstant->accessData(0) = 1./sizeof(DataType); - } - for (size_t i = 0; i < constantArray->accessChannel(0).size(); ++i){ - constantArray->accessChannel(0)[i] = dataTypeConstant->accessData(0)*i*i; + // unsigned int + dataTypeConstant->accessData(0) = sizeof(DataType); } + }else{ + // floating point + dataTypeConstant->accessData(0) = 1./sizeof(DataType); } + for (size_t i = 0; i < constantArray->accessChannel(0).size(); ++i){ + constantArray->accessChannel(0)[i] = dataTypeConstant->accessData(0)*i*i; + } + } void inputToOutput(){ fromDeviceScalar->accessChannel(0) = toDeviceScalar->accessChannel(0); @@ -192,10 +192,10 @@ inline void ReferenceTestApplication::mainLoop(){ mainLoopExecutionRequested() = false; initalisationForManualLoopControlFinished() = true; do{ - mainLoopMutex().unlock(); - boost::this_thread::sleep_for( boost::chrono::microseconds(10) ); - mainLoopMutex().lock(); - } while( !mainLoopExecutionRequested() ); + mainLoopMutex().unlock(); + boost::this_thread::sleep_for( boost::chrono::microseconds(10) ); + mainLoopMutex().lock(); + } while( !mainLoopExecutionRequested() ); }else{ boost::this_thread::sleep_for( boost::chrono::milliseconds(100) ); } From c8fafb0f6b765b3e58556f643d8a2f4b6038eab0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 9 Oct 2017 16:33:06 +0200 Subject: [PATCH 269/786] update for latest change in DeviceAccess --- tests/src/testAsyncRead.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index a978bb4..b7f8461 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -72,19 +72,24 @@ void AsyncReadTest::testAsyncRead() { senderAccessor = 5; senderAccessor.write(); future = &(accessor.readAsync()); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); future->wait(); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); BOOST_CHECK( accessor == 5 ); senderAccessor = 6; senderAccessor.write(); future = &(accessor.readAsync()); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); future->wait(); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); BOOST_CHECK( accessor == 6 ); // check that future's wait() function won't return before the read is complete for(int i=0; i<5; ++i) { senderAccessor = 42+i; future = &(accessor.readAsync()); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); std::atomic flag; flag = false; std::thread thread([&future, &flag] { future->wait(); flag = true; }); @@ -93,21 +98,26 @@ void AsyncReadTest::testAsyncRead() { senderAccessor.write(); thread.join(); BOOST_CHECK( accessor == 42+i ); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); } // check that obtaining the same future multiple times works properly senderAccessor = 666; for(int i=0; i<5; ++i) { future = &(accessor.readAsync()); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); BOOST_CHECK( accessor == 46 ); // still the old value from the last test part } senderAccessor.write(); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); future->wait(); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); BOOST_CHECK( accessor == 666 ); // now try another asynchronous transfer senderAccessor = 999; future = &(accessor.readAsync()); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); std::atomic flag; flag = false; std::thread thread([&future, &flag] { future->wait(); flag = true; }); @@ -115,6 +125,7 @@ void AsyncReadTest::testAsyncRead() { BOOST_CHECK(flag == false); senderAccessor.write(); thread.join(); + BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); BOOST_CHECK( accessor == 999 ); } @@ -272,12 +283,12 @@ void AsyncReadTest::testReadAny() { s2.write(); // no point to use a thread here auto r = TransferElement::readAny({a1,a2,a3,a4}); - BOOST_CHECK(a1.isSameRegister(r)); + BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 123); r = TransferElement::readAny({a1,a2,a3,a4}); - BOOST_CHECK(a2.isSameRegister(r)); + BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); @@ -300,28 +311,28 @@ void AsyncReadTest::testReadAny() { // no point to use a thread here auto r = TransferElement::readAny({a1,a2,a3,a4}); - BOOST_CHECK(a4.isSameRegister(r)); + BOOST_CHECK(a4.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); r = TransferElement::readAny({a1,a2,a3,a4}); - BOOST_CHECK(a2.isSameRegister(r)); + BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); r = TransferElement::readAny({a1,a2,a3,a4}); - BOOST_CHECK(a3.isSameRegister(r)); + BOOST_CHECK(a3.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 333); BOOST_CHECK(a4 == 111); r = TransferElement::readAny({a1,a2,a3,a4}); - BOOST_CHECK(a1.isSameRegister(r)); + BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 444); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 333); From bde692eea5b6fb0f1e438164276e92b06eb7477c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 10 Oct 2017 16:45:25 +0200 Subject: [PATCH 270/786] Fixed wrong copy constructor of ProcessArray::Buffer - by removing it entirely, since it is not used. --- .../ControlSystemAdapter/ProcessArray.h | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index c6b98e9..90cf0ab 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -191,8 +191,28 @@ namespace ChimeraTK { */ struct Buffer : public TransferFuture::Data { - TimeStamp timeStamp; + /** + * Default constructor. Has to be defined explicitly because we delete our copy constructor. + */ + Buffer() + : TransferFuture::Data({}) + {} + + /** + * Delete all copy and move constructors and assignment operators. In C++11, elements of a std::vector no longer + * have to be copy-constructable if the size of the vector is fixed and specified in the constructor. + */ + Buffer(const Buffer &other) = delete; + Buffer& operator=(const Buffer &other) = delete; + Buffer(Buffer &&other) = delete; + Buffer& operator=(Buffer &&other) = delete; + + /** The actual data contained in this buffer. */ std::vector value; + + /** Flag whether this buffer currently contains valid data. This flag is used only for the atomic triple buffer + * and not set for buffers on the queues (since the queue the buffer is in already tells whether it contains + * valid data or not). */ bool _isValid{false}; /** @@ -206,32 +226,8 @@ namespace ChimeraTK { */ uint64_t _internalVersionNumber; - /** - * Default constructor. Has to be defined explicitly because we have an - * non-default copy constructor. - */ - Buffer() - : TransferFuture::Data({}) - {} - - /** - * Copy constructor. Some STL implementations need the copy constructor - * while initializing the vector that stores the buffers: They do not - * default construct each element but only default construct the first - * element and then copy it. In this case, this copy constructor will - * work perfectly fine. It would even work if the value pointer was - * already initialized with a vector, however we might have a serious - * performance problem if that happened. Therefore, we use an assertion - * to check that the copy constructor is never used once the buffer has - * been initialized with a vector. - */ - Buffer(Buffer const & other) - : timeStamp(other.timeStamp), - value(other.value ? new std::vector(*(other.value)) : 0), - TransferFuture::Data(other._versionNumber) - { - assert(!(other.value)); - } + /** Time stamp associated with this buffer */ + TimeStamp timeStamp; }; From 4cddd4961bc58435531c892364eb91550d61227d Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 11 Oct 2017 14:42:01 +0200 Subject: [PATCH 271/786] increased minor version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fe3bbb..b5a1d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 07) +set(${PROJECT_NAME}_MINOR_VERSION 08) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) From a247752e50d32f26854d4849a3b3335aabb8dce1 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 12 Oct 2017 13:40:58 +0200 Subject: [PATCH 272/786] Changed the default value of the initialValue argument of createProcessArray() from "0" to "T()", which is equivalent for all numeric data types but also works for std::strings. Before using the default value for T=std::string was leading to a std::logic_error. --- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 90cf0ab..752fb8f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -408,7 +408,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", T initialValue = 0, std::size_t numberOfBuffers = 2, + const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = From 948af914d2e6b2c1621328a7f54778d394f97aaf Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Mon, 23 Oct 2017 16:36:24 +0200 Subject: [PATCH 273/786] Improve comments (issue #10). There were a few misleading comments in the ProcessArray class template that did not correctly reflect the current implementation. These comments have been changed to match the actual implementation. --- .../ControlSystemAdapter/ProcessArray.h | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 752fb8f..44a54fc 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -271,19 +271,19 @@ namespace ChimeraTK { std::vector _buffers; /** - * Special atomic triple buffer to be filled when the queue runs over (i.e. all normal buffers are full). - * After construction, buffer 2 is owned by the sender, 3 by the shared state and 4 by the receiver. A buffer is - * considered containing valid data if its version number is valid. Default constructed version numbers are - * invalid, so all buffers are flagged as invalid upon construction. + * Special atomic triple buffer to be filled when the queue runs over + * (i.e. all normal buffers are full). After construction, buffer 2 is + * owned by the sender, 3 by the shared state and 4 by the receiver. A + * buffer is considered containing valid data if its _isValid flag is + * set. The buffer's default constructor sets _isValid to false. */ std::atomic _tripleBufferIndex; /** * Queue holding the indices of the full buffers. Those are the buffers - * that have been sent but not yet received. We do not use an spsc_queue - * for this queue, because might want to take elements from the sending - * thread, so there are two threads which might consume elements and thus - * an spsc_queue is not safe. + * that have been sent but not yet received. We can use an spsc_queue for + * this queue because it is only filled by the sending process array and + * only consumed by the receiving process array. */ boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; @@ -319,7 +319,10 @@ namespace ChimeraTK { Buffer *_currentIndex; /** - * Index into the _tripleBuffer array that is currently owned by this instance. + * Index into the _buffers array that is currently owned by this instance. + * The _tripleBufferIndex is different from the _currentIndex because it is + * only used as a fallback when a value cannot be transferred through the + * _fullBufferQueue because the _emptyBufferQueue is empty. */ Buffer *_tripleBufferIndex; From 6947507ae9edbbcf537a944dca2169f040745ad0 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 25 Oct 2017 17:10:50 +0200 Subject: [PATCH 274/786] Added map do store created decorators because multiple decorators don't work for a transfer element. Not tested at the moment and not working yet. --- .../TypeChangingDecorator.h | 42 ++++++++++++++++++- tests/src/testTypeChangingDecorator.cpp | 2 +- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 7eaa669..557d20d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -51,6 +51,18 @@ namespace ChimeraTK { typedef boost::mpl::integral_c round_style ; }; + enum class DecoratorType{ range_checking, limiting, C_style_conversion}; + + // An intermediate base class which is only typed on the user type and has a function to + // provide the decorator type. It is needed to have something to cast to when the + // IMPL_T has already been abstracted away (the implementations are all templated to two parameters) + template + class DecoratorTypeAccessor : public mtca4u::NDRegisterAccessor{ + public: + using mtca4u::NDRegisterAccessor::NDRegisterAccessor; + virtual DecoratorType getDecoratorType() = 0; + }; + /** * Strictly this is not a real decorator. It provides an NDRegisterAccessor of a * different template type and accesses the original one as "backend" under the hood. @@ -336,7 +348,13 @@ namespace ChimeraTK { } }; - enum class DecoratorType{ range_checking, limiting, C_style_conversion}; + /** Quasi singleton to have a unique, global map across UserType templated factories. + * We need it to loop up if a decorator has already been created for the transfer element, + * and return this if so. Multiple decorators for the same transfer element don't work. + */ + std::map< boost::shared_ptr, + boost::shared_ptr > & getGlobalDecoratorMap(); + template class DecoratorFactory{ public: @@ -368,6 +386,28 @@ namespace ChimeraTK { // the factory function. You don't have to care about the IMPL_Type when requesting a decorator template boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType = DecoratorType::range_checking ){ + // check if there already is a decorator for the transfer element + auto decoratorMapEntry = getGlobalDecoratorMap().find( transferElement.getHighLevelImplElement() ); + if (decoratorMapEntry != getGlobalDecoratorMap().end()){ + // There already is a decorator for this transfer element + // The decorator has to have a matching type, otherwise we can only throw + auto castedType = boost::dynamic_pointer_cast< DecoratorTypeAccessor< UserType > >(decoratorMapEntry->second); + if (castedType){// User type matches, but the decorator type also has to match + if (castedType->getDecoratorType() == decoratorType){ + // decorator matches, return it + return castedType; + }else{ + // sorry there is a decorator, but it's the wrong user type + throw std::logic_error("ChimeraTK::ControlSystemAdapter: Decorator for TransferElement " + + transferElement.getName() +" already exists as a different decorator type."); + } + }else{ + // sorry there is a decorator, but it's the wrong user type + throw std::logic_error("ChimeraTK::ControlSystemAdapter: Decorator for TransferElement " + + transferElement.getName() +" already exists with a different user type."); + } + } + DecoratorFactory< UserType > factory( transferElement.getHighLevelImplElement(), decoratorType ); boost::fusion::for_each(mtca4u::userTypeMap(), factory); return factory.createdDecorator; diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 5c808d9..d9774da 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -78,7 +78,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue // FIXME: We cannot test that the decorator is relaying doReadTransfer, doReadTransferLatest and // do readTransferLatest correctly with the dummy backend because they all point to the same - // implementation. This we intentionally do not call them to indicate them uncovered. + // implementation. Thus we intentionally do not call them to indicate them uncovered. // We would have to use the control system adapter implementations with the queues to test it. BOOST_CHECK( decoratedScalar.isSameRegister( boost::dynamic_pointer_cast(anotherImplTAccessor.getHighLevelImplElement()) ) ); From a3ed106d18f7fc438d483bcedb77f0bc5819c555 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 25 Oct 2017 17:15:43 +0200 Subject: [PATCH 275/786] added missing cc file with the map --- src/TypeChangingDecorator.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/TypeChangingDecorator.cc diff --git a/src/TypeChangingDecorator.cc b/src/TypeChangingDecorator.cc new file mode 100644 index 0000000..5219a7c --- /dev/null +++ b/src/TypeChangingDecorator.cc @@ -0,0 +1,14 @@ +#include "TypeChangingDecorator.h" + +namespace ChimeraTK{ + +// The global instance of the map +std::map< boost::shared_ptr, + boost::shared_ptr > globalDecoratorMap; + +std::map< boost::shared_ptr, boost::shared_ptr > & +getGlobalDecoratorMap(){ + return globalDecoratorMap; +} + +}// namespace ChimeraTK From a4f912113cc1b70a2f3f1d01dcf21bb356af2eee Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 25 Oct 2017 18:20:54 +0200 Subject: [PATCH 276/786] decorator map in the factory function is working --- .../TypeChangingDecorator.h | 20 ++++++++++++++++--- tests/src/testTypeChangingDecorator.cpp | 19 +++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 557d20d..6513b6c 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -60,7 +60,7 @@ namespace ChimeraTK { class DecoratorTypeAccessor : public mtca4u::NDRegisterAccessor{ public: using mtca4u::NDRegisterAccessor::NDRegisterAccessor; - virtual DecoratorType getDecoratorType() = 0; + virtual DecoratorType getDecoratorType() const = 0; }; /** @@ -76,7 +76,7 @@ namespace ChimeraTK { * */ template - class TypeChangingDecorator : public mtca4u::NDRegisterAccessor { + class TypeChangingDecorator : public DecoratorTypeAccessor { public: @@ -218,6 +218,9 @@ namespace ChimeraTK { virtual ~TypeChangingRangeCheckingDecorator(){ this->shutdown(); } + virtual DecoratorType getDecoratorType() const override{ + return DecoratorType::range_checking; + } private: /** Internal exceptions to overload the what() function of the boost exceptions in order to fill in the variable name. * These exceptions are not part of the external interface and cannot be caught explicitly because they are protected. @@ -250,6 +253,9 @@ namespace ChimeraTK { virtual ~TypeChangingRangeCheckingDecorator(){ this->shutdown(); } + virtual DecoratorType getDecoratorType() const override{ + return DecoratorType::range_checking; + } }; @@ -259,7 +265,7 @@ namespace ChimeraTK { template TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept - : mtca4u::NDRegisterAccessor(impl->getName(), impl->getUnit(), impl->getDescription()), _impl(impl) + : DecoratorTypeAccessor(impl->getName(), impl->getUnit(), impl->getDescription()), _impl(impl) { this->buffer_2D.resize(impl->getNumberOfChannels()); for (auto & channel : this->buffer_2D){ @@ -309,6 +315,9 @@ namespace ChimeraTK { class TypeChangingDirectCastDecorator : public TypeChangingStringImplDecorator { public: using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; + virtual DecoratorType getDecoratorType() const override{ + return DecoratorType::C_style_conversion; + } void convertAndCopyFromImpl() override { //fixme: are iterartors more efficient? @@ -346,6 +355,9 @@ namespace ChimeraTK { virtual ~TypeChangingDirectCastDecorator(){ this->shutdown(); } + virtual DecoratorType getDecoratorType() const override{ + return DecoratorType::C_style_conversion; + } }; /** Quasi singleton to have a unique, global map across UserType templated factories. @@ -386,6 +398,7 @@ namespace ChimeraTK { // the factory function. You don't have to care about the IMPL_Type when requesting a decorator template boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType = DecoratorType::range_checking ){ + // check if there already is a decorator for the transfer element auto decoratorMapEntry = getGlobalDecoratorMap().find( transferElement.getHighLevelImplElement() ); if (decoratorMapEntry != getGlobalDecoratorMap().end()){ @@ -410,6 +423,7 @@ namespace ChimeraTK { DecoratorFactory< UserType > factory( transferElement.getHighLevelImplElement(), decoratorType ); boost::fusion::for_each(mtca4u::userTypeMap(), factory); + getGlobalDecoratorMap()[transferElement.getHighLevelImplElement()]=factory.createdDecorator; return factory.createdDecorator; } diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index d9774da..6f53b6a 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -254,7 +254,6 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ group.write(); wholeArray.read(); - std::cout << "whole Array " << wholeArray[0] << " " << wholeArray[1] << std::endl; BOOST_CHECK( test_close( wholeArray[0], 4321) ); BOOST_CHECK( test_close( wholeArray[1], 4322) ); } @@ -271,15 +270,25 @@ BOOST_AUTO_TEST_CASE( testFactory ){ auto castedScalar = boost::dynamic_pointer_cast< TypeChangingRangeCheckingDecorator< int, double> >( decoratedScalar) ; BOOST_CHECK( castedScalar ); - auto decoratedDirectConvertingScalar = getDecorator(transferElement, DecoratorType::C_style_conversion); + // if there already is a decorator you cannot create another one with a different user type + // or decorator type + CHECK_THROW_PRINT( getDecorator(transferElement, DecoratorType::C_style_conversion), std::logic_error); + CHECK_THROW_PRINT( getDecorator(transferElement), std::logic_error); + // but you can get the same decorator again if you ask for it + auto sameDecorator = getDecorator(transferElement); + BOOST_CHECK( sameDecorator.get() == decoratedScalar.get() ); + + // Test for direct convertion decorator type + // we have to use a different transfer element for this to work + auto scalar2 = d.getScalarRegisterAccessor("/SOME/SCALAR"); + auto decoratedDirectConvertingScalar = getDecorator(scalar2, DecoratorType::C_style_conversion); BOOST_CHECK( decoratedDirectConvertingScalar ); - auto castedDCScalar = boost::dynamic_pointer_cast< TypeChangingDirectCastDecorator< int, double> >( decoratedDirectConvertingScalar) ; BOOST_CHECK( castedDCScalar ); - // fixme: at the moment we are throwing if a limiting decorator is requested - CHECK_THROW_PRINT( getDecorator(transferElement, DecoratorType::limiting), mtca4u::NotImplementedException ); + auto scalar3 = d.getScalarRegisterAccessor("/SOME/SCALAR"); + CHECK_THROW_PRINT( getDecorator(scalar3, DecoratorType::limiting), mtca4u::NotImplementedException ); } BOOST_AUTO_TEST_SUITE_END() From 3f50f0f70426cd8f4f3cbafe4eea8c49b558aa78 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 26 Oct 2017 13:11:42 +0200 Subject: [PATCH 277/786] code cleanup and commenting in TypeChangingDecorator --- .../TypeChangingDecorator.h | 113 ++++++++++-------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 6513b6c..039e326 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -1,57 +1,39 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H -//#include -//#include -//#include -//#include -//#include -// -//#include -//#include -//#include -//#include -//#include -//#include -// #include -//#include -// -//#include "ProcessVariableListener.h" -//#include "TimeStampSource.h" -//#include "PersistentDataStorage.h" #include #include namespace ChimeraTK { - template - T stringToT(std::string const & input){ - std::stringstream s; - s << input; - T t; - s >> t; - return t; - } + /** There are three types of TypeChanging decorators which do different data conversions + * from the user data type to the implementation data type. + * + * \li range_checking This decorator checks the limits and throws an exception of the range is exceeced (for instance if you cast a negative integer to an unsiged int, or 500 to int8_t). + * This decorator does proper rounding from floating point types t o integer (e.g. 6.7 to 7). + * \li limiting This decorator limits the data to the maximum possible in the target data type (for instance 500 will result in 127 in int8_t and 255 in uint8_t, -200 will be -128 in int8t, 0 in uint8_t). + * This decorator also does correct rounding from floating point to integer type. + * \li C_style_conversion This decorator does a direct cast like an assigment in C/C++ does it. For instance 500 (=0x1f4) will result in 0xf4 for an 8 bit integer, which is interpreted as 244 in uint8_t and -12 in int8_t. Digits after the decimal point are cut when converting a floating point value to an integer type. This decorator can be useful to display unsigned integers which use the full dynamic range in a control system which only supports signed data types (the user has to correctly interpret the 'wrong' representation), of for bit fields where it is acceptable to lose the higher bits. + */ + enum class DecoratorType{ range_checking, limiting, C_style_conversion}; - template - std::string T_ToString(T input){ - std::stringstream s; - s << input; - std::string output; - s >> output; - return output; - } - - template - struct Round { - static S nearbyint ( S s ){ - return round(s); - } - - typedef boost::mpl::integral_c round_style ; - }; + /** The factory function for type changing decorators. + * + * TypeChanging decorators take a transfer element (usually NDRegisterAccessor) and wraps it in an NDRegisterAccessor. + * It automatically performs the right type conversion (configurable as argument of the factory function). + * The decorator has it's own buffer of type UserType and synchronises it in the preWrite() and postRead() functions with the implementation. + * You don't have to care about the implementation type of the transfer element. + * The factory will automatically create the correct decorator. + * + * @param transferElement The TransferElement to be decorated. It can either be an NDRegisterAccessor (usually the case) or and NDRegisterAccessorBridge (but here the user already picks the type he wants). + * @param decoratorType The type of decorator you want (see description of DecoratorType) + */ + template + boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType = DecoratorType::range_checking ); - enum class DecoratorType{ range_checking, limiting, C_style_conversion}; + //=================================================================================================================================================== + //Everything from here are implementation details. The exact identity of the decorator should not matter to the user, it would break the abstraction. + //=================================================================================================================================================== // An intermediate base class which is only typed on the user type and has a function to // provide the decorator type. It is needed to have something to cast to when the @@ -183,6 +165,37 @@ namespace ChimeraTK { using TypeChangingDecorator::TypeChangingDecorator; }; + /// A sub-namespace in order not to expose the classes to the ChimeraTK namespace + namespace csa_helpers{ + + template + T stringToT(std::string const & input){ + std::stringstream s; + s << input; + T t; + s >> t; + return t; + } + + template + std::string T_ToString(T input){ + std::stringstream s; + s << input; + std::string output; + s >> output; + return output; + } + + template + struct Round { + static S nearbyint ( S s ){ + return round(s); + } + + typedef boost::mpl::integral_c round_style ; + }; + }// namespace csa_helpers + /** The actual partial implementation for strings as impl type **/ template class TypeChangingStringImplDecorator: public TypeChangingDecorator{ @@ -191,14 +204,14 @@ namespace ChimeraTK { virtual void convertAndCopyFromImpl(){ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = stringToT(this->_impl->accessChannel(i)[j]); + this->buffer_2D[i][j] = csa_helpers::stringToT(this->_impl->accessChannel(i)[j]); } } } virtual void convertAndCopyToImpl(){ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->_impl->accessChannel(i)[j] = T_ToString(this->buffer_2D[i][j]); + this->_impl->accessChannel(i)[j] = csa_helpers::T_ToString(this->buffer_2D[i][j]); } } } @@ -278,7 +291,7 @@ namespace ChimeraTK { template void TypeChangingRangeCheckingDecorator::convertAndCopyFromImpl() { typedef boost::numeric::converter, - boost::numeric::def_overflow_handler, Round > FromImplConverter; + boost::numeric::def_overflow_handler, csa_helpers::Round > FromImplConverter; //fixme: are iterartors more efficient? try{ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ @@ -298,7 +311,7 @@ namespace ChimeraTK { template void TypeChangingRangeCheckingDecorator::convertAndCopyToImpl() { typedef boost::numeric::converter, - boost::numeric::def_overflow_handler, Round > ToImplConverter; + boost::numeric::def_overflow_handler, csa_helpers::Round > ToImplConverter; for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ this->_impl->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); @@ -395,9 +408,9 @@ namespace ChimeraTK { }; - // the factory function. You don't have to care about the IMPL_Type when requesting a decorator + // the factory function. You don't have to care about the IMPL_Type when requesting a decorator. Implementation, declared at the top of this file template - boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType = DecoratorType::range_checking ){ + boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType){ // check if there already is a decorator for the transfer element auto decoratorMapEntry = getGlobalDecoratorMap().find( transferElement.getHighLevelImplElement() ); From 74c79b48217cadd6890723e0ecc881218a74d2ba Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 26 Oct 2017 13:54:22 +0200 Subject: [PATCH 278/786] adapted minimal required deviceaccess version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5a1d6c..e6fbff6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.25 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.26 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) From b6cf788aca92aee65a7730e1dfc9fce636a4aedb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 27 Oct 2017 10:11:56 +0200 Subject: [PATCH 279/786] corrected comments (issue #10): - proper explanation why _fullBufferQueue is a spsc_queue - _notificationPromise is for the future on the _fullBufferQueue - removed leftover commented code --- .../ControlSystemAdapter/ProcessArray.h | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 44a54fc..1805e14 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -280,10 +280,9 @@ namespace ChimeraTK { std::atomic _tripleBufferIndex; /** - * Queue holding the indices of the full buffers. Those are the buffers - * that have been sent but not yet received. We can use an spsc_queue for - * this queue because it is only filled by the sending process array and - * only consumed by the receiving process array. + * Queue holding the indices of the full buffers. Those are the buffers that have been sent but not yet received. + * We have to use an spsc_queue because the standard boost::lockfree::queue cannot hold futures (as they are not + * trivially assignable/destructable). */ boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; @@ -292,18 +291,9 @@ namespace ChimeraTK { * returned to the sender by the receiver. */ boost::lockfree::spsc_queue _emptyBufferQueue; - - /** - * Queue holding futures for notification. The receiver can obtain a future from this queue when the - * _fullBufferQueue is empty and wait on it until new data has been sent. - * - * We are using a shared future, since the spsc_queue expects a copy-constructable data type. Since the data - * type is not particularly expensive to copy, this should have no performance impact. - */ - //boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _notificationQueue; /** - * The promise corresponding to the unfulfilled future in the _notificationQueue. + * The promise corresponding to the unfulfilled future in the _fullBufferQueue. * This promise is basically only used by the sender. Only in the destructor of the receiver it is used to * shutdown properly, thus it must be in the shared state. */ From c5b057fcce6f56a1f589c42cbdd039f5fd7e7dfd Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 1 Nov 2017 11:43:23 +0100 Subject: [PATCH 280/786] increased minor version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6fbff6..e817fb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 08) +set(${PROJECT_NAME}_MINOR_VERSION 09) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) From 50685dd0fbc074ffb2ff9ec32ff0ee95a2fc1577 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Mon, 23 Oct 2017 18:56:51 +0200 Subject: [PATCH 281/786] Separate ProcessArray interface and implementation (#2). In order to support bidirectional process arrays, it makes sense to separate the interface and implementation of the ProcessArrays. This way, we can have different implementations for unidirectional and bidirectional ProcessArrays. --- .../ControlSystemAdapter/ProcessArray.h | 808 +--------------- .../UnidirectionalProcessArray.h | 875 ++++++++++++++++++ 2 files changed, 906 insertions(+), 777 deletions(-) create mode 100644 include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 1805e14..4666d33 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -24,11 +24,14 @@ namespace ChimeraTK { /** - * Array implementation of the ProcessVariable. This implementation is used - * for both use cases (sender and receiver). + * Array version of the ProcessVariable. This class mainly exists for + * historical reasons: Originally, there were different implementations for + * scalars and arrays. Now, only arrays exist, so all ProcessVariables are + * ProcessArrays. * * This class is not thread-safe and should only be used from a single thread. * + * TODO This comment might not belong here: * If no version number source is specified when creating an instance of this * class, version number checks are disabled. This means that a receive * operation will always proceed, regardless of the version number of the new @@ -62,75 +65,36 @@ namespace ChimeraTK { /** * Instance acts as the receiver in a sender / receiver pair. */ - RECEIVER + RECEIVER, + + /** + * Instance acts as both a sender and a receiver. This is only true for + * a process array that is part of a bidirectional pair. + */ + SENDER_RECEIVER + }; /** - * Creates a process array that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. + * Creates a process array of the specified type. */ - ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, - const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers); + ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, + const std::string &unit, const std::string &description); - /** - * Creates a process array that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the maySendDestructively flag is true, the - * {@link ProcessArray::writeDestructively()} method may be used to transfer values without - * copying but losing them on the sender side. - * - * The optional send-notification listener is notified every time the - * sender's {@link ProcessArray::write()} method is called. It can be - * used to queue a request for the receiver's - * readNonBlocking() method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - ProcessArray(InstanceType instanceType, bool maySendDestructively, - TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener, - ProcessArray::SharedPtr receiver); - - ~ProcessArray(); + virtual ~ProcessArray(); bool isReadable() const override { - return _instanceType == RECEIVER; + return _instanceType == RECEIVER || _instanceType == SENDER_RECEIVER; } bool isWriteable() const override { - return _instanceType == SENDER; + return _instanceType == SENDER || _instanceType == SENDER_RECEIVER; } bool isReadOnly() const override { return !isWriteable(); } - TimeStamp getTimeStamp() const override { - return _currentIndex->timeStamp; - } - - ChimeraTK::VersionNumber getVersionNumber() const override { - return _currentIndex->_versionNumber; - } - - void doReadTransfer() override; - - bool doReadTransferNonBlocking() override; - - bool doReadTransferLatest() override; - - mtca4u::TransferFuture& readAsync() override; - - void postRead() override; - - bool write(ChimeraTK::VersionNumber versionNumber={}) override; - /** * Sends the current value to the receiver. Returns true if an * empty buffer was available and false if no empty buffer was @@ -150,7 +114,7 @@ namespace ChimeraTK { * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ - bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}); /// @todo FIXME this function must be present in TransferElement already! + virtual bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) = 0; /// @todo FIXME this function must be present in TransferElement already! const std::type_info& getValueType() const override { return typeid(T); @@ -172,750 +136,40 @@ namespace ChimeraTK { void replaceTransferElement(boost::shared_ptr) override { // You can't replace anything here. Just do nothing. } - - void setPersistentDataStorage(boost::shared_ptr storage) override ; - - /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the - * same variable but different for any other process variable within the same process. The unique ID will not be - * persistent accross executions of the process. */ - size_t getUniqueId() const { - return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state - } private: - /** - * Type for the individual buffers. Each buffer stores a vector (wrapped in a Boost scoped pointer) and a time - * stamp. It extends the TransferFuture::Data type (which provides the version number) so it can be transported - * by a TransferFuture. - */ - struct Buffer : public TransferFuture::Data { - - /** - * Default constructor. Has to be defined explicitly because we delete our copy constructor. - */ - Buffer() - : TransferFuture::Data({}) - {} - - /** - * Delete all copy and move constructors and assignment operators. In C++11, elements of a std::vector no longer - * have to be copy-constructable if the size of the vector is fixed and specified in the constructor. - */ - Buffer(const Buffer &other) = delete; - Buffer& operator=(const Buffer &other) = delete; - Buffer(Buffer &&other) = delete; - Buffer& operator=(Buffer &&other) = delete; - - /** The actual data contained in this buffer. */ - std::vector value; - - /** Flag whether this buffer currently contains valid data. This flag is used only for the atomic triple buffer - * and not set for buffers on the queues (since the queue the buffer is in already tells whether it contains - * valid data or not). */ - bool _isValid{false}; - - /** - * Extra version number used to sort the values within the process variable (so this version is not globally - * valid). This number is needed in addition to the globally valid _versionNumber defined in - * TransferFuture::Data, because the globally valid version numebr can be controlled by the application and - * values with an older version number might be written to this process variable later. That later data with - * the older version number shall still be considered as last data e.g. in the context of readLatest(), i.e. - * whatever has been written to the process variable last should be read with readLatest(), even if it has - * the older version number. - */ - uint64_t _internalVersionNumber; - - /** Time stamp associated with this buffer */ - TimeStamp timeStamp; - - }; - /** * Type this instance is representing. */ InstanceType _instanceType; - /** - * Number of elements that each vector (and thus this array) has. - */ - std::size_t _vectorSize; - - /** - * Flag indicating whether the {@code sendDestructively} methods may be used - * on this process array. - */ - bool _maySendDestructively; - - /** - * Last internal version number written. See Buffer::_internalVersionNumber for more details. - */ - uint64_t _lastInternalVersionNumber{0}; - - /** - * The state shared between the sender and the receiver - */ - struct SharedState { - - SharedState(size_t numberOfBuffers) - : _buffers(numberOfBuffers + 2 + 3), // two additional buffers owned by sender and receiver, 3 more additional - // buffers for the atomic triple buffer - _tripleBufferIndex(&(_buffers[3])), - _fullBufferQueue(numberOfBuffers+1), - _emptyBufferQueue(numberOfBuffers) - {} - - /** - * Buffers that hold the actual values. - */ - std::vector _buffers; - - /** - * Special atomic triple buffer to be filled when the queue runs over - * (i.e. all normal buffers are full). After construction, buffer 2 is - * owned by the sender, 3 by the shared state and 4 by the receiver. A - * buffer is considered containing valid data if its _isValid flag is - * set. The buffer's default constructor sets _isValid to false. - */ - std::atomic _tripleBufferIndex; - - /** - * Queue holding the indices of the full buffers. Those are the buffers that have been sent but not yet received. - * We have to use an spsc_queue because the standard boost::lockfree::queue cannot hold futures (as they are not - * trivially assignable/destructable). - */ - boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; - - /** - * Queue holding the empty buffers. Those are the buffers that have been - * returned to the sender by the receiver. - */ - boost::lockfree::spsc_queue _emptyBufferQueue; - - /** - * The promise corresponding to the unfulfilled future in the _fullBufferQueue. - * This promise is basically only used by the sender. Only in the destructor of the receiver it is used to - * shutdown properly, thus it must be in the shared state. - */ - TransferFuture::PromiseType _notificationPromise; - - }; - boost::shared_ptr _sharedState; - - /** - * Index into the _buffers array that is currently owned by this instance - * and used for read and (possibly) write operations. - */ - Buffer *_currentIndex; - - /** - * Index into the _buffers array that is currently owned by this instance. - * The _tripleBufferIndex is different from the _currentIndex because it is - * only used as a fallback when a value cannot be transferred through the - * _fullBufferQueue because the _emptyBufferQueue is empty. - */ - Buffer *_tripleBufferIndex; - - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr _receiver; - - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; - - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; - - /** - * Persistent data storage which needs to be informed when the process variable is sent. - */ - boost::shared_ptr _persistentDataStorage; - - /** - * Variable ID for the persistent data storage - */ - size_t _persistentDataStorageID{0}; - - /** - * Internal implementation of the various {@code send} methods. All these - * methods basically do the same and only differ in whether the data in the - * sent array is copied to the buffer used as the new current buffer and - * which version number is used. - * - * The return value will be true, if older data was overwritten during the send operation, or otherwise false. - */ - bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy); - }; -/*********************************************************************************************************************/ -/*** Non-member functions below this line ****************************************************************************/ -/*********************************************************************************************************************/ - - /** - * Creates a synchronized process array. A synchronized process array works - * as a pair of two process arrays, where one process array acts as a sender - * and the other one acts as a receiver. - * - * The sender allows full read-write access. Changes that have been made to - * the sender can be sent to the receiver through the - * {@link ProcessArray::write()} method. The receiver can be updated with these - * changes by calling its readNonBlocking() method. - * - * The synchronization is implemented in a thread-safe manner, so that the - * sender and the receiver can safely be used by different threads without - * a mutex. However, both the sender and receiver each only support a single - * thread. This means that the sender or the receiver have to be protected - * with a mutex if more than one thread wants to access either of them. - * - * The number of buffers specifies how many buffers are allocated for the - * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::write()} can be called - * in a row without losing data when readNonBlocking() is not - * called in between. - * - * If the maySendDestructively flag is true (it is - * false by default), the {@link ProcessArray::writeDestructively()} method may - * be used to transfer values without copying but losing them on the sender - * side. - * - * The specified time-stamp source is used for determining the current time - * when sending a value. The receiver will be updated with this time stamp - * when receiving the value. If no time-stamp source is specified, the current - * system-time when the value is sent is used. - * - * The optional send-notification listener is notified every time the sender's - * {@link ProcessArray::write()} method is called. It can be used to queue a - * request for the receiver's readNonBlocking() method to be - * called. The process variable passed to the listener is the receiver and - * not the sender. - * - * The specified initial value is used for all the elements of the array. - */ - template - std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 2, - bool maySendDestructively = false, - TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = - ProcessVariableListener::SharedPtr()); - - /** - * Creates a synchronized process array. A synchronized process array works - * as a pair of two process arrays, where one process array acts as a sender - * and the other one acts as a receiver. - * - * The sender allows full read-write access. Changes that have been made to - * the sender can be sent to the receiver through the - * {@link ProcessArray::write()} method. The receiver can be updated with these - * changes by calling its readNonBlocking() method. - * - * The synchronization is implemented in a thread-safe manner, so that the - * sender and the receiver can safely be used by different threads without - * a mutex. However, both the sender and receiver each only support a single - * thread. This means that the sender or the receiver have to be protected - * with a mutex if more than one thread wants to access either of them. - * - * The number of buffers specifies how many buffers are allocated for the - * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::write()} can be called - * in a row without losing data when readNonBlocking() is not - * called in between. - * - * If the maySendDestructively flag is true (it is - * false by default), the {@link ProcessArray::writeDestructively()} method may - * be used to transfer values without copying but losing them on the sender - * side. - * - * The specified time-stamp source is used for determining the current time - * when sending a value. The receiver will be updated with this time stamp - * when receiving the value. If no time-stamp source is specified, the current - * system-time when the value is sent is used. - * - * The optional send-notification listener is notified every time the sender's - * {@link ProcessArray::write()} method is called. It can be used to queue a - * request for the receiver's readNonBlocking() method to be - * called. The process variable passed to the listener is the receiver and - * not the sender. - * - * The array's size is set to the number of elements stored in the vector - * provided for initialization and all elements are initialized with the - * values provided by this vector. - */ - template - std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, - TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = - ProcessVariableListener::SharedPtr()); - /*********************************************************************************************************************/ /*** Implementations of member functions below this line *************************************************************/ /*********************************************************************************************************************/ template - ProcessArray::ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, - const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers) - : mtca4u::NDRegisterAccessor(name, unit, description), - _instanceType(instanceType), - _vectorSize(initialValue.size()), - _maySendDestructively(false), - _sharedState(boost::make_shared(numberOfBuffers)), - _currentIndex(&(_sharedState->_buffers[0])), - _tripleBufferIndex(&(_sharedState->_buffers[4])) - { - try { - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != RECEIVER) { - throw std::invalid_argument("This constructor may only be used for a receiver process variable."); - } - // We need at least two buffers for the queue (so four buffers in total) - // in order to guarantee that we never have to block. - if (numberOfBuffers < 2) { - throw std::invalid_argument("The number of buffers must be at least two."); - } - // We have to limit the number of buffers because we cannot allocate - // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { - throw std::invalid_argument("The number of buffers is too large."); - } - // We have to initialize the buffers by copying in the initial vectors. - for (auto &i : _sharedState->_buffers) { - i.value = initialValue; - } - // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the - // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of - // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. - for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { - _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); - } - } - catch(...) { - this->shutdown(); - throw; - } - } - -/*********************************************************************************************************************/ - - template - ProcessArray::ProcessArray(InstanceType instanceType, bool maySendDestructively, - TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener, - ProcessArray::SharedPtr receiver) - : mtca4u::NDRegisterAccessor(receiver->getName(), receiver->getUnit(), receiver->getDescription()), - _instanceType(instanceType), - _vectorSize(receiver->_vectorSize), - _maySendDestructively(maySendDestructively), - _sharedState(receiver->_sharedState), - _currentIndex(&(_sharedState->_buffers[1])), - _tripleBufferIndex(&(_sharedState->_buffers[2])), - _receiver(receiver), - _timeStampSource(timeStampSource), - _sendNotificationListener(sendNotificationListener) - { - try { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (instanceType != SENDER) { - throw std::invalid_argument("This constructor may only be used for a sender process variable."); - } - if (!receiver) { - throw std::invalid_argument("The pointer to the receiver must not be null."); - } - if (receiver->_instanceType != RECEIVER) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; - // put future into the notification queue - _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); - } - catch(...) { - this->shutdown(); - throw; - } + ProcessArray::ProcessArray(InstanceType instanceType, + const mtca4u::RegisterPath& name, const std::string &unit, + const std::string &description) : + mtca4u::NDRegisterAccessor(name, unit, description), _instanceType( + instanceType) { } /*********************************************************************************************************************/ template ProcessArray::~ProcessArray() { - // if this is a receiver, shutdown a potentially running readAsync - if(_instanceType == RECEIVER) { - if(this->readAsyncThread.joinable()) { - this->readAsyncThread.interrupt(); - try { - _sharedState->_notificationPromise.set_value(0); - } - catch(boost::promise_already_satisfied) { - // ignore -> already notified - } - this->readAsyncThread.join(); - } - } - this->shutdown(); - } - -/*********************************************************************************************************************/ - - template - void ProcessArray::doReadTransfer() { - - // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call - // TransferFuture::wait(). - readAsync().getBoostFuture().wait(); - boost::this_thread::interruption_point(); - - } - -/*********************************************************************************************************************/ - - template - bool ProcessArray::doReadTransferNonBlocking() { - - // Obtain future and check if a transfer is complete. - auto theFuture = readAsync().getBoostFuture(); - auto status = theFuture.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) return false; - return true; - - } - -/*********************************************************************************************************************/ - - template - bool ProcessArray::doReadTransferLatest() { - // As long as there is more than one valid element on the queue, discard it. - // Due to our implementation there is always one unfulfilled future in the queue, so - // we must pop until there are two elements left in order not to flush out the newest valid value. - auto theFuture = readAsync().getBoostFuture(); - while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout - && _sharedState->_fullBufferQueue.read_available() > 2 ) { - // discard data by moving the buffer to the empty buffer queue - TransferFuture::Data *discardedBuffer = theFuture.get(); - _sharedState->_fullBufferQueue.pop(); - mtca4u::TransferElement::hasActiveFuture = false; - _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue - theFuture = readAsync().getBoostFuture(); - } - // Check whether data is present in the atomic triple buffer. If it is newer, we also need to discard the last - // valid element in the queue. If it is older, we need to discard the data in the triple buffer. - // First update the triple buffer if it is not valid on our side - if(!(_tripleBufferIndex->_isValid)) { - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - } - // if it now contains a valid element, check if it is newer - if(_tripleBufferIndex->_isValid) { - uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; - uint64_t queueVersion = static_cast(theFuture.get())->_internalVersionNumber; - if(tripleBufferVersion > queueVersion) { - TransferFuture::Data *discardedBuffer = theFuture.get(); - _sharedState->_fullBufferQueue.pop(); - _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); - } - else { - _tripleBufferIndex->_isValid = false; - } - } - return this->doReadTransferNonBlocking(); - } - -/*********************************************************************************************************************/ - - template - mtca4u::TransferFuture& ProcessArray::readAsync() { - - // If we already have a still-active future, we can return it directly. It is not possible that this future is - // blocking and there is valid data to be read in the atomic triple buffer. - // This is only a preformance optimisation, we would generate the same future again. - if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; - - if (_instanceType != RECEIVER) { - throw std::logic_error("Receive operation is only allowed for a receiver process variable."); - } - - // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. - // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this - // function even if the future has not yet been used still result in the correct behaviour. - assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic - TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); - - // if the future blocks, check for data in the tripple buffer - auto status = future.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) { - // if local buffer of triple buffer is invalid, swap with shared buffer first - if(!(_tripleBufferIndex->_isValid)) { - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - } - // if now the local buffer contains valid data, use that data first - if(_tripleBufferIndex->_isValid) { - future = boost::make_ready_future(static_cast(_tripleBufferIndex)).share(); - } - } - - // return the future - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; - return mtca4u::TransferElement::activeFuture; - - } - -/*********************************************************************************************************************/ - - template - void ProcessArray::postRead() { - mtca4u::TransferElement::hasActiveFuture = false; - - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the sender might get into - // trouble when it suddenly experiences a vector of the wrong size. - if(_currentIndex->value.size() != _vectorSize) { - throw std::runtime_error("Cannot run read operation because the size of the vector belonging to the current" - " buffer has been modified."); - } - - // If local buffer of triple buffer is invalid, swap with shared buffer first - if(!(_tripleBufferIndex->_isValid)) { - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - } - - // Determine version number of data in the triple buffer - bool tripleBufferHasData = _tripleBufferIndex->_isValid; - - // Determine version number of data in the queue - assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic - TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); - auto status = future.wait_for(boost::chrono::duration(0)); - bool queueHasData = (status != boost::future_status::timeout); - - // If postRead() has been called, there should be new data somewhere - /// @todo FIXME find this problem and make an assert again! - //assert(queueHasData || tripleBufferHasData); - if(!(queueHasData || tripleBufferHasData)) { - std::cout << "ERROR: assert(queueHasData || tripleBufferHasData) failed! Waiting for new data..." << std::endl; - future.wait(); - } - - // Determine whether to use the data from the queue or from the triple buffer. If in both places data is present, - // use the older data (by the version number). - bool useTripleBuffer; - if(queueHasData && !tripleBufferHasData) { - // only data in queue present - useTripleBuffer = false; - } - else if(!queueHasData && tripleBufferHasData) { - // only data in atomic triple buffer present - useTripleBuffer = true; - } - else { // queueHasData && tripleBufferHasData -> see the assert above - uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; - uint64_t queueVersion = static_cast(future.get())->_internalVersionNumber; - if(tripleBufferVersion < queueVersion) { - useTripleBuffer = true; - } - else { - useTripleBuffer = false; - } - } - - // perform the determined action - if(!useTripleBuffer) { - // remove future from queue, since we only obtained it through front() so far - _sharedState->_fullBufferQueue.pop(); - // get buffer from future - TransferFuture::Data *nextBuffer = future.get(); - // put old buffer back on empty buffer queue and update current index - _sharedState->_emptyBufferQueue.push(_currentIndex); - _currentIndex = static_cast(nextBuffer); // static_cast should be ok, since we control the queue - } - else { - Buffer *nextBuffer = _tripleBufferIndex; - // put old buffer back into triple buffer (after invalidating int) and update current index - _currentIndex->_isValid = false; - _tripleBufferIndex = _currentIndex; - _currentIndex = nextBuffer; - } - - // swap data out of the queue buffer - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _currentIndex->value ); - - } - -/*********************************************************************************************************************/ - - template - bool ProcessArray::write(ChimeraTK::VersionNumber versionNumber) { - return writeInternal(versionNumber, true); - } - -/*********************************************************************************************************************/ - - template - bool ProcessArray::writeDestructively(ChimeraTK::VersionNumber versionNumber) { - if (!_maySendDestructively) { - throw std::runtime_error( - "This process variable must not be sent destructively because the corresponding flag has not been set."); - } - return writeInternal(versionNumber, false); - } - -/*********************************************************************************************************************/ - - template - void ProcessArray::setPersistentDataStorage(boost::shared_ptr storage) { - if(!isWriteable()) return; - bool sendInitialValue = false; - if(!_persistentDataStorage) sendInitialValue = true; - _persistentDataStorage = storage; - _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), - mtca4u::NDRegisterAccessor::getNumberOfSamples()); - if(sendInitialValue) { - mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); - write(); - } - } - -/*********************************************************************************************************************/ - - template - bool ProcessArray::writeInternal(VersionNumber newVersionNumber, bool shouldCopy) { - if(_instanceType != SENDER) { - throw std::logic_error( - "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); - } - // We have to check that the vector that we currently own still has the - // right size. Otherwise, the code using the receiver might get into - // trouble when it suddenly experiences a vector of the wrong size. - if(mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { - throw std::runtime_error( - "Cannot run receive operation because the size of the vector belonging to the current buffer has been " - "modified. Variable name: "+this->getName()); - } - - // First update the persistent data storage, if any was associated. This cannot be done after sending, since the - // value might no longer be available within this instance. - if(_persistentDataStorage) { - _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); - } - - // Before sending the value, we have to set the associated time-stamp. - TimeStamp newTimeStamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); - _currentIndex->timeStamp = newTimeStamp; - - // set the version numbers - _currentIndex->_versionNumber = newVersionNumber; - _currentIndex->_internalVersionNumber = _lastInternalVersionNumber; - ++_lastInternalVersionNumber; - - // flag current data as valid - _currentIndex->_isValid = true; - - // set the data by copying or swapping - if(shouldCopy) { - _currentIndex->value = mtca4u::NDRegisterAccessor::buffer_2D[0]; - } - else { - _currentIndex->value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); - } - // send the data to the queue by first obtaining a new empty buffer from the empty buffer queue - Buffer *nextIndex; - bool lostData = false; - if(_sharedState->_emptyBufferQueue.pop(nextIndex)) { - // Create a new promise first and push its future to the full buffer queue before fulfilling the current promise. - // This makes sure that always at least one unfulfilled future is available for the receier to wait on. - TransferFuture::PromiseType nextPromise; - _sharedState->_fullBufferQueue.push(nextPromise.get_future()); - _sharedState->_notificationPromise.set_value(_currentIndex); - _sharedState->_notificationPromise = std::move(nextPromise); - } - else { - // No empty buffer available: write to special atomic triple buffer - nextIndex = _tripleBufferIndex; - _tripleBufferIndex = _currentIndex; - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - // if the buffer swapped out of the shared state contained valid data, we have now lost that data - if(_tripleBufferIndex->_isValid) { - lostData = true; - // mark as invalid so we can always just swap it with the shared state - _tripleBufferIndex->_isValid = false; - } - } - - // change current index to the new empty buffer - if(shouldCopy) { - nextIndex->timeStamp = newTimeStamp; - nextIndex->_versionNumber = newVersionNumber; - } - _currentIndex = nextIndex; - - // notify send notification listener, if present - if(_sendNotificationListener) { - _sendNotificationListener->notify(_receiver); - } - - return lostData; - } - -/*********************************************************************************************************************/ -/*** Implementations of non-member functions below this line *********************************************************/ -/*********************************************************************************************************************/ - - template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, - std::size_t numberOfBuffers, bool maySendDestructively, - TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener) { - typename boost::shared_ptr > receiver = boost::make_shared< - ProcessArray >(ProcessArray::RECEIVER, name, unit, description, - std::vector(size, initialValue), numberOfBuffers); - typename ProcessArray::SharedPtr sender = boost::make_shared< - ProcessArray >(ProcessArray::SENDER, maySendDestructively, - timeStampSource, sendNotificationListener, receiver); - return std::pair::SharedPtr, - typename ProcessArray::SharedPtr>(sender, receiver); - } - -/*********************************************************************************************************************/ - - template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, - std::size_t numberOfBuffers, bool maySendDestructively, - TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener) { - typename boost::shared_ptr > receiver = boost::make_shared< - ProcessArray >(ProcessArray::RECEIVER, name, unit, description, initialValue, - numberOfBuffers); - typename ProcessArray::SharedPtr sender = boost::make_shared< - ProcessArray >(ProcessArray::SENDER, maySendDestructively, - timeStampSource, sendNotificationListener, receiver); - return std::pair::SharedPtr, - typename ProcessArray::SharedPtr>(sender, receiver); } } // namespace ChimeraTK +// We include the UnidirectionalProcessArray.h header so that code using the +// create... functions does not have to do so explicitly. This include must be +// after the declaration of ProcessArray because the code in +// UnidirectionalProcessArray.h depends on it. +#include "UnidirectionalProcessArray.h" + #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_H diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h new file mode 100644 index 0000000..ef59cfc --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -0,0 +1,875 @@ +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_UNIDIRECTIONAL_PROCESS_ARRAY_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_UNIDIRECTIONAL_PROCESS_ARRAY_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "ProcessArray.h" +#include "ProcessVariableListener.h" +#include "TimeStampSource.h" +#include "PersistentDataStorage.h" + +namespace ChimeraTK { + + /** + * Implementation of the process array that transports data in a single + * direction. This implementation is used for both sides (sender and + * receiver). + * + * This class is not thread-safe and should only be used from a single thread. + * + * If no version number source is specified when creating an instance of this + * class, version number checks are disabled. This means that a receive + * operation will always proceed, regardless of the version number of the new + * value. The version number of the process array without a version number + * source will always stay at zero. + */ + template + class UnidirectionalProcessArray : public ProcessArray { + + public: + + /** + * Type alias for a shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + + /** + * Creates a process array that acts as a receiver. A receiver is + * intended to work as a tandem with a sender and receives values that + * have been set to the sender. + * + * This constructor creates the buffers and queues that are needed for the + * send/receive process and are shared with the sender. + */ + UnidirectionalProcessArray( + typename ProcessArray::InstanceType instanceType, + const mtca4u::RegisterPath& name, const std::string &unit, + const std::string &description, const std::vector& initialValue, + std::size_t numberOfBuffers); + + /** + * Creates a process array that acts as a sender. A sender is intended + * intended to work as a tandem with a receiver and send set values to + * it. It uses the buffers and queues that have been created by the + * receiver. + * + * If the maySendDestructively flag is true, the + * {@link ProcessArray::writeDestructively()} method may be used to transfer + * values without copying but losing them on the sender side. + * + * The optional send-notification listener is notified every time the + * sender's {@link ProcessArray::write()} method is called. It can be + * used to queue a request for the receiver's + * readNonBlocking() method to be called. The process + * variable passed to the listener is the receiver and not the sender. + */ + UnidirectionalProcessArray( + typename ProcessArray::InstanceType instanceType, + bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener, + UnidirectionalProcessArray::SharedPtr receiver); + + ~UnidirectionalProcessArray(); + + TimeStamp getTimeStamp() const override { + return _currentIndex->timeStamp; + } + + ChimeraTK::VersionNumber getVersionNumber() const override { + return _currentIndex->_versionNumber; + } + + void doReadTransfer() override; + + bool doReadTransferNonBlocking() override; + + bool doReadTransferLatest() override; + + mtca4u::TransferFuture& readAsync() override; + + void postRead() override; + + bool write(ChimeraTK::VersionNumber versionNumber={}) override; + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) override; /// @todo FIXME this function must be present in TransferElement already! + + void setPersistentDataStorage(boost::shared_ptr storage) override ; + + /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the + * same variable but different for any other process variable within the same process. The unique ID will not be + * persistent accross executions of the process. */ + size_t getUniqueId() const { + return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state + } + + private: + + /** + * Type for the individual buffers. Each buffer stores a vector (wrapped in a Boost scoped pointer) and a time + * stamp. It extends the TransferFuture::Data type (which provides the version number) so it can be transported + * by a TransferFuture. + */ + struct Buffer : public TransferFuture::Data { + + /** + * Default constructor. Has to be defined explicitly because we delete our copy constructor. + */ + Buffer() + : TransferFuture::Data({}) + {} + + /** + * Delete all copy and move constructors and assignment operators. In C++11, elements of a std::vector no longer + * have to be copy-constructable if the size of the vector is fixed and specified in the constructor. + */ + Buffer(const Buffer &other) = delete; + Buffer& operator=(const Buffer &other) = delete; + Buffer(Buffer &&other) = delete; + Buffer& operator=(Buffer &&other) = delete; + + /** The actual data contained in this buffer. */ + std::vector value; + + /** Flag whether this buffer currently contains valid data. This flag is used only for the atomic triple buffer + * and not set for buffers on the queues (since the queue the buffer is in already tells whether it contains + * valid data or not). */ + bool _isValid{false}; + + /** + * Extra version number used to sort the values within the process variable (so this version is not globally + * valid). This number is needed in addition to the globally valid _versionNumber defined in + * TransferFuture::Data, because the globally valid version numebr can be controlled by the application and + * values with an older version number might be written to this process variable later. That later data with + * the older version number shall still be considered as last data e.g. in the context of readLatest(), i.e. + * whatever has been written to the process variable last should be read with readLatest(), even if it has + * the older version number. + */ + uint64_t _internalVersionNumber; + + /** Time stamp associated with this buffer */ + TimeStamp timeStamp; + + }; + + /** + * Number of elements that each vector (and thus this array) has. + */ + std::size_t _vectorSize; + + /** + * Flag indicating whether the {@code sendDestructively} methods may be used + * on this process array. + */ + bool _maySendDestructively; + + /** + * Last internal version number written. See Buffer::_internalVersionNumber for more details. + */ + uint64_t _lastInternalVersionNumber{0}; + + /** + * The state shared between the sender and the receiver + */ + struct SharedState { + + SharedState(size_t numberOfBuffers) + : _buffers(numberOfBuffers + 2 + 3), // two additional buffers owned by sender and receiver, 3 more additional + // buffers for the atomic triple buffer + _tripleBufferIndex(&(_buffers[3])), + _fullBufferQueue(numberOfBuffers+1), + _emptyBufferQueue(numberOfBuffers) + {} + + /** + * Buffers that hold the actual values. + */ + std::vector _buffers; + + /** + * Special atomic triple buffer to be filled when the queue runs over + * (i.e. all normal buffers are full). After construction, buffer 2 is + * owned by the sender, 3 by the shared state and 4 by the receiver. A + * buffer is considered containing valid data if its _isValid flag is + * set. The buffer's default constructor sets _isValid to false. + */ + std::atomic _tripleBufferIndex; + + /** + * Queue holding the indices of the full buffers. Those are the buffers + * that have been sent but not yet received. We can use an spsc_queue for + * this queue because it is only filled by the sending process array and + * only consumed by the receiving process array. In fact, we have to use + * an spsc_queue because the standard boost::lockfree::queue cannot hold + * futures (as they are not trivially assignable / destructable). + */ + boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; + + /** + * Queue holding the empty buffers. Those are the buffers that have been + * returned to the sender by the receiver. + */ + boost::lockfree::spsc_queue _emptyBufferQueue; + + /** + * The promise corresponding to the unfulfilled future in the _fullBufferQueue. + * This promise is basically only used by the sender. Only in the destructor of the receiver it is used to + * shutdown properly, thus it must be in the shared state. + */ + TransferFuture::PromiseType _notificationPromise; + + }; + boost::shared_ptr _sharedState; + + /** + * Index into the _buffers array that is currently owned by this instance + * and used for read and (possibly) write operations. + */ + Buffer *_currentIndex; + + /** + * Index into the _buffers array that is currently owned by this instance. + * The _tripleBufferIndex is different from the _currentIndex because it is + * only used as a fallback when a value cannot be transferred through the + * _fullBufferQueue because the _emptyBufferQueue is empty. + */ + Buffer *_tripleBufferIndex; + + /** + * Pointer to the receiver associated with this sender. This field is only + * used if this process variable represents a sender. + */ + boost::shared_ptr _receiver; + + /** + * Time-stamp source used to update the time-stamp when sending a value. + */ + boost::shared_ptr _timeStampSource; + + /** + * Listener that is notified when the process variable is sent. + */ + boost::shared_ptr _sendNotificationListener; + + /** + * Persistent data storage which needs to be informed when the process variable is sent. + */ + boost::shared_ptr _persistentDataStorage; + + /** + * Variable ID for the persistent data storage + */ + size_t _persistentDataStorageID{0}; + + /** + * Internal implementation of the various {@code send} methods. All these + * methods basically do the same and only differ in whether the data in the + * sent array is copied to the buffer used as the new current buffer and + * which version number is used. + * + * The return value will be true, if older data was overwritten during the send operation, or otherwise false. + */ + bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy); + + }; + +/*********************************************************************************************************************/ +/*** Non-member functions below this line ****************************************************************************/ +/*********************************************************************************************************************/ + + /** + * Creates a synchronized process array. A synchronized process array works + * as a pair of two process arrays, where one process array acts as a sender + * and the other one acts as a receiver. + * + * The sender allows full read-write access. Changes that have been made to + * the sender can be sent to the receiver through the + * {@link ProcessArray::write()} method. The receiver can be updated with these + * changes by calling its readNonBlocking() method. + * + * The synchronization is implemented in a thread-safe manner, so that the + * sender and the receiver can safely be used by different threads without + * a mutex. However, both the sender and receiver each only support a single + * thread. This means that the sender or the receiver have to be protected + * with a mutex if more than one thread wants to access either of them. + * + * The number of buffers specifies how many buffers are allocated for the + * send / receive mechanism. The minimum number (and default) is two. This + * number specifies, how many times {@link ProcessArray::write()} can be called + * in a row without losing data when readNonBlocking() is not + * called in between. + * + * If the maySendDestructively flag is true (it is + * false by default), the {@link ProcessArray::writeDestructively()} method may + * be used to transfer values without copying but losing them on the sender + * side. + * + * The specified time-stamp source is used for determining the current time + * when sending a value. The receiver will be updated with this time stamp + * when receiving the value. If no time-stamp source is specified, the current + * system-time when the value is sent is used. + * + * The optional send-notification listener is notified every time the sender's + * {@link ProcessArray::write()} method is called. It can be used to queue a + * request for the receiver's readNonBlocking() method to be + * called. The process variable passed to the listener is the receiver and + * not the sender. + * + * The specified initial value is used for all the elements of the array. + */ + template + std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( + std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", + const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 2, + bool maySendDestructively = false, + TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = + ProcessVariableListener::SharedPtr()); + + /** + * Creates a synchronized process array. A synchronized process array works + * as a pair of two process arrays, where one process array acts as a sender + * and the other one acts as a receiver. + * + * The sender allows full read-write access. Changes that have been made to + * the sender can be sent to the receiver through the + * {@link ProcessArray::write()} method. The receiver can be updated with these + * changes by calling its readNonBlocking() method. + * + * The synchronization is implemented in a thread-safe manner, so that the + * sender and the receiver can safely be used by different threads without + * a mutex. However, both the sender and receiver each only support a single + * thread. This means that the sender or the receiver have to be protected + * with a mutex if more than one thread wants to access either of them. + * + * The number of buffers specifies how many buffers are allocated for the + * send / receive mechanism. The minimum number (and default) is two. This + * number specifies, how many times {@link ProcessArray::write()} can be called + * in a row without losing data when readNonBlocking() is not + * called in between. + * + * If the maySendDestructively flag is true (it is + * false by default), the {@link ProcessArray::writeDestructively()} method may + * be used to transfer values without copying but losing them on the sender + * side. + * + * The specified time-stamp source is used for determining the current time + * when sending a value. The receiver will be updated with this time stamp + * when receiving the value. If no time-stamp source is specified, the current + * system-time when the value is sent is used. + * + * The optional send-notification listener is notified every time the sender's + * {@link ProcessArray::write()} method is called. It can be used to queue a + * request for the receiver's readNonBlocking() method to be + * called. The process variable passed to the listener is the receiver and + * not the sender. + * + * The array's size is set to the number of elements stored in the vector + * provided for initialization and all elements are initialized with the + * values provided by this vector. + */ + template + std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( + const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", + const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, + TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = + ProcessVariableListener::SharedPtr()); + +/*********************************************************************************************************************/ +/*** Implementations of member functions below this line *************************************************************/ +/*********************************************************************************************************************/ + + template + UnidirectionalProcessArray::UnidirectionalProcessArray( + typename ProcessArray::InstanceType instanceType, + const mtca4u::RegisterPath& name, const std::string &unit, + const std::string &description, const std::vector& initialValue, + std::size_t numberOfBuffers) : + ProcessArray(instanceType, name, unit, description), _vectorSize( + initialValue.size()), _maySendDestructively(false), _sharedState( + boost::make_shared(numberOfBuffers)), _currentIndex( + &(_sharedState->_buffers[0])), _tripleBufferIndex( + &(_sharedState->_buffers[4])) { + try { + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (!this->isReadable()) { + throw std::invalid_argument("This constructor may only be used for a receiver process variable."); + } + // We need at least two buffers for the queue (so four buffers in total) + // in order to guarantee that we never have to block. + if (numberOfBuffers < 2) { + throw std::invalid_argument("The number of buffers must be at least two."); + } + // We have to limit the number of buffers because we cannot allocate + // more buffers than can be represented by size_t and the total number + // of buffers is the specified number plus two. These extra two buffers + // are needed because the sender and the receiver each hold one buffer + // at all times and thus only the rest remains for the queue. We use + // this definition of the number of buffers to keep it consistent with + // what ProcessScalarImpl uses. + if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { + throw std::invalid_argument("The number of buffers is too large."); + } + // We have to initialize the buffers by copying in the initial vectors. + for (auto &i : _sharedState->_buffers) { + i.value = initialValue; + } + // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the + // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of + // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. + for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { + _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); + } + } + catch(...) { + this->shutdown(); + throw; + } + } + +/*********************************************************************************************************************/ + + template + UnidirectionalProcessArray::UnidirectionalProcessArray( + typename ProcessArray::InstanceType instanceType, + bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener, + UnidirectionalProcessArray::SharedPtr receiver) : + ProcessArray(instanceType, receiver->getName(), receiver->getUnit(), + receiver->getDescription()), _vectorSize(receiver->_vectorSize), _maySendDestructively( + maySendDestructively), _sharedState(receiver->_sharedState), _currentIndex( + &(_sharedState->_buffers[1])), _tripleBufferIndex( + &(_sharedState->_buffers[2])), _receiver(receiver), _timeStampSource( + timeStampSource), _sendNotificationListener(sendNotificationListener) { + try { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (!this->isWriteable()) { + throw std::invalid_argument("This constructor may only be used for a sender process variable."); + } + if (!receiver) { + throw std::invalid_argument("The pointer to the receiver must not be null."); + } + if (!receiver->isReadable()) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; + // put future into the notification queue + _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); + } + catch(...) { + this->shutdown(); + throw; + } + } + +/*********************************************************************************************************************/ + + template + UnidirectionalProcessArray::~UnidirectionalProcessArray() { + // if this is a receiver, shutdown a potentially running readAsync + if(this->isReadable()) { + if(this->readAsyncThread.joinable()) { + this->readAsyncThread.interrupt(); + try { + _sharedState->_notificationPromise.set_value(0); + } + catch(boost::promise_already_satisfied) { + // ignore -> already notified + } + this->readAsyncThread.join(); + } + } + this->shutdown(); + } + +/*********************************************************************************************************************/ + + template + void UnidirectionalProcessArray::doReadTransfer() { + + // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call + // TransferFuture::wait(). + readAsync().getBoostFuture().wait(); + boost::this_thread::interruption_point(); + + } + +/*********************************************************************************************************************/ + + template + bool UnidirectionalProcessArray::doReadTransferNonBlocking() { + + // Obtain future and check if a transfer is complete. + auto theFuture = readAsync().getBoostFuture(); + auto status = theFuture.wait_for(boost::chrono::duration(0)); + if(status == boost::future_status::timeout) return false; + return true; + + } + +/*********************************************************************************************************************/ + + template + bool UnidirectionalProcessArray::doReadTransferLatest() { + // As long as there is more than one valid element on the queue, discard it. + // Due to our implementation there is always one unfulfilled future in the queue, so + // we must pop until there are two elements left in order not to flush out the newest valid value. + auto theFuture = readAsync().getBoostFuture(); + while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout + && _sharedState->_fullBufferQueue.read_available() > 2 ) { + // discard data by moving the buffer to the empty buffer queue + TransferFuture::Data *discardedBuffer = theFuture.get(); + _sharedState->_fullBufferQueue.pop(); + mtca4u::TransferElement::hasActiveFuture = false; + _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue + theFuture = readAsync().getBoostFuture(); + } + // Check whether data is present in the atomic triple buffer. If it is newer, we also need to discard the last + // valid element in the queue. If it is older, we need to discard the data in the triple buffer. + // First update the triple buffer if it is not valid on our side + if(!(_tripleBufferIndex->_isValid)) { + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + } + // if it now contains a valid element, check if it is newer + if(_tripleBufferIndex->_isValid) { + uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; + uint64_t queueVersion = static_cast(theFuture.get())->_internalVersionNumber; + if(tripleBufferVersion > queueVersion) { + TransferFuture::Data *discardedBuffer = theFuture.get(); + _sharedState->_fullBufferQueue.pop(); + _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); + } + else { + _tripleBufferIndex->_isValid = false; + } + } + return this->doReadTransferNonBlocking(); + } + +/*********************************************************************************************************************/ + + template + mtca4u::TransferFuture& UnidirectionalProcessArray::readAsync() { + + // If we already have a still-active future, we can return it directly. It is not possible that this future is + // blocking and there is valid data to be read in the atomic triple buffer. + // This is only a preformance optimisation, we would generate the same future again. + if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; + + if (!this->isReadable()) { + throw std::logic_error("Receive operation is only allowed for a receiver process variable."); + } + + // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. + // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this + // function even if the future has not yet been used still result in the correct behaviour. + assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic + TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); + + // if the future blocks, check for data in the tripple buffer + auto status = future.wait_for(boost::chrono::duration(0)); + if(status == boost::future_status::timeout) { + // if local buffer of triple buffer is invalid, swap with shared buffer first + if(!(_tripleBufferIndex->_isValid)) { + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + } + // if now the local buffer contains valid data, use that data first + if(_tripleBufferIndex->_isValid) { + future = boost::make_ready_future(static_cast(_tripleBufferIndex)).share(); + } + } + + // return the future + mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); + mtca4u::TransferElement::hasActiveFuture = true; + return mtca4u::TransferElement::activeFuture; + + } + +/*********************************************************************************************************************/ + + template + void UnidirectionalProcessArray::postRead() { + mtca4u::TransferElement::hasActiveFuture = false; + + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the sender might get into + // trouble when it suddenly experiences a vector of the wrong size. + if(_currentIndex->value.size() != _vectorSize) { + throw std::runtime_error("Cannot run read operation because the size of the vector belonging to the current" + " buffer has been modified."); + } + + // If local buffer of triple buffer is invalid, swap with shared buffer first + if(!(_tripleBufferIndex->_isValid)) { + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + } + + // Determine version number of data in the triple buffer + bool tripleBufferHasData = _tripleBufferIndex->_isValid; + + // Determine version number of data in the queue + assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic + TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); + auto status = future.wait_for(boost::chrono::duration(0)); + bool queueHasData = (status != boost::future_status::timeout); + + // If postRead() has been called, there should be new data somewhere + /// @todo FIXME find this problem and make an assert again! + //assert(queueHasData || tripleBufferHasData); + if(!(queueHasData || tripleBufferHasData)) { + std::cout << "ERROR: assert(queueHasData || tripleBufferHasData) failed! Waiting for new data..." << std::endl; + future.wait(); + } + + // Determine whether to use the data from the queue or from the triple buffer. If in both places data is present, + // use the older data (by the version number). + bool useTripleBuffer; + if(queueHasData && !tripleBufferHasData) { + // only data in queue present + useTripleBuffer = false; + } + else if(!queueHasData && tripleBufferHasData) { + // only data in atomic triple buffer present + useTripleBuffer = true; + } + else { // queueHasData && tripleBufferHasData -> see the assert above + uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; + uint64_t queueVersion = static_cast(future.get())->_internalVersionNumber; + if(tripleBufferVersion < queueVersion) { + useTripleBuffer = true; + } + else { + useTripleBuffer = false; + } + } + + // perform the determined action + if(!useTripleBuffer) { + // remove future from queue, since we only obtained it through front() so far + _sharedState->_fullBufferQueue.pop(); + // get buffer from future + TransferFuture::Data *nextBuffer = future.get(); + // put old buffer back on empty buffer queue and update current index + _sharedState->_emptyBufferQueue.push(_currentIndex); + _currentIndex = static_cast(nextBuffer); // static_cast should be ok, since we control the queue + } + else { + Buffer *nextBuffer = _tripleBufferIndex; + // put old buffer back into triple buffer (after invalidating int) and update current index + _currentIndex->_isValid = false; + _tripleBufferIndex = _currentIndex; + _currentIndex = nextBuffer; + } + + // swap data out of the queue buffer + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _currentIndex->value ); + + } + +/*********************************************************************************************************************/ + + template + bool UnidirectionalProcessArray::write( + ChimeraTK::VersionNumber versionNumber) { + return writeInternal(versionNumber, true); + } + +/*********************************************************************************************************************/ + + template + bool UnidirectionalProcessArray::writeDestructively( + ChimeraTK::VersionNumber versionNumber) { + if (!_maySendDestructively) { + throw std::runtime_error( + "This process variable must not be sent destructively because the corresponding flag has not been set."); + } + return writeInternal(versionNumber, false); + } + +/*********************************************************************************************************************/ + + template + void UnidirectionalProcessArray::setPersistentDataStorage( + boost::shared_ptr storage) { + if(!this->isWriteable()) return; + bool sendInitialValue = false; + if(!_persistentDataStorage) sendInitialValue = true; + _persistentDataStorage = storage; + _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), + mtca4u::NDRegisterAccessor::getNumberOfSamples()); + if(sendInitialValue) { + mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); + write(); + } + } + +/*********************************************************************************************************************/ + + template + bool UnidirectionalProcessArray::writeInternal( + VersionNumber newVersionNumber, bool shouldCopy) { + if(!this->isWriteable()) { + throw std::logic_error( + "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); + } + // We have to check that the vector that we currently own still has the + // right size. Otherwise, the code using the receiver might get into + // trouble when it suddenly experiences a vector of the wrong size. + if(mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { + throw std::runtime_error( + "Cannot run receive operation because the size of the vector belonging to the current buffer has been " + "modified. Variable name: "+this->getName()); + } + + // First update the persistent data storage, if any was associated. This cannot be done after sending, since the + // value might no longer be available within this instance. + if(_persistentDataStorage) { + _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); + } + + // Before sending the value, we have to set the associated time-stamp. + TimeStamp newTimeStamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); + _currentIndex->timeStamp = newTimeStamp; + + // set the version numbers + _currentIndex->_versionNumber = newVersionNumber; + _currentIndex->_internalVersionNumber = _lastInternalVersionNumber; + ++_lastInternalVersionNumber; + + // flag current data as valid + _currentIndex->_isValid = true; + + // set the data by copying or swapping + if(shouldCopy) { + _currentIndex->value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + } + else { + _currentIndex->value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + } + // send the data to the queue by first obtaining a new empty buffer from the empty buffer queue + Buffer *nextIndex; + bool lostData = false; + if(_sharedState->_emptyBufferQueue.pop(nextIndex)) { + // Create a new promise first and push its future to the full buffer queue before fulfilling the current promise. + // This makes sure that always at least one unfulfilled future is available for the receier to wait on. + TransferFuture::PromiseType nextPromise; + _sharedState->_fullBufferQueue.push(nextPromise.get_future()); + _sharedState->_notificationPromise.set_value(_currentIndex); + _sharedState->_notificationPromise = std::move(nextPromise); + } + else { + // No empty buffer available: write to special atomic triple buffer + nextIndex = _tripleBufferIndex; + _tripleBufferIndex = _currentIndex; + _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); + // if the buffer swapped out of the shared state contained valid data, we have now lost that data + if(_tripleBufferIndex->_isValid) { + lostData = true; + // mark as invalid so we can always just swap it with the shared state + _tripleBufferIndex->_isValid = false; + } + } + + // change current index to the new empty buffer + if(shouldCopy) { + nextIndex->timeStamp = newTimeStamp; + nextIndex->_versionNumber = newVersionNumber; + } + _currentIndex = nextIndex; + + // notify send notification listener, if present + if(_sendNotificationListener) { + _sendNotificationListener->notify(_receiver); + } + + return lostData; + } + +/*********************************************************************************************************************/ +/*** Implementations of non-member functions below this line *********************************************************/ +/*********************************************************************************************************************/ + + template + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createSynchronizedProcessArray( + std::size_t size, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, + std::size_t numberOfBuffers, bool maySendDestructively, + TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener) { + typename boost::shared_ptr > receiver = + boost::make_shared >( + ProcessArray::RECEIVER, name, unit, description, + std::vector(size, initialValue), numberOfBuffers); + typename UnidirectionalProcessArray::SharedPtr sender = + boost::make_shared >( + ProcessArray::SENDER, maySendDestructively, timeStampSource, + sendNotificationListener, receiver); + return std::pair::SharedPtr, + typename ProcessArray::SharedPtr>(sender, receiver); + } + +/*********************************************************************************************************************/ + + template + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createSynchronizedProcessArray( + const std::vector& initialValue, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, + std::size_t numberOfBuffers, bool maySendDestructively, + TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener) { + typename boost::shared_ptr > receiver = + boost::make_shared >( + ProcessArray::RECEIVER, name, unit, description, initialValue, + numberOfBuffers); + typename UnidirectionalProcessArray::SharedPtr sender = + boost::make_shared >( + ProcessArray::SENDER, maySendDestructively, timeStampSource, + sendNotificationListener, receiver); + return std::pair::SharedPtr, + typename ProcessArray::SharedPtr>(sender, receiver); + } + +} // namespace ChimeraTK + +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_UNIDIRECTIONAL_PROCESS_ARRAY_H From 8f28b2075aba50a322dda1a3a187bcc723d8911a Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 27 Oct 2017 15:05:24 +0200 Subject: [PATCH 282/786] Implement bidirectional process arrays (issue #2). This commit adds the bidirectional process-array implementation and makes it available through the PVManager and DevicePVManager. It also makes a few minor modifications to the unidirectional process array. In particular, the unidirectional process array now checks that the version number used in a write operation is greater than the last version number used. This should help to detect bugs in device libraries that incorrectly deal with version numbers. --- .../BidirectionalProcessArray.h | 523 ++++++++++++++++++ .../ControlSystemAdapter/DevicePVManager.h | 16 + .../ControlSystemAdapter/PVManager.h | 74 +++ .../ControlSystemAdapter/ProcessArray.h | 15 +- .../SynchronizationDirection.h | 7 +- .../UnidirectionalProcessArray.h | 15 +- 6 files changed, 635 insertions(+), 15 deletions(-) create mode 100644 include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h new file mode 100644 index 0000000..0279989 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -0,0 +1,523 @@ +#ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_BIDIRECTIONAL_PROCESS_ARRAY_H +#define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_BIDIRECTIONAL_PROCESS_ARRAY_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "ProcessArray.h" +#include "ProcessVariableListener.h" +#include "TimeStampSource.h" +#include "PersistentDataStorage.h" + +namespace ChimeraTK { + + /** + * Creates a bidirectional synchronized process array. A bidirectional + * synchronized process array works as a pair of two process arrays, where + * changes written on either side can be read on the respective other side. + * + * The synchronization is implemented in a thread-safe manner, so that the + * two coupled process arrays can safely be used by different threads without + * a mutex. However, both process arrays each only support a single thread. + * This means that access to each process array have to be protected with a + * mutex if more than one thread wants to access it. + * + * The number of buffers specifies how many buffers are allocated for the + * send / receive mechanism. The minimum number (and default) is two. This + * number specifies, how many times {@link ProcessArray::write()} can be + * called in a row without losing data when readNonBlocking() is not called in + * between. The specified number of buffers is used for each direction. + * + * The specified time-stamp sources are used for determining the current time + * when sending a value. The receiver will be updated with this time stamp + * when receiving the value. If no time-stamp source is specified, the current + * system-time when the value is sent is used. The first specified time-stamp + * source is used when sending values from the first returned process array + * and the second specified time-stamp source is used when sending values from + * the second returned process array. + * + * The optional send-notification listeners are notified every time one of the + * process array's {@link ProcessArray::write()} method is called. These + * listeners can be used to queue a request for the receiver's + * readNonBlocking() method to be called. The process variable passed to the + * listener is the receiver and not the sender. The first listener is notified + * when the first of the returned process arrays is sent (and is passed the + * second of the returned process arrays). The second listener is notified + * when the second of the returned process arrays is sent (and is passed the + * first of the returned process arrays). + * + * The specified initial value is used for all the elements of the array. + * + * Of the two returned process arrays, only the first one can take an + * optional persistent data storage. Trying to set a persistent data storage + * on the first one results in an exception. + */ + template + std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + std::size_t size, const mtca4u::RegisterPath & name = "", + const std::string &unit = "", const std::string &description = "", + T initialValue = T(), std::size_t numberOfBuffers = 2, + TimeStampSource::SharedPtr timeStampSource1 = + TimeStampSource::SharedPtr(), + TimeStampSource::SharedPtr timeStampSource2 = + TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener1 = + ProcessVariableListener::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener2 = + ProcessVariableListener::SharedPtr()); + + /** + * Creates a bidirectional synchronized process array. A bidirectional + * synchronized process array works as a pair of two process arrays, where + * changes written on either side can be read on the respective other side. + * + * The synchronization is implemented in a thread-safe manner, so that the + * two coupled process arrays can safely be used by different threads without + * a mutex. However, both process arrays each only support a single thread. + * This means that access to each process array have to be protected with a + * mutex if more than one thread wants to access it. + * + * The number of buffers specifies how many buffers are allocated for the + * send / receive mechanism. The minimum number (and default) is two. This + * number specifies, how many times {@link ProcessArray::write()} can be + * called in a row without losing data when readNonBlocking() is not called in + * between. The specified number of buffers is used for each direction. + * + * The specified time-stamp source is used for determining the current time + * when sending a value. The receiver will be updated with this time stamp + * when receiving the value. If no time-stamp source is specified, the current + * system-time when the value is sent is used. + * + * The optional send-notification listeners are notified every time one of the + * process array's {@link ProcessArray::write()} method is called. These + * listeners can be used to queue a request for the receiver's + * readNonBlocking() method to be called. The process variable passed to the + * listener is the receiver and not the sender. The first listener is notified + * when the first of the returned process arrays is sent (and is passed the + * second of the returned process arrays). The second listener is notified + * when the second of the returned process arrays is sent (and is passed the + * first of the returned process arrays). + * + * The array's size is set to the number of elements stored in the vector + * provided for initialization and all elements are initialized with the + * values provided by this vector. + * + * Of the two returned process arrays, only the first one can take an + * optional persistent data storage. Trying to set a persistent data storage + * on the first one results in an exception. + */ + template + std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + const std::vector& initialValue, + const mtca4u::RegisterPath & name = "", const std::string &unit = "", + const std::string &description = "", std::size_t numberOfBuffers = 2, + TimeStampSource::SharedPtr timeStampSource1 = + TimeStampSource::SharedPtr(), + TimeStampSource::SharedPtr timeStampSource2 = + TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener1 = + ProcessVariableListener::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener2 = + ProcessVariableListener::SharedPtr()); + + /** + * Implementation of the process array that transports data in both + * directions. + * + * This class is not thread-safe and should only be used from a single thread. + */ + template + class BidirectionalProcessArray : public ProcessArray { + + public: + + /** + * Type alias for a shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + + /* The two createBidirectionalSynchronizedProcessArray functions have to be + friends so that they can access the private _partner field. */ + friend std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + const std::vector&, const mtca4u::RegisterPath &, + const std::string &, const std::string &, std::size_t, + TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, + ProcessVariableListener::SharedPtr, ProcessVariableListener::SharedPtr); + friend std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + std::size_t, const mtca4u::RegisterPath &, const std::string &, + const std::string &, T, std::size_t, TimeStampSource::SharedPtr, + TimeStampSource::SharedPtr, ProcessVariableListener::SharedPtr, + ProcessVariableListener::SharedPtr); + + /** + * Creates a bidirectional process array that uses the passed process arrays + * for sending and receiving data. This constructor should not be used + * directly. Instead, one should use one of the + * createBidirectionalSynchronizedProcessArray functions. + */ + BidirectionalProcessArray(std::size_t uniqueId, + const mtca4u::RegisterPath& name, const std::string &unit, + const std::string &description, bool allowPersistentDataStorage, + typename ProcessArray::SharedPtr receiver, + typename ProcessArray::SharedPtr sender, + ProcessVariableListener::SharedPtr sendNotificationListener, + TimeStamp initialTimeStamp, VersionNumber initialVersionNumber); + + ~BidirectionalProcessArray(); + + TimeStamp getTimeStamp() const override { + return _timeStamp; + } + + ChimeraTK::VersionNumber getVersionNumber() const override { + return _versionNumber; + } + + void doReadTransfer() override; + + bool doReadTransferNonBlocking() override; + + bool doReadTransferLatest() override; + + mtca4u::TransferFuture& readAsync() override; + + void postRead() override; + + bool write(ChimeraTK::VersionNumber versionNumber={}) override; + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) override; /// @todo FIXME this function must be present in TransferElement already! + + void setPersistentDataStorage(boost::shared_ptr storage) override ; + + /** + * Returns a unique ID of this process variable, which will be indentical + * for the receiver and sender side of the same variable but different for + * any other process variable within the same process. The unique ID will + * not be persistent across executions of the process. */ + size_t getUniqueId() const override { + return _uniqueId; + } + + private: + + /** + * Flag indicating whether this process array may be associated with a + * persistent data storage. Usually, on the control-system side of a process + * array should have a persistent data storage. This is critical for + * bi-directional process arrays because we cannot have both sides compete + * for updating the persistent data storage. + */ + bool _allowPersistentDataStorage; + + /** + * Other process array that belongs to the pair of process array. Due to the + * obvious circular dependency, this field is not initialized by the + * constructor. Instead, it is set by + * createBidirectionalSynchronizedProcessArray after constructing both + * instances of a pair. For the same reason, we have to use a weak pointer + * instead of a shared pointer. + */ + typename boost::weak_ptr> _partner; + + /** + * Persistent data storage which needs to be informed when the process + * variable is sent. + */ + boost::shared_ptr _persistentDataStorage; + + /** + * Variable ID for the persistent data storage. + */ + size_t _persistentDataStorageID{0}; + + /** + * Process array from which we receive values. When this process array is + * read, we actually read from the receiver. + */ + typename ProcessArray::SharedPtr _receiver; + + /** + * Process array to which send values. When this process array is written, + * we actually write to the sender. + */ + typename ProcessArray::SharedPtr _sender; + + /** + * Listener that is notified when this process array is sent. + */ + ProcessVariableListener::SharedPtr _sendNotificationListener; + + /** + * Current time stamp. Depending on whether the last operation was a read or + * write, the time stamp from the receiver or the sender is supposed to be + * used. This is why we store a separate copy of the time stamp. + */ + mtca4u::TimeStamp _timeStamp; + + /** + * Unique ID identifying the pair to which this process array belongs. + */ + std::size_t _uniqueId; + + /** + * Current version number. Depending on whether the last operation was a + * read or write, the version number from the receiver or sender is supposed + * to be used. This is why we store a separate copy of the version number. + */ + VersionNumber _versionNumber; + + }; + + +/*********************************************************************************************************************/ +/*** Implementations of member functions below this line *************************************************************/ +/*********************************************************************************************************************/ + + template + BidirectionalProcessArray::BidirectionalProcessArray(std::size_t uniqueId, + const mtca4u::RegisterPath &name, const std::string &unit, + const std::string &description, bool allowPersistentDataStorage, + typename ProcessArray::SharedPtr receiver, + typename ProcessArray::SharedPtr sender, + ProcessVariableListener::SharedPtr sendNotificationListener, + TimeStamp initialTimeStamp, VersionNumber initialVersionNumber) : + ProcessArray(ProcessArray::SENDER_RECEIVER, name, unit, + description), _allowPersistentDataStorage(allowPersistentDataStorage), _receiver( + receiver), _sender(sender), _sendNotificationListener( + sendNotificationListener), _timeStamp(initialTimeStamp), _uniqueId( + uniqueId), _versionNumber(initialVersionNumber) { + if (!receiver->isReadable()) { + throw std::runtime_error("The passed receiver must be readable."); + } + if (!sender->isWriteable()) { + throw std::runtime_error("The passed sender must be writable."); + } + } + + template + BidirectionalProcessArray::~BidirectionalProcessArray() { + // The actual destruction logic is in the underlying process variables + // which are destructed automatically when there is no reference any longer. + } + +/*********************************************************************************************************************/ + + template + void BidirectionalProcessArray::doReadTransfer() { + _receiver->doReadTransfer(); + } + +/*********************************************************************************************************************/ + + template + bool BidirectionalProcessArray::doReadTransferNonBlocking() { + return _receiver->doReadTransferNonBlocking(); + } + +/*********************************************************************************************************************/ + + template + bool BidirectionalProcessArray::doReadTransferLatest() { + return _receiver->doReadTransferLatest(); + } + +/*********************************************************************************************************************/ + + template + mtca4u::TransferFuture& BidirectionalProcessArray::readAsync() { + return _receiver->readAsync(); + } + +/*********************************************************************************************************************/ + + template + void BidirectionalProcessArray::postRead() { + _receiver->postRead(); + // We only update the current value (stored in the sender) when the version + // number of the data that we received is greater than the current version + // number. This ensures that old updates (that might arrive late due to the + // asynchronous nature of the transfer logic) do not overwrite newer values + // and also helps to ensure that we do not get a feedback loop where two (or + // more) bidirectional process variables "play ping-pong" (see issue #2 for + // the full discussion). + if (_receiver->getVersionNumber() > _versionNumber) { + _sender->accessChannel(0).swap(_receiver->accessChannel(0)); + // After receiving, our new time stamp and version number are the ones + // that we got from the receiver. + _timeStamp = _receiver->getTimeStamp(); + _versionNumber = _receiver->getVersionNumber(); + // If we have a persistent data-storage, we have to update it. We have to + // do this because a (new) value received from the other side should be + // treated like a value sent by this side. + if (_persistentDataStorage) { + _persistentDataStorage->updateValue(_persistentDataStorageID, + _sender->accessChannel(0)); + } + } + } + +/*********************************************************************************************************************/ + + template + bool BidirectionalProcessArray::write( + ChimeraTK::VersionNumber versionNumber) { + bool lostData = _sender->write(versionNumber); + // After sending the new value, our current time stamp and version number + // are the one from the sender. + _timeStamp = _sender->getTimeStamp(); + _versionNumber = _sender->getVersionNumber(); + // If we have a persistent data-storage, we have to update it. + if (_persistentDataStorage) { + _persistentDataStorage->updateValue(_persistentDataStorageID, + _sender->accessChannel(0)); + } + if (_sendNotificationListener) { + typename ProcessArray::SharedPtr partner = _partner.lock(); + if (partner) { + _sendNotificationListener->notify(partner); + } + } + return lostData; + } + +/*********************************************************************************************************************/ + + template + bool BidirectionalProcessArray::writeDestructively( + ChimeraTK::VersionNumber) { + throw std::runtime_error( + "This process variable must not be sent destructively because it is a bidirectional process variable."); + } + +/*********************************************************************************************************************/ + + template + void BidirectionalProcessArray::setPersistentDataStorage( + boost::shared_ptr storage) { + if (_allowPersistentDataStorage) { + throw std::logic_error( + "This device side of a process array must not be associated with a persistent data storage."); + } + bool sendInitialValue = false; + if (!_persistentDataStorage) { + sendInitialValue = true; + } + _persistentDataStorage = storage; + _persistentDataStorageID = _persistentDataStorage->registerVariable( + mtca4u::TransferElement::getName(), + mtca4u::NDRegisterAccessor::getNumberOfSamples()); + if (sendInitialValue) { + mtca4u::NDRegisterAccessor::buffer_2D[0] = + _persistentDataStorage->retrieveValue(_persistentDataStorageID); + write(); + } + } + +/*********************************************************************************************************************/ +/*** Implementations of non-member functions below this line *********************************************************/ +/*********************************************************************************************************************/ + + template + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + std::size_t size, const mtca4u::RegisterPath & name, + const std::string &unit, const std::string &description, T initialValue, + std::size_t numberOfBuffers, TimeStampSource::SharedPtr timeStampSource1, + TimeStampSource::SharedPtr timeStampSource2, + ProcessVariableListener::SharedPtr sendNotificationListener1, + ProcessVariableListener::SharedPtr sendNotificationListener2) { + auto senderReceiver1 = createSynchronizedProcessArray(size, name, unit, + description, initialValue, numberOfBuffers, false, timeStampSource1); + auto senderReceiver2 = createSynchronizedProcessArray(size, name, unit, + description, initialValue, numberOfBuffers, false, timeStampSource2); + mtca4u::TimeStamp timeStamp = senderReceiver2.first->getTimeStamp(); + VersionNumber versionNumber = senderReceiver2.first->getVersionNumber(); + // The unique ID has to be the same for both process arrays that belong to + // the pair, but it has to be different from the one used by all other + // process arrays. We simply use the unique ID used by the second + // sender / receiver pair. + std::size_t uniqueId = senderReceiver2.first->getUniqueId(); + typename boost::shared_ptr > pv1 = + boost::make_shared >(uniqueId, name, unit, + description, true, senderReceiver2.second, senderReceiver1.first, + sendNotificationListener1, timeStamp, versionNumber); + typename boost::shared_ptr > pv2 = + boost::make_shared >(uniqueId, name, unit, + description, false, senderReceiver1.second, senderReceiver2.first, + sendNotificationListener2, timeStamp, versionNumber); + return std::pair::SharedPtr, + typename ProcessArray::SharedPtr>(pv1, pv2); + } + +/*********************************************************************************************************************/ + + template + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + const std::vector& initialValue, const mtca4u::RegisterPath & name, + const std::string &unit, const std::string &description, + std::size_t numberOfBuffers, TimeStampSource::SharedPtr timeStampSource1, + TimeStampSource::SharedPtr timeStampSource2, + ProcessVariableListener::SharedPtr sendNotificationListener1, + ProcessVariableListener::SharedPtr sendNotificationListener2) { + auto senderReceiver1 = createSynchronizedProcessArray(initialValue, name, + unit, description, numberOfBuffers, false, timeStampSource1); + auto senderReceiver2 = createSynchronizedProcessArray(initialValue, name, + unit, description, numberOfBuffers, false, timeStampSource2); + mtca4u::TimeStamp timeStamp = senderReceiver2.first->getTimeStamp(); + VersionNumber versionNumber = senderReceiver2.first->getVersionNumber(); + // The unique ID has to be the same for both process arrays that belong to + // the pair, but it has to be different from the one used by all other + // process arrays. We simply use the unique ID used by the second + // sender / receiver pair. + std::size_t uniqueId = senderReceiver2.first->getUniqueId(); + typename boost::shared_ptr > pv1 = + boost::make_shared >(uniqueId, name, unit, + description, true, senderReceiver2.second, senderReceiver1.first, + sendNotificationListener1, timeStamp, versionNumber); + typename boost::shared_ptr > pv2 = + boost::make_shared >(uniqueId, name, unit, + description, false, senderReceiver1.second, senderReceiver2.first, + sendNotificationListener2, timeStamp, versionNumber); + return std::pair::SharedPtr, + typename ProcessArray::SharedPtr>(pv1, pv2); + } + +} // namespace ChimeraTK + +#endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_BIDIRECTIONAL_PROCESS_ARRAY_H diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 67f23b5..426a431 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -236,6 +236,14 @@ namespace ChimeraTK { return _pvManager->createProcessArrayDeviceToControlSystem( processVariableName, std::vector(size, initialValue), unit, description, maySendDestructively, numberOfBuffers).second; + case bidirectional: + if (maySendDestructively) { + throw std::logic_error( + "A bidirectional process variable cannot be sent destructively."); + } + return _pvManager->createBidirectionalProcessArray( + processVariableName, std::vector(size, initialValue), unit, + description, numberOfBuffers).second; default: throw std::invalid_argument("invalid SynchronizationDirection"); } @@ -256,6 +264,14 @@ namespace ChimeraTK { return _pvManager->createProcessArrayDeviceToControlSystem( processVariableName, initialValue, unit, description, maySendDestructively, numberOfBuffers).second; + case bidirectional: + if (maySendDestructively) { + throw std::logic_error( + "A bidirectional process variable cannot be sent destructively."); + } + return _pvManager->createBidirectionalProcessArray( + processVariableName, initialValue, unit, description, + numberOfBuffers).second; default: throw std::invalid_argument("invalid SynchronizationDirection"); } diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index cea215a..0c8b87a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -61,6 +61,35 @@ namespace ChimeraTK { */ typedef std::map ProcessVariableMap; + /** + * Creates a new process array for transferring data between the device + * library and the control system in both directions and registers it with + * the PV manager. Creating a process array with a name that is already used + * for a different process array is an error and causes an + * \c std::invalid_argument exception to be thrown. + * + * The array's size is set to the number of elements stored in the vector + * provided for initialization and all elements are initialized with the + * values provided by this vector. + * + * The number of buffers (the minimum and default value is two) is the max. + * number of values that can be queued in the transfer queue. Specifying a + * larger number make loss of data less likely but increases the memory + * footprint. + * + * Two process arrays are created: one for the control system and one for + * the device library. The pair that is returned has a reference to the + * instance intended for the control system as its first and a reference to + * the instance intended for the device library as its second member. + */ + template + std::pair::SharedPtr, + typename ProcessArray::SharedPtr> createBidirectionalProcessArray( + mtca4u::RegisterPath const & processVariableName, + const std::vector& initialValue, const std::string& unit = + mtca4u::TransferElement::unitNotSet, + const std::string& description = "", std::size_t numberOfBuffers = 2); + /** * Creates a new process array for transferring data from the device library * to the control system and registers it with the PV manager. @@ -423,6 +452,51 @@ namespace ChimeraTK { std::pair, boost::shared_ptr > createPVManager(); + template + std::pair::SharedPtr, + typename ProcessArray::SharedPtr> PVManager::createBidirectionalProcessArray( + mtca4u::RegisterPath const & processVariableName, + const std::vector& initialValue, const std::string& unit, + const std::string& description, std::size_t numberOfBuffers) { + if (_processVariables.find(processVariableName) + != _processVariables.end()) { + throw std::invalid_argument( + "Process variable with name " + processVariableName + + " already exists."); + } + + // We do not use a time-stamp source for process variables that are + // synchronized from the control system to the device. + boost::shared_ptr timeStampSource1; + boost::shared_ptr timeStampSource2 = boost::make_shared< + TimeStampSourceImpl>(shared_from_this()); + boost::shared_ptr sendNotificationListener1 = + boost::make_shared( + shared_from_this()); + boost::shared_ptr sendNotificationListener2 = + boost::make_shared( + shared_from_this()); + + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> processVariables = + createBidirectionalSynchronizedProcessArray(initialValue, + processVariableName, unit, description, numberOfBuffers, + timeStampSource1, timeStampSource2, sendNotificationListener1, + sendNotificationListener2); + + _processVariables.insert( + std::make_pair(processVariableName, + std::make_pair(processVariables.first, processVariables.second))); + + // Increase notification queue size by one to make space for this process + // variable. We can use the unsafe variant because calls to this method have + // to be synchronized anyway. + _controlSystemNotificationQueue.reserve_unsafe(1); + _deviceNotificationQueue.reserve_unsafe(1); + + return std::make_pair(processVariables.first, processVariables.second); + } + template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index 4666d33..d37a6f7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -30,13 +30,6 @@ namespace ChimeraTK { * ProcessArrays. * * This class is not thread-safe and should only be used from a single thread. - * - * TODO This comment might not belong here: - * If no version number source is specified when creating an instance of this - * class, version number checks are disabled. This means that a receive - * operation will always proceed, regardless of the version number of the new - * value. The version number of the process array without a version number - * source will always stay at zero. */ template class ProcessArray : public mtca4u::NDRegisterAccessor { @@ -116,6 +109,11 @@ namespace ChimeraTK { */ virtual bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) = 0; /// @todo FIXME this function must be present in TransferElement already! + /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the + * same variable but different for any other process variable within the same process. The unique ID will not be + * persistent accross executions of the process. */ + virtual size_t getUniqueId() const = 0; + const std::type_info& getValueType() const override { return typeid(T); } @@ -171,5 +169,8 @@ namespace ChimeraTK { // after the declaration of ProcessArray because the code in // UnidirectionalProcessArray.h depends on it. #include "UnidirectionalProcessArray.h" +// The BidirectionalProcessArray depends on the UnidirectionalProcessArray, so +// its header has to be included second. +#include "BidirectionalProcessArray.h" #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_ARRAY_H diff --git a/include/ChimeraTK/ControlSystemAdapter/SynchronizationDirection.h b/include/ChimeraTK/ControlSystemAdapter/SynchronizationDirection.h index 0cbc24f..4abf3c0 100644 --- a/include/ChimeraTK/ControlSystemAdapter/SynchronizationDirection.h +++ b/include/ChimeraTK/ControlSystemAdapter/SynchronizationDirection.h @@ -18,7 +18,12 @@ namespace ChimeraTK { /** * Synchronize from the device library to the control system. */ - deviceToControlSystem + deviceToControlSystem, + + /** + * Synchronize in both directions. + */ + bidirectional }; diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index ef59cfc..039a510 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -29,12 +29,6 @@ namespace ChimeraTK { * receiver). * * This class is not thread-safe and should only be used from a single thread. - * - * If no version number source is specified when creating an instance of this - * class, version number checks are disabled. This means that a receive - * operation will always proceed, regardless of the version number of the new - * value. The version number of the process array without a version number - * source will always stay at zero. */ template class UnidirectionalProcessArray : public ProcessArray { @@ -130,7 +124,7 @@ namespace ChimeraTK { /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the * same variable but different for any other process variable within the same process. The unique ID will not be * persistent accross executions of the process. */ - size_t getUniqueId() const { + size_t getUniqueId() const override { return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state } @@ -761,6 +755,13 @@ namespace ChimeraTK { "Cannot run receive operation because the size of the vector belonging to the current buffer has been " "modified. Variable name: "+this->getName()); } + // A version should never be send with a version number that is equal to or + // even less than the last version number used. Such an attempt indicates + // that there is a problem in the logic attempting the write operation. + if (newVersionNumber <= getVersionNumber()) { + throw std::runtime_error( + "The version number passed to write is less than or equal to the last version number used."); + } // First update the persistent data storage, if any was associated. This cannot be done after sending, since the // value might no longer be available within this instance. From 48f39e73791d61a277c9980a5e8ebfecba5bbcf0 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 27 Oct 2017 16:19:19 +0200 Subject: [PATCH 283/786] Fix friend declaration to avoid compiler warning. --- .../ControlSystemAdapter/BidirectionalProcessArray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 0279989..b52ffbd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -153,13 +153,13 @@ namespace ChimeraTK { /* The two createBidirectionalSynchronizedProcessArray functions have to be friends so that they can access the private _partner field. */ friend std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray<>( const std::vector&, const mtca4u::RegisterPath &, const std::string &, const std::string &, std::size_t, TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, ProcessVariableListener::SharedPtr, ProcessVariableListener::SharedPtr); friend std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( + typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray<>( std::size_t, const mtca4u::RegisterPath &, const std::string &, const std::string &, T, std::size_t, TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, ProcessVariableListener::SharedPtr, From 46f2b94b3e079cc343959411c5eb99ea45f17232 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 27 Oct 2017 16:25:11 +0200 Subject: [PATCH 284/786] Add override classifier to avoid compiler warning. --- include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 039e326..05abf22 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -113,7 +113,7 @@ namespace ChimeraTK { _impl->postWrite(); } - virtual bool write(ChimeraTK::VersionNumber versionNumber={}){ + virtual bool write(ChimeraTK::VersionNumber versionNumber={}) override { // don't call preWrite() here. It would trigger the preWrite on the impl, which is also // happening when _impl()->write() is called. convertAndCopyToImpl(); From e53968a77636628c5add30470022014cb2576bc1 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 27 Oct 2017 20:29:43 +0200 Subject: [PATCH 285/786] Fix read/write logic of BidirectionalProcessArray. In order to correctly implement the write logic of the BidirectionalProcessArray without having to copy twice, another version of the writeDestructively method has been added to UnidirectionalProcessArray. This version allows for explicitly passing a time stamp instead of using the time-stamp source of the process array. --- .../BidirectionalProcessArray.h | 84 +++++++++++++------ .../UnidirectionalProcessArray.h | 56 +++++++++++-- 2 files changed, 111 insertions(+), 29 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index b52ffbd..67b1987 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -14,8 +14,6 @@ #include #include -#include - #include "ProcessArray.h" #include "ProcessVariableListener.h" #include "TimeStampSource.h" @@ -176,6 +174,7 @@ namespace ChimeraTK { const std::string &description, bool allowPersistentDataStorage, typename ProcessArray::SharedPtr receiver, typename ProcessArray::SharedPtr sender, + TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, TimeStamp initialTimeStamp, VersionNumber initialVersionNumber); @@ -185,7 +184,7 @@ namespace ChimeraTK { return _timeStamp; } - ChimeraTK::VersionNumber getVersionNumber() const override { + VersionNumber getVersionNumber() const override { return _versionNumber; } @@ -275,7 +274,7 @@ namespace ChimeraTK { * Process array to which send values. When this process array is written, * we actually write to the sender. */ - typename ProcessArray::SharedPtr _sender; + typename UnidirectionalProcessArray::SharedPtr _sender; /** * Listener that is notified when this process array is sent. @@ -287,7 +286,12 @@ namespace ChimeraTK { * write, the time stamp from the receiver or the sender is supposed to be * used. This is why we store a separate copy of the time stamp. */ - mtca4u::TimeStamp _timeStamp; + TimeStamp _timeStamp; + + /** + * Time stamp source used when this process array is written. + */ + TimeStampSource::SharedPtr _timeStampSource; /** * Unique ID identifying the pair to which this process array belongs. @@ -314,25 +318,38 @@ namespace ChimeraTK { const std::string &description, bool allowPersistentDataStorage, typename ProcessArray::SharedPtr receiver, typename ProcessArray::SharedPtr sender, + TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, TimeStamp initialTimeStamp, VersionNumber initialVersionNumber) : ProcessArray(ProcessArray::SENDER_RECEIVER, name, unit, description), _allowPersistentDataStorage(allowPersistentDataStorage), _receiver( - receiver), _sender(sender), _sendNotificationListener( - sendNotificationListener), _timeStamp(initialTimeStamp), _uniqueId( - uniqueId), _versionNumber(initialVersionNumber) { + receiver), _sender( + boost::dynamic_pointer_cast>(sender)), _sendNotificationListener( + sendNotificationListener), _timeStamp(initialTimeStamp), _timeStampSource( + timeStampSource), _uniqueId(uniqueId), _versionNumber( + initialVersionNumber) { + // If the passed sender was not null but the class variable is, the dynamic + // cast failed. + if (sender && !_sender) { + throw std::runtime_error( + "The passed sender must be an instance of UnidirectionalProcessArray."); + } if (!receiver->isReadable()) { throw std::runtime_error("The passed receiver must be readable."); } if (!sender->isWriteable()) { throw std::runtime_error("The passed sender must be writable."); } + // Allocate and initialize the buffer of the base class we copy the value + // from the receiver because the calling code should already have take care + // of initializing that value. + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->accessChannel(0); } template BidirectionalProcessArray::~BidirectionalProcessArray() { - // The actual destruction logic is in the underlying process variables - // which are destructed automatically when there is no reference any longer. + this->shutdown(); } /*********************************************************************************************************************/ @@ -376,7 +393,7 @@ namespace ChimeraTK { // more) bidirectional process variables "play ping-pong" (see issue #2 for // the full discussion). if (_receiver->getVersionNumber() > _versionNumber) { - _sender->accessChannel(0).swap(_receiver->accessChannel(0)); + this->accessChannel(0).swap(_receiver->accessChannel(0)); // After receiving, our new time stamp and version number are the ones // that we got from the receiver. _timeStamp = _receiver->getTimeStamp(); @@ -396,11 +413,20 @@ namespace ChimeraTK { template bool BidirectionalProcessArray::write( ChimeraTK::VersionNumber versionNumber) { - bool lostData = _sender->write(versionNumber); + // We have to copy our current value to the sender. We cannot swap it + // because this would mean that we would lose the current value. + _sender->accessChannel(0) = this->accessChannel(0); + // We need a new time stamp. + TimeStamp newTimeStamp = + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); + // We already copied the value, so the sender does not have to copy the + // value again. + bool lostData = _sender->writeDestructively(newTimeStamp, versionNumber); // After sending the new value, our current time stamp and version number // are the one from the sender. - _timeStamp = _sender->getTimeStamp(); - _versionNumber = _sender->getVersionNumber(); + _timeStamp = newTimeStamp; + _versionNumber = versionNumber; // If we have a persistent data-storage, we have to update it. if (_persistentDataStorage) { _persistentDataStorage->updateValue(_persistentDataStorageID, @@ -462,11 +488,14 @@ namespace ChimeraTK { ProcessVariableListener::SharedPtr sendNotificationListener1, ProcessVariableListener::SharedPtr sendNotificationListener2) { auto senderReceiver1 = createSynchronizedProcessArray(size, name, unit, - description, initialValue, numberOfBuffers, false, timeStampSource1); + description, initialValue, numberOfBuffers, true); auto senderReceiver2 = createSynchronizedProcessArray(size, name, unit, - description, initialValue, numberOfBuffers, false, timeStampSource2); - mtca4u::TimeStamp timeStamp = senderReceiver2.first->getTimeStamp(); - VersionNumber versionNumber = senderReceiver2.first->getVersionNumber(); + description, initialValue, numberOfBuffers, true); + // We create a default-constructed time-stamp and version number because we + // want to be sure that initially both sides have the same time stamp and + // version number. + TimeStamp timeStamp; + VersionNumber versionNumber; // The unique ID has to be the same for both process arrays that belong to // the pair, but it has to be different from the one used by all other // process arrays. We simply use the unique ID used by the second @@ -475,11 +504,13 @@ namespace ChimeraTK { typename boost::shared_ptr > pv1 = boost::make_shared >(uniqueId, name, unit, description, true, senderReceiver2.second, senderReceiver1.first, - sendNotificationListener1, timeStamp, versionNumber); + timeStampSource1, sendNotificationListener1, timeStamp, + versionNumber); typename boost::shared_ptr > pv2 = boost::make_shared >(uniqueId, name, unit, description, false, senderReceiver1.second, senderReceiver2.first, - sendNotificationListener2, timeStamp, versionNumber); + timeStampSource2, sendNotificationListener2, timeStamp, + versionNumber); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(pv1, pv2); } @@ -499,8 +530,11 @@ namespace ChimeraTK { unit, description, numberOfBuffers, false, timeStampSource1); auto senderReceiver2 = createSynchronizedProcessArray(initialValue, name, unit, description, numberOfBuffers, false, timeStampSource2); - mtca4u::TimeStamp timeStamp = senderReceiver2.first->getTimeStamp(); - VersionNumber versionNumber = senderReceiver2.first->getVersionNumber(); + // We create a default-constructed time-stamp and version number because we + // want to be sure that initially both sides have the same time stamp and + // version number. + TimeStamp timeStamp; + VersionNumber versionNumber; // The unique ID has to be the same for both process arrays that belong to // the pair, but it has to be different from the one used by all other // process arrays. We simply use the unique ID used by the second @@ -509,11 +543,13 @@ namespace ChimeraTK { typename boost::shared_ptr > pv1 = boost::make_shared >(uniqueId, name, unit, description, true, senderReceiver2.second, senderReceiver1.first, - sendNotificationListener1, timeStamp, versionNumber); + timeStampSource1, sendNotificationListener1, timeStamp, + versionNumber); typename boost::shared_ptr > pv2 = boost::make_shared >(uniqueId, name, unit, description, false, senderReceiver1.second, senderReceiver2.first, - sendNotificationListener2, timeStamp, versionNumber); + timeStampSource2, sendNotificationListener2, timeStamp, + versionNumber); return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(pv1, pv2); } diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 039a510..850e8c4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -114,11 +114,37 @@ namespace ChimeraTK { * undefined. Therefore, this method must only be used if this process * variable is not read (on the sender side) after sending it. * + * The time-stamp of the sent value is retrieved from the time-stamp source. + * * Throws an exception if this process variable is not a sender or if this * process variable does not allow destructive sending. */ bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) override; /// @todo FIXME this function must be present in TransferElement already! + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * The time-stamp source of this process array is ignored and the passed + * time stamp is used instead. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool writeDestructively(ChimeraTK::TimeStamp timeStamp, ChimeraTK::VersionNumber versionNumber); + void setPersistentDataStorage(boost::shared_ptr storage) override ; /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the @@ -294,7 +320,8 @@ namespace ChimeraTK { * * The return value will be true, if older data was overwritten during the send operation, or otherwise false. */ - bool writeInternal(VersionNumber newVersionNumber, bool shouldCopy); + bool writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, + bool shouldCopy); }; @@ -706,7 +733,10 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::write( ChimeraTK::VersionNumber versionNumber) { - return writeInternal(versionNumber, true); + return writeInternal( + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(), + versionNumber, true); } /*********************************************************************************************************************/ @@ -718,9 +748,26 @@ namespace ChimeraTK { throw std::runtime_error( "This process variable must not be sent destructively because the corresponding flag has not been set."); } - return writeInternal(versionNumber, false); + return writeInternal( + _timeStampSource ? + _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(), + versionNumber, false); } + /*********************************************************************************************************************/ + + template + bool UnidirectionalProcessArray::writeDestructively( + ChimeraTK::TimeStamp timeStamp, + ChimeraTK::VersionNumber versionNumber) { + if (!_maySendDestructively) { + throw std::runtime_error( + "This process variable must not be sent destructively because the corresponding flag has not been set."); + } + return writeInternal(timeStamp, versionNumber, + false); + } + /*********************************************************************************************************************/ template @@ -742,7 +789,7 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::writeInternal( - VersionNumber newVersionNumber, bool shouldCopy) { + TimeStamp newTimeStamp, VersionNumber newVersionNumber, bool shouldCopy) { if(!this->isWriteable()) { throw std::logic_error( "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); @@ -770,7 +817,6 @@ namespace ChimeraTK { } // Before sending the value, we have to set the associated time-stamp. - TimeStamp newTimeStamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); _currentIndex->timeStamp = newTimeStamp; // set the version numbers From 6139d7fd9f7b3f7f8adbb30cc19a112c4127f5b2 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Sat, 28 Oct 2017 14:58:22 +0200 Subject: [PATCH 286/786] Set _partner field so that listener is notified. The createBidirectionalSynchronizedProcessArray functions were missing the code for initializing the _partner field of the BidirectionalProcessArray. This had the consequence that the send notification listeners were never notified. This commit fixes this bug by correctly initializing the _partner field. --- .../ControlSystemAdapter/BidirectionalProcessArray.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 67b1987..0915be5 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -511,6 +511,8 @@ namespace ChimeraTK { description, false, senderReceiver1.second, senderReceiver2.first, timeStampSource2, sendNotificationListener2, timeStamp, versionNumber); + pv1->_partner = pv2; + pv2->_partner = pv1; return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(pv1, pv2); } @@ -550,6 +552,8 @@ namespace ChimeraTK { description, false, senderReceiver1.second, senderReceiver2.first, timeStampSource2, sendNotificationListener2, timeStamp, versionNumber); + pv1->_partner = pv2; + pv2->_partner = pv1; return std::pair::SharedPtr, typename ProcessArray::SharedPtr>(pv1, pv2); } From b0abfdd8bf14e7de389d498fe5b9daddadb4b883 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Sat, 28 Oct 2017 15:20:44 +0200 Subject: [PATCH 287/786] Add unit tests for BidirectionalProcessArray. In addition to adding the new testBidirectionalProcessArray test, testProcessArray has been renamed to testUnidirectionalProcessArray. --- tests/src/testBidirectionalProcessArray.cpp | 218 ++++++++++++++++++ ...cpp => testUnidirectionalProcessArray.cpp} | 66 +++--- 2 files changed, 251 insertions(+), 33 deletions(-) create mode 100644 tests/src/testBidirectionalProcessArray.cpp rename tests/src/{testProcessArray.cpp => testUnidirectionalProcessArray.cpp} (90%) diff --git a/tests/src/testBidirectionalProcessArray.cpp b/tests/src/testBidirectionalProcessArray.cpp new file mode 100644 index 0000000..4d756dc --- /dev/null +++ b/tests/src/testBidirectionalProcessArray.cpp @@ -0,0 +1,218 @@ +// Define a name for the test module. +#define BOOST_TEST_MODULE BidirectionalProcessArrayTest +// Only after defining the name include the unit test header. +#include + +#include + +#include "CountingProcessVariableListener.h" +#include "CountingTimeStampSource.h" +#include "ProcessArray.h" + +using namespace boost::unit_test_framework; +using namespace ChimeraTK; + +using boost::make_shared; +using std::pair; +using std::tie; + +// We only test an array of doubles because the logic is actually the same for +// all data types and the low-level stuff is already tested for all data types +// when testing the UnidirectionalProcessArray. +using DoubleArray = ProcessArray; + +// Create a test suite which holds all your tests. +BOOST_AUTO_TEST_SUITE( BidirectionalProcessArrayTestSuite ) + + // Test that a delayed incoming update with an older version does not + // overwrite a newer version. + BOOST_AUTO_TEST_CASE( testConflictingUpdates ) { + DoubleArray::SharedPtr pv1, pv2; + double initialValue = 3.5; + tie(pv1, pv2) = createBidirectionalSynchronizedProcessArray(1, "", "", "", + initialValue); + // Initially, both sides should have the initial value. + BOOST_CHECK(pv1->accessData(0) == initialValue); + BOOST_CHECK(pv2->accessData(0) == initialValue); + // Now we write to pv1. After that, pv1 should have been updated, but pv2 + // should still have its old value. + double newValue1 = -2.1; + pv1->accessData(0) = newValue1; + pv1->write(); + BOOST_CHECK(pv1->accessData(0) == newValue1); + BOOST_CHECK(pv2->accessData(0) == initialValue); + // Now we write to pv2. We do this before reading pv2. As a result, both + // pv1 and pv2 should have their respective new values. + double newValue2 = 1.8; + pv2->accessData(0) = newValue2; + pv2->write(); + BOOST_CHECK(pv1->accessData(0) == newValue1); + BOOST_CHECK(pv2->accessData(0) == newValue2); + // Now we read pv2. As the incoming update is older than the current value, + // it should be discarded. + pv2->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == newValue1); + BOOST_CHECK(pv2->accessData(0) == newValue2); + // Now we read pv1. The incoming update should overwrite the current value. + pv1->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == newValue2); + BOOST_CHECK(pv2->accessData(0) == newValue2); + // Now we write another value to pv2, but before reading it on pv1, we write + // a new value there. + double newValue3 = 25.0; + pv2->accessData(0) = newValue3; + pv2->write(); + BOOST_CHECK(pv1->accessData(0) == newValue2); + BOOST_CHECK(pv2->accessData(0) == newValue3); + double newValue4 = 100.0; + pv1->accessData(0) = newValue4; + pv1->write(); + BOOST_CHECK(pv1->accessData(0) == newValue4); + BOOST_CHECK(pv2->accessData(0) == newValue3); + // Now we read pv1. The incoming update should be discarded. + pv1->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == newValue4); + BOOST_CHECK(pv2->accessData(0) == newValue3); + // Now we read pv2. The incoming update should succeed. + pv2->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == newValue4); + BOOST_CHECK(pv2->accessData(0) == newValue4); + } + + // Test that send notification listeners are called correctly. + BOOST_AUTO_TEST_CASE( testListeners ) { + DoubleArray::SharedPtr pv1, pv2; + TimeStampSource::SharedPtr nullTimeStampSource; + auto listener1 = make_shared(); + auto listener2 = make_shared(); + tie(pv1, pv2) = createBidirectionalSynchronizedProcessArray(1, "", "", "", + 0.0, 2, nullTimeStampSource, nullTimeStampSource, listener1, listener2); + // Initially, the two listeners should not have received any notifications. + BOOST_CHECK(listener1->count == 0); + BOOST_CHECK(!listener1->lastProcessVariable); + BOOST_CHECK(listener2->count == 0); + BOOST_CHECK(!listener2->lastProcessVariable); + // Now we write pv1. This should lead to listener1 being notified that pv2 + // is now ready to be read. + pv1->write(); + BOOST_CHECK(listener1->count == 1); + BOOST_CHECK(listener1->lastProcessVariable == pv2); + BOOST_CHECK(listener2->count == 0); + BOOST_CHECK(!listener2->lastProcessVariable); + // Now we write pv2. This should lead to listener2 being notified that pv1 + // is now ready to be read. + pv2->readNonBlocking(); + pv2->write(); + BOOST_CHECK(listener1->count == 1); + BOOST_CHECK(listener1->lastProcessVariable == pv2); + BOOST_CHECK(listener2->count == 1); + BOOST_CHECK(listener2->lastProcessVariable == pv1); + // We reset the pointers stored in the listeners in order to avoid a cyclic + // dependency. + listener1->lastProcessVariable.reset(); + listener2->lastProcessVariable.reset(); + } + + // Test that the data-transfer mechanism works. + BOOST_AUTO_TEST_CASE( testSync ) { + DoubleArray::SharedPtr pv1, pv2; + // We use the same time-stamp source for both sides. The counting time-stamp + // source is not thread-safe, but we use the same thread for both sides, so + // this is okay. + auto timeStampSource = make_shared(); + // We retrieve one time stamp from the source so that the first time stamp + // returned from the source is not zero. + timeStampSource->getCurrentTimeStamp(); + double initialValue = 2.0; + tie(pv1, pv2) = createBidirectionalSynchronizedProcessArray(1, "", "", "", + initialValue, 2, timeStampSource, timeStampSource); + // Both sides should be initialized to zero. + BOOST_CHECK(pv1->accessData(0) == initialValue); + BOOST_CHECK(pv2->accessData(0) == initialValue); + // Both sides should have the same time stamp and version number. + BOOST_CHECK(pv1->getTimeStamp() == pv2->getTimeStamp()); + BOOST_CHECK(pv1->getVersionNumber() == pv2->getVersionNumber()); + // We save the time stamp and version number so that we can reuse it in + // later checks. + auto initialTimeStamp = pv1->getTimeStamp(); + auto initialVersionNumber = pv1->getVersionNumber(); + // Nothing has been written yet, so calling read() should not have any + // effects. + pv1->readNonBlocking(); + pv2->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == initialValue); + BOOST_CHECK(pv1->getTimeStamp() == initialTimeStamp); + BOOST_CHECK(pv1->getVersionNumber() == initialVersionNumber); + BOOST_CHECK(pv2->accessData(0) == initialValue); + BOOST_CHECK(pv2->getTimeStamp() == initialTimeStamp); + BOOST_CHECK(pv2->getVersionNumber() == initialVersionNumber); + // Now we write on pv1. + double newValue1 = 5.0; + pv1->accessData(0) = newValue1; + pv1->write(); + // After writing, the time stamp and version number should have increased on + // pv1, but pv2 should not have changed. + BOOST_CHECK(pv1->accessData(0) == newValue1); + BOOST_CHECK(pv1->getTimeStamp().index0 > initialTimeStamp.index0); + BOOST_CHECK(pv1->getVersionNumber() > initialVersionNumber); + auto newTimeStamp1 = pv1->getTimeStamp(); + auto newVersionNumber1 = pv1->getVersionNumber(); + BOOST_CHECK(pv2->accessData(0) == initialValue); + BOOST_CHECK(pv2->getTimeStamp() == initialTimeStamp); + BOOST_CHECK(pv2->getVersionNumber() == initialVersionNumber); + // Now we read pv2. After this, the two PVs should match again. + pv2->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == newValue1); + BOOST_CHECK(pv1->getTimeStamp() == newTimeStamp1); + BOOST_CHECK(pv1->getVersionNumber() == newVersionNumber1); + BOOST_CHECK(pv2->accessData(0) == newValue1); + BOOST_CHECK(pv2->getTimeStamp() == newTimeStamp1); + BOOST_CHECK(pv2->getVersionNumber() == newVersionNumber1); + // Now, we write on pv2. + double newValue2 = 42.0; + pv2->accessData(0) = newValue2; + pv2->write(); + // After writing, the time stamp and version number should have increased on + // pv2, but pv1 should not have changed. + BOOST_CHECK(pv2->accessData(0) == newValue2); + BOOST_CHECK(pv2->getTimeStamp().index0 > newTimeStamp1.index0); + BOOST_CHECK(pv2->getVersionNumber() > newVersionNumber1); + auto newTimeStamp2 = pv2->getTimeStamp(); + auto newVersionNumber2 = pv2->getVersionNumber(); + BOOST_CHECK(pv1->accessData(0) == newValue1); + BOOST_CHECK(pv1->getTimeStamp() == newTimeStamp1); + BOOST_CHECK(pv1->getVersionNumber() == newVersionNumber1); + // Now we read pv1. After this, the two PVs should match again. + pv1->readNonBlocking(); + BOOST_CHECK(pv1->accessData(0) == newValue2); + BOOST_CHECK(pv1->getTimeStamp() == newTimeStamp2); + BOOST_CHECK(pv1->getVersionNumber() == newVersionNumber2); + BOOST_CHECK(pv2->accessData(0) == newValue2); + BOOST_CHECK(pv2->getTimeStamp() == newTimeStamp2); + BOOST_CHECK(pv2->getVersionNumber() == newVersionNumber2); + } + + // Test that time-stamp source are used correctly. + BOOST_AUTO_TEST_CASE( testTimeStampSources ) { + DoubleArray::SharedPtr pv1, pv2; + auto timeStampSource1 = make_shared(); + auto timeStampSource2 = make_shared(); + tie(pv1, pv2) = createBidirectionalSynchronizedProcessArray(1, "", "", "", + 0.0, 2, timeStampSource1, timeStampSource2); + // Before writing, both time-stamp sources should have a count of zero. + BOOST_CHECK(timeStampSource1->count == 0); + BOOST_CHECK(timeStampSource2->count == 0); + // After writing pv1, timestampSource1 should have a count of 1. + pv1->write(); + pv2->readNonBlocking(); + BOOST_CHECK(timeStampSource1->count == 1); + BOOST_CHECK(timeStampSource2->count == 0); + // After writing pv2, timestampSource2 should also have a count of 1. + pv2->write(); + pv1->readNonBlocking(); + BOOST_CHECK(timeStampSource1->count == 1); + BOOST_CHECK(timeStampSource2->count == 1); + } + + // After you finished all test you have to end the test suite. + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/src/testProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp similarity index 90% rename from tests/src/testProcessArray.cpp rename to tests/src/testUnidirectionalProcessArray.cpp index d34a95a..92b43dd 100644 --- a/tests/src/testProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -18,7 +18,7 @@ namespace ChimeraTK { * It is templated to be tested with all data types. */ template - class ProcessArrayTest { + class UnidirectionalProcessArrayTest { public: static void testConstructors(); static void testGet(); @@ -45,25 +45,25 @@ namespace ChimeraTK { * The boost test suite which executes the ProcessArrayTest. */ template - class ProcessArrayTestSuite: public test_suite { + class UnidirectionalProcessArrayTestSuite: public test_suite { public: - ProcessArrayTestSuite() : - test_suite("ProcessArray test suite") { - add(BOOST_TEST_CASE(&ProcessArrayTest::testConstructors)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testGet)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testSet)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testSwap)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testSendNotification)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testTimeStampSource)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testSynchronization)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testVersionNumbers)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testBlockingRead)); - add(BOOST_TEST_CASE(&ProcessArrayTest::testReadLatest)); + UnidirectionalProcessArrayTestSuite() : + test_suite("UnidirectionalProcessArray test suite") { + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testConstructors)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testGet)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSet)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSwap)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSendNotification)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testTimeStampSource)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSynchronization)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testVersionNumbers)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testBlockingRead)); + add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testReadLatest)); } }; template - void ProcessArrayTest::testConstructors() { + void UnidirectionalProcessArrayTest::testConstructors() { std::vector referenceVector; referenceVector.push_back(0); @@ -128,7 +128,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testGet() { + void UnidirectionalProcessArrayTest::testGet() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); @@ -148,7 +148,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testSet() { + void UnidirectionalProcessArrayTest::testSet() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS); typename ProcessArray::SharedPtr sender = senderReceiver.first; @@ -168,7 +168,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testSwap() { + void UnidirectionalProcessArrayTest::testSwap() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = @@ -196,7 +196,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testSendNotification() { + void UnidirectionalProcessArrayTest::testSendNotification() { boost::shared_ptr sendNotificationListener( boost::make_shared()); typename std::pair::SharedPtr, @@ -215,7 +215,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testTimeStampSource() { + void UnidirectionalProcessArrayTest::testTimeStampSource() { TimeStampSource::SharedPtr timeStampSource( boost::make_shared()); typename std::pair::SharedPtr, @@ -236,7 +236,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testSynchronization() { + void UnidirectionalProcessArrayTest::testSynchronization() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); @@ -344,7 +344,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testVersionNumbers() { + void UnidirectionalProcessArrayTest::testVersionNumbers() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true, @@ -409,7 +409,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testBlockingRead() { + void UnidirectionalProcessArrayTest::testBlockingRead() { auto senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); auto sender = senderReceiver.first; auto receiver = senderReceiver.second; @@ -448,7 +448,7 @@ namespace ChimeraTK { } template - void ProcessArrayTest::testReadLatest() { + void UnidirectionalProcessArrayTest::testReadLatest() { auto senderReceiver = createSynchronizedProcessArray(1); auto sender = senderReceiver.first; auto receiver = senderReceiver.second; @@ -524,24 +524,24 @@ namespace ChimeraTK { test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { - framework::master_test_suite().p_name.value = "ProcessArray test suite"; + framework::master_test_suite().p_name.value = "UnidirectionalProcessArray test suite"; framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); framework::master_test_suite().add( - new ChimeraTK::ProcessArrayTestSuite); + new ChimeraTK::UnidirectionalProcessArrayTestSuite); return NULL; } From eb37e8f78ad7a585f6e050f05659009995049632 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Sat, 28 Oct 2017 18:23:48 +0200 Subject: [PATCH 288/786] Set maySendDestructively on internal PVs. Only one of the two createBidirectionalSynchronizedProcessArray functions set these flags, so the write operation would fail on BidirectionalProcessArrays created by the buggy function. --- .../ControlSystemAdapter/BidirectionalProcessArray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 0915be5..f954002 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -529,9 +529,9 @@ namespace ChimeraTK { ProcessVariableListener::SharedPtr sendNotificationListener1, ProcessVariableListener::SharedPtr sendNotificationListener2) { auto senderReceiver1 = createSynchronizedProcessArray(initialValue, name, - unit, description, numberOfBuffers, false, timeStampSource1); + unit, description, numberOfBuffers, true, timeStampSource1); auto senderReceiver2 = createSynchronizedProcessArray(initialValue, name, - unit, description, numberOfBuffers, false, timeStampSource2); + unit, description, numberOfBuffers, true, timeStampSource2); // We create a default-constructed time-stamp and version number because we // want to be sure that initially both sides have the same time stamp and // version number. From 813ade8e3a8204879c5bfb76832efdf0c57e59d8 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Sat, 28 Oct 2017 18:26:14 +0200 Subject: [PATCH 289/786] Add integration test for bidirectional PVs. --- tests/src/testPVManager.cpp | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index ab31a2f..8560f23 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -748,5 +748,72 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) intAcs->getTimeStamp().seconds < intBcs->getTimeStamp().seconds); } + struct TestDeviceCallable5 { + shared_ptr pvManager; + + void operator()() { + auto biDouble = pvManager->getProcessArray("biDouble"); + auto stopDeviceThread = pvManager->getProcessArray( + "stopDeviceThread"); + + while (stopDeviceThread->accessData(0) == 0) { + if (biDouble->readNonBlocking()) { + double &value = biDouble->accessData(0); + if (value > 5.0) { + value = 5.0; + biDouble->write(); + } else if (value < -5.0) { + value = -5.0; + biDouble->write(); + } + } + // On each iteration, we set the current time-stamp. This should ensure + // that all PVs receive the same time-stamp. + boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); + stopDeviceThread->readNonBlocking(); + } + } + }; + + static shared_ptr initTestDeviceLib5() { + auto pvManagers = createPVManager(); + auto csManager = pvManagers.first; + auto devManager = pvManagers.second; + + devManager->createProcessArray(bidirectional, "biDouble", 1); + devManager->createProcessArray(controlSystemToDevice, + "stopDeviceThread", 1); + + TestDeviceCallable5 callable; + callable.pvManager = devManager; + + // Start device thread. + boost::thread deviceThread(callable); + + return csManager; + } + + BOOST_AUTO_TEST_CASE( bidirectionalProcessVariable ) { + auto pvManager = initTestDeviceLib5(); + auto biDouble = pvManager->getProcessArray("biDouble"); + auto stopDeviceThread = pvManager->getProcessArray( + "stopDeviceThread"); + biDouble->accessData(0) = 2.0; + biDouble->write(); + boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); + // The value is in the allowed range, so we do not expect an update. + BOOST_CHECK(!biDouble->readNonBlocking()); + // Now we write a value that is out of range. + biDouble->accessData(0) = 25.0; + biDouble->write(); + // We expect the value to be limited to 5.0. + boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); + BOOST_CHECK(biDouble->readNonBlocking()); + BOOST_CHECK(biDouble->accessData(0) == 5.0); + // Stop the device thread. + stopDeviceThread->accessData(0) = 1; + stopDeviceThread->write(); + } + // After you finished all test you have to end the test suite. BOOST_AUTO_TEST_SUITE_END() From e4cad0a5c302a365634f3469f53dfcac15449733 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 9 Nov 2017 10:39:05 +0100 Subject: [PATCH 290/786] adapted to the changed interface of TransferElement --- CMakeLists.txt | 4 ++-- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e817fb7..5aece1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 09) -set(${PROJECT_NAME}_PATCH_VERSION 00) +set(${PROJECT_NAME}_PATCH_VERSION 01) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow @@ -46,7 +46,7 @@ ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.26 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.28 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 850e8c4..208f207 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -641,7 +641,7 @@ namespace ChimeraTK { } // return the future - mtca4u::TransferElement::activeFuture = mtca4u::TransferFuture(future, static_cast(this)); + mtca4u::TransferElement::activeFuture.reset(future, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; return mtca4u::TransferElement::activeFuture; From e5a977017ec19136d3badeb15b35be1a1197b8ee Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 9 Nov 2017 13:02:25 +0100 Subject: [PATCH 291/786] removed doxygen warnings --- .../BidirectionalProcessArray.h | 8 ++++---- .../UnidirectionalProcessArray.h | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index f954002..ec1bfc8 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -34,7 +34,7 @@ namespace ChimeraTK { * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::write()} can be + * number specifies, how many times ProcessArray::write() can be * called in a row without losing data when readNonBlocking() is not called in * between. The specified number of buffers is used for each direction. * @@ -47,7 +47,7 @@ namespace ChimeraTK { * the second returned process array. * * The optional send-notification listeners are notified every time one of the - * process array's {@link ProcessArray::write()} method is called. These + * process array's ProcessArray::write() method is called. These * listeners can be used to queue a request for the receiver's * readNonBlocking() method to be called. The process variable passed to the * listener is the receiver and not the sender. The first listener is notified @@ -90,7 +90,7 @@ namespace ChimeraTK { * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::write()} can be + * number specifies, how many times ProcessArray::write() can be * called in a row without losing data when readNonBlocking() is not called in * between. The specified number of buffers is used for each direction. * @@ -100,7 +100,7 @@ namespace ChimeraTK { * system-time when the value is sent is used. * * The optional send-notification listeners are notified every time one of the - * process array's {@link ProcessArray::write()} method is called. These + * process array's ProcessArray::write() method is called. These * listeners can be used to queue a request for the receiver's * readNonBlocking() method to be called. The process variable passed to the * listener is the receiver and not the sender. The first listener is notified diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 208f207..aa79b5d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -65,7 +65,7 @@ namespace ChimeraTK { * values without copying but losing them on the sender side. * * The optional send-notification listener is notified every time the - * sender's {@link ProcessArray::write()} method is called. It can be + * sender's ProcessArray::write() method is called. It can be * used to queue a request for the receiver's * readNonBlocking() method to be called. The process * variable passed to the listener is the receiver and not the sender. @@ -336,7 +336,7 @@ namespace ChimeraTK { * * The sender allows full read-write access. Changes that have been made to * the sender can be sent to the receiver through the - * {@link ProcessArray::write()} method. The receiver can be updated with these + * ProcessArray::write() method. The receiver can be updated with these * changes by calling its readNonBlocking() method. * * The synchronization is implemented in a thread-safe manner, so that the @@ -347,7 +347,7 @@ namespace ChimeraTK { * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::write()} can be called + * number specifies, how many times ProcessArray::write() can be called * in a row without losing data when readNonBlocking() is not * called in between. * @@ -362,7 +362,7 @@ namespace ChimeraTK { * system-time when the value is sent is used. * * The optional send-notification listener is notified every time the sender's - * {@link ProcessArray::write()} method is called. It can be used to queue a + * ProcessArray::write() method is called. It can be used to queue a * request for the receiver's readNonBlocking() method to be * called. The process variable passed to the listener is the receiver and * not the sender. @@ -385,7 +385,7 @@ namespace ChimeraTK { * * The sender allows full read-write access. Changes that have been made to * the sender can be sent to the receiver through the - * {@link ProcessArray::write()} method. The receiver can be updated with these + * ProcessArray::write() method. The receiver can be updated with these * changes by calling its readNonBlocking() method. * * The synchronization is implemented in a thread-safe manner, so that the @@ -396,7 +396,7 @@ namespace ChimeraTK { * * The number of buffers specifies how many buffers are allocated for the * send / receive mechanism. The minimum number (and default) is two. This - * number specifies, how many times {@link ProcessArray::write()} can be called + * number specifies, how many times ProcessArray::write() can be called * in a row without losing data when readNonBlocking() is not * called in between. * @@ -411,7 +411,7 @@ namespace ChimeraTK { * system-time when the value is sent is used. * * The optional send-notification listener is notified every time the sender's - * {@link ProcessArray::write()} method is called. It can be used to queue a + * ProcessArray::write() method is called. It can be used to queue a * request for the receiver's readNonBlocking() method to be * called. The process variable passed to the listener is the receiver and * not the sender. From fbf82fc05bfbe28df2bbb21c099a0ad5a2b838d9 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 9 Nov 2017 16:48:02 +0100 Subject: [PATCH 292/786] Added TransferFutureDecorator and tests for asyncronous read, which is now giving out a decorated future instead of starting a thread to wait for the original future to finish. --- .../TransferFutureDecorator.h | 49 +++++++++++++++++ .../TypeChangingDecorator.h | 21 ++++++- tests/src/testTypeChangingDecorator.cpp | 55 ++++++++++++++++--- 3 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h diff --git a/include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h new file mode 100644 index 0000000..7f01d87 --- /dev/null +++ b/include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h @@ -0,0 +1,49 @@ +#ifndef CHIMERATK_TRANSFER_FUTURE_DECORATOR_H +#define CHIMERATK_TRANSFER_FUTURE_DECORATOR_H + +#include + +namespace mtca4u { + class TransferElement; + class TransferFutureIterator; +} + +namespace ChimeraTK { + template + class TypeChangingDecorator; + + template + class TransferFutureDecorator: public TransferFuture { + public: + + virtual void wait() override{ + _originalFuture->wait(); + _decorator->convertAndCopyFromImpl(); + _decorator->hasActiveFuture = false; + } + + TransferFutureDecorator() + : _originalFuture(nullptr), _decorator(nullptr) + {} + + /** Constructor accepting another transfer future. I think I don't need anything else.*/ + TransferFutureDecorator(TransferFuture * futureToBeDecorated, TypeChangingDecorator *decorator) + : _originalFuture(futureToBeDecorated), _decorator(decorator) + {} + + void reset(TransferFuture * futureToBeDecorated, TypeChangingDecorator *decorator){ + _originalFuture = futureToBeDecorated; + _decorator = decorator; + } + + protected: + // the decorated accessor will allways hold the instance of the future, so we just store a pointer + TransferFuture *_originalFuture; + + // The decorator will always hold the instance of this future, so we can use a plain pointer + TypeChangingDecorator *_decorator; + }; + +} /* namespace ChimeraTK */ + +#endif /* CHIMERATK_TRANSFER_FUTURE_DECORATOR_H */ diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 05abf22..af87b05 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -4,6 +4,7 @@ #include #include #include +#include "TransferFutureDecorator.h" namespace ChimeraTK { /** There are three types of TypeChanging decorators which do different data conversions @@ -149,9 +150,14 @@ namespace ChimeraTK { return _impl->getFixedPointConverter(); } + virtual TransferFuture& readAsync() override; + protected: boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; + TransferFutureDecorator _activeDecoratedFuture; + + friend TransferFutureDecorator; }; /** This class is intended as a base class and intentionally does not call shutdown in the destructor in order not to shadow the @@ -285,7 +291,20 @@ namespace ChimeraTK { channel.resize(impl->getNumberOfSamples()); } } - + + template + TransferFuture& TypeChangingDecorator::readAsync(){ + ChimeraTK::ExperimentalFeatures::check("asynchronous read"); + if(this->hasActiveFuture) return _activeDecoratedFuture; // the last future given out by this fuction is still active + //no active future. Call readAsync on the decorated transfer element, then decorate the + // future it returns + auto & originalFuture = _impl->readAsync(); + _activeDecoratedFuture.reset(&originalFuture, this); + this->hasActiveFuture=true; + + return _activeDecoratedFuture; + } + /*********************************************************************************************************************/ template diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 6f53b6a..a4b5db7 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -9,9 +9,32 @@ using namespace ChimeraTK; #include #include +//function which can be called many times but only call enable() the first time +void enableExperimental(){ + static bool isEnabled(false); + if (!isEnabled){ + ChimeraTK::ExperimentalFeatures::enable();// still experimental. + isEnabled = true; + } +} + // always test close for floating point values. Don't rely on exact binary representations bool test_close(double a, double b, double tolerance = 0.0001){ - return (std::fabs(a - b) < tolerance ); + if (std::fabs(a - b) < tolerance ){ + return true; + }else{ + std::cout << a << " - " << b << " is not < " << tolerance << std::endl; + return false; + } +} + +bool test_not_close(double a, double b, double tolerance = 0.0001){ + if (std::fabs(a - b) > tolerance ){ + return true; + }else{ + std::cout << a << " - " << b << " is not > " << tolerance << std::endl; + return false; + } } BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) @@ -40,7 +63,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue anotherScalarAccessor = startReadValue; anotherScalarAccessor.write(); // check that the values are different at start so we know the test is sensitive - BOOST_CHECK( !test_close(decoratedScalar.accessData(0), expectedReadValue) ); + BOOST_CHECK( test_not_close(decoratedScalar.accessData(0), expectedReadValue) ); decoratedScalar.read(); // internal precision of the register is 16 fractional bits fixed point BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue) ); @@ -76,6 +99,20 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue anotherScalarAccessor.read(); BOOST_CHECK( test_close(anotherScalarAccessor, expectedWriteValue+1) ); + //test asynchronouy reading. Check that the modification is arriving in the decorator's buffer + enableExperimental(); + + // just to check that the test is not producing false positives by accident + assert( fabs(startReadValue+2 - (expectedWriteValue+1)) > 0.001 ); + anotherScalarAccessor = startReadValue+2; + anotherScalarAccessor.write(); + + auto & future = decoratedScalar.readAsync(); + // nothing must change on the user buffer yet + BOOST_CHECK( test_close(decoratedScalar.accessData(0), startWriteValue+1) ); + future.wait(); // this calls the post-reads correctly + BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue+2) ); + // FIXME: We cannot test that the decorator is relaying doReadTransfer, doReadTransferLatest and // do readTransferLatest correctly with the dummy backend because they all point to the same // implementation. Thus we intentionally do not call them to indicate them uncovered. @@ -97,7 +134,7 @@ BOOST_AUTO_TEST_CASE( testAllDecoratorConversions ){ testDecorator(18.4,18, 28, 28); testDecorator(19.5,20, 29, 29); testDecorator(19.4,19, 29, 29); - testDecorator(101, 101, 102, 102); + testDecorator(101, 101, 112, 112); testDecorator(112,112, -122.4, -122); testDecorator(112,112, -122.5, -123); @@ -113,14 +150,14 @@ BOOST_AUTO_TEST_CASE( testAllDecoratorConversions ){ testDecorator(117,117, 127.5, 128); testDecorator(118.5,118.5, 128.6, 128.6); testDecorator(119.5,119.5, 129.6, 129.6); - testDecorator(101.1, 101.1, 102.2, 102.2); - testDecorator(201.1, 201.1, 202.2, 202.2); + testDecorator(101.1, 101.1, 112.2, 112.2); + testDecorator(201.1, 201.1, 212.2, 212.2); - testDecorator(201, 201, 202, 202); - testDecorator(201, 201, 202, 202); - testDecorator(201, 201, 202, 202); + testDecorator(201, 201, 212, 212); + testDecorator(202, 202, 213, 213); + testDecorator(203, 203, 214, 214); testDecorator(218.5,218, 228, 228); - testDecorator(228, 228, 229.6, 229); + testDecorator(228, 228, 239.6, 239); } From cc1c8cc9d9b1f0a04a2e92f22a91134c0ec56f0c Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 13 Nov 2017 14:13:26 +0100 Subject: [PATCH 293/786] reenabled an assert() --- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 208f207..1a3522b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -572,6 +572,7 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferLatest() { + // As long as there is more than one valid element on the queue, discard it. // Due to our implementation there is always one unfulfilled future in the queue, so // we must pop until there are two elements left in order not to flush out the newest valid value. @@ -651,6 +652,7 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::postRead() { + mtca4u::TransferElement::hasActiveFuture = false; // We have to check that the vector that we currently own still has the @@ -676,12 +678,7 @@ namespace ChimeraTK { bool queueHasData = (status != boost::future_status::timeout); // If postRead() has been called, there should be new data somewhere - /// @todo FIXME find this problem and make an assert again! - //assert(queueHasData || tripleBufferHasData); - if(!(queueHasData || tripleBufferHasData)) { - std::cout << "ERROR: assert(queueHasData || tripleBufferHasData) failed! Waiting for new data..." << std::endl; - future.wait(); - } + assert(queueHasData || tripleBufferHasData); // Determine whether to use the data from the queue or from the triple buffer. If in both places data is present, // use the older data (by the version number). From 587839ad7470c9867541c59006466d9a49734bf8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 13 Nov 2017 15:23:13 +0100 Subject: [PATCH 294/786] increased minor version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5aece1b..820e368 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 09) -set(${PROJECT_NAME}_PATCH_VERSION 01) +set(${PROJECT_NAME}_MINOR_VERSION 10) +set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From 2c59f6cb6ad63bc8f1e8f035f7f98403c4728366 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 13 Nov 2017 15:57:10 +0100 Subject: [PATCH 295/786] Make bidirectional process variables experimental for now, since their usability should be proven under realistic conditions before allowing them in production use. --- .../BidirectionalProcessArray.h | 34 ++++++++++++++----- tests/src/testBidirectionalProcessArray.cpp | 8 +++++ tests/src/testPVManager.cpp | 2 ++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index ec1bfc8..0ed859e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -14,6 +14,8 @@ #include #include +#include + #include "ProcessArray.h" #include "ProcessVariableListener.h" #include "TimeStampSource.h" @@ -61,6 +63,10 @@ namespace ChimeraTK { * Of the two returned process arrays, only the first one can take an * optional persistent data storage. Trying to set a persistent data storage * on the first one results in an exception. + * + * Note: biderectional process arrays are still experimental, so experimental + * features must be enable when using this function (see + * ChimeraTK::ExperimentalFeatures in DeviceAccess). */ template std::pair::SharedPtr, @@ -116,6 +122,10 @@ namespace ChimeraTK { * Of the two returned process arrays, only the first one can take an * optional persistent data storage. Trying to set a persistent data storage * on the first one results in an exception. + * + * Note: biderectional process arrays are still experimental, so experimental + * features must be enable when using this function (see + * ChimeraTK::ExperimentalFeatures in DeviceAccess). */ template std::pair::SharedPtr, @@ -137,6 +147,10 @@ namespace ChimeraTK { * directions. * * This class is not thread-safe and should only be used from a single thread. + * + * Note: biderectional process arrays are still experimental, so experimental + * features must be enable when using this class (see + * ChimeraTK::ExperimentalFeatures in DeviceAccess). */ template class BidirectionalProcessArray : public ProcessArray { @@ -320,14 +334,18 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr sender, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, - TimeStamp initialTimeStamp, VersionNumber initialVersionNumber) : - ProcessArray(ProcessArray::SENDER_RECEIVER, name, unit, - description), _allowPersistentDataStorage(allowPersistentDataStorage), _receiver( - receiver), _sender( - boost::dynamic_pointer_cast>(sender)), _sendNotificationListener( - sendNotificationListener), _timeStamp(initialTimeStamp), _timeStampSource( - timeStampSource), _uniqueId(uniqueId), _versionNumber( - initialVersionNumber) { + TimeStamp initialTimeStamp, VersionNumber initialVersionNumber) + : ProcessArray(ProcessArray::SENDER_RECEIVER, name, unit, description), + _allowPersistentDataStorage(allowPersistentDataStorage), + _receiver(receiver), + _sender(boost::dynamic_pointer_cast>(sender)), + _sendNotificationListener(sendNotificationListener), + _timeStamp(initialTimeStamp), + _timeStampSource(timeStampSource), + _uniqueId(uniqueId), + _versionNumber(initialVersionNumber) + { + ChimeraTK::ExperimentalFeatures::check("BidirectionalProcessArray"); // If the passed sender was not null but the class variable is, the dynamic // cast failed. if (sender && !_sender) { diff --git a/tests/src/testBidirectionalProcessArray.cpp b/tests/src/testBidirectionalProcessArray.cpp index 4d756dc..939ef20 100644 --- a/tests/src/testBidirectionalProcessArray.cpp +++ b/tests/src/testBidirectionalProcessArray.cpp @@ -27,6 +27,8 @@ BOOST_AUTO_TEST_SUITE( BidirectionalProcessArrayTestSuite ) // Test that a delayed incoming update with an older version does not // overwrite a newer version. BOOST_AUTO_TEST_CASE( testConflictingUpdates ) { + ChimeraTK::ExperimentalFeatures::enable(); + DoubleArray::SharedPtr pv1, pv2; double initialValue = 3.5; tie(pv1, pv2) = createBidirectionalSynchronizedProcessArray(1, "", "", "", @@ -81,6 +83,8 @@ BOOST_AUTO_TEST_SUITE( BidirectionalProcessArrayTestSuite ) // Test that send notification listeners are called correctly. BOOST_AUTO_TEST_CASE( testListeners ) { + ChimeraTK::ExperimentalFeatures::enable(); + DoubleArray::SharedPtr pv1, pv2; TimeStampSource::SharedPtr nullTimeStampSource; auto listener1 = make_shared(); @@ -115,6 +119,8 @@ BOOST_AUTO_TEST_SUITE( BidirectionalProcessArrayTestSuite ) // Test that the data-transfer mechanism works. BOOST_AUTO_TEST_CASE( testSync ) { + ChimeraTK::ExperimentalFeatures::enable(); + DoubleArray::SharedPtr pv1, pv2; // We use the same time-stamp source for both sides. The counting time-stamp // source is not thread-safe, but we use the same thread for both sides, so @@ -194,6 +200,8 @@ BOOST_AUTO_TEST_SUITE( BidirectionalProcessArrayTestSuite ) // Test that time-stamp source are used correctly. BOOST_AUTO_TEST_CASE( testTimeStampSources ) { + ChimeraTK::ExperimentalFeatures::enable(); + DoubleArray::SharedPtr pv1, pv2; auto timeStampSource1 = make_shared(); auto timeStampSource2 = make_shared(); diff --git a/tests/src/testPVManager.cpp b/tests/src/testPVManager.cpp index 8560f23..bb18b1f 100644 --- a/tests/src/testPVManager.cpp +++ b/tests/src/testPVManager.cpp @@ -794,6 +794,8 @@ BOOST_AUTO_TEST_SUITE( PVManagerTestSuite ) } BOOST_AUTO_TEST_CASE( bidirectionalProcessVariable ) { + ChimeraTK::ExperimentalFeatures::enable(); + auto pvManager = initTestDeviceLib5(); auto biDouble = pvManager->getProcessArray("biDouble"); auto stopDeviceThread = pvManager->getProcessArray( From 165640177967045c719faf56b9343c2a67f4b618 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 20 Nov 2017 16:28:15 +0100 Subject: [PATCH 296/786] removed trailing blanks --- .../UnidirectionalProcessArray.h | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 2ab7d55..1b43645 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -22,7 +22,7 @@ #include "PersistentDataStorage.h" namespace ChimeraTK { - + /** * Implementation of the process array that transports data in a single * direction. This implementation is used for both sides (sender and @@ -93,7 +93,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; mtca4u::TransferFuture& readAsync() override; - + void postRead() override; bool write(ChimeraTK::VersionNumber versionNumber={}) override; @@ -542,12 +542,12 @@ namespace ChimeraTK { } this->shutdown(); } - + /*********************************************************************************************************************/ template void UnidirectionalProcessArray::doReadTransfer() { - + // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call // TransferFuture::wait(). readAsync().getBoostFuture().wait(); @@ -617,17 +617,17 @@ namespace ChimeraTK { // blocking and there is valid data to be read in the atomic triple buffer. // This is only a preformance optimisation, we would generate the same future again. if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; - + if (!this->isReadable()) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } - + // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this // function even if the future has not yet been used still result in the correct behaviour. assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); - + // if the future blocks, check for data in the tripple buffer auto status = future.wait_for(boost::chrono::duration(0)); if(status == boost::future_status::timeout) { @@ -640,14 +640,14 @@ namespace ChimeraTK { future = boost::make_ready_future(static_cast(_tripleBufferIndex)).share(); } } - + // return the future mtca4u::TransferElement::activeFuture.reset(future, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; return mtca4u::TransferElement::activeFuture; } - + /*********************************************************************************************************************/ template @@ -662,15 +662,15 @@ namespace ChimeraTK { throw std::runtime_error("Cannot run read operation because the size of the vector belonging to the current" " buffer has been modified."); } - + // If local buffer of triple buffer is invalid, swap with shared buffer first if(!(_tripleBufferIndex->_isValid)) { _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); } - + // Determine version number of data in the triple buffer bool tripleBufferHasData = _tripleBufferIndex->_isValid; - + // Determine version number of data in the queue assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); @@ -750,7 +750,7 @@ namespace ChimeraTK { _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(), versionNumber, false); } - + /*********************************************************************************************************************/ template @@ -781,7 +781,7 @@ namespace ChimeraTK { write(); } } - + /*********************************************************************************************************************/ template @@ -812,18 +812,18 @@ namespace ChimeraTK { if(_persistentDataStorage) { _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); } - + // Before sending the value, we have to set the associated time-stamp. _currentIndex->timeStamp = newTimeStamp; - + // set the version numbers _currentIndex->_versionNumber = newVersionNumber; _currentIndex->_internalVersionNumber = _lastInternalVersionNumber; ++_lastInternalVersionNumber; - + // flag current data as valid _currentIndex->_isValid = true; - + // set the data by copying or swapping if(shouldCopy) { _currentIndex->value = mtca4u::NDRegisterAccessor::buffer_2D[0]; @@ -854,14 +854,14 @@ namespace ChimeraTK { _tripleBufferIndex->_isValid = false; } } - + // change current index to the new empty buffer if(shouldCopy) { nextIndex->timeStamp = newTimeStamp; nextIndex->_versionNumber = newVersionNumber; } _currentIndex = nextIndex; - + // notify send notification listener, if present if(_sendNotificationListener) { _sendNotificationListener->notify(_receiver); From 567bfacaeef111c257bd1bdb84db5228e084a946 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 22 Nov 2017 18:43:30 +0100 Subject: [PATCH 297/786] removed virtual keyword from overrides (since it is redundant) --- .../TypeChangingDecorator.h | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 05abf22..44ba727 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -8,7 +8,7 @@ namespace ChimeraTK { /** There are three types of TypeChanging decorators which do different data conversions * from the user data type to the implementation data type. - * + * * \li range_checking This decorator checks the limits and throws an exception of the range is exceeced (for instance if you cast a negative integer to an unsiged int, or 500 to int8_t). * This decorator does proper rounding from floating point types t o integer (e.g. 6.7 to 7). * \li limiting This decorator limits the data to the maximum possible in the target data type (for instance 500 will result in 127 in int8_t and 255 in uint8_t, -200 will be -128 in int8t, 0 in uint8_t). @@ -18,7 +18,7 @@ namespace ChimeraTK { enum class DecoratorType{ range_checking, limiting, C_style_conversion}; /** The factory function for type changing decorators. - * + * * TypeChanging decorators take a transfer element (usually NDRegisterAccessor) and wraps it in an NDRegisterAccessor. * It automatically performs the right type conversion (configurable as argument of the factory function). * The decorator has it's own buffer of type UserType and synchronises it in the preWrite() and postRead() functions with the implementation. @@ -33,7 +33,7 @@ namespace ChimeraTK { //=================================================================================================================================================== //Everything from here are implementation details. The exact identity of the decorator should not matter to the user, it would break the abstraction. - //=================================================================================================================================================== + //=================================================================================================================================================== // An intermediate base class which is only typed on the user type and has a function to // provide the decorator type. It is needed to have something to cast to when the @@ -44,14 +44,14 @@ namespace ChimeraTK { using mtca4u::NDRegisterAccessor::NDRegisterAccessor; virtual DecoratorType getDecoratorType() const = 0; }; - + /** - * Strictly this is not a real decorator. It provides an NDRegisterAccessor of a + * Strictly this is not a real decorator. It provides an NDRegisterAccessor of a * different template type and accesses the original one as "backend" under the hood. * * This is the base class which has pure virtual convertAndCopyFromImpl and convertAndCopyToImpl functions. * Apart from this it implements all decorating functions which just pass through to the impl. - * It intentionally does not call shutdown in the constructor in order not to shadow the + * It intentionally does not call shutdown in the constructor in order not to shadow the * shutdown detection for the child classes, so it is not forgotten there. * * This class is not thread-safe and should only be used from a single thread. @@ -64,97 +64,97 @@ namespace ChimeraTK { TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept; - virtual bool isReadable() const override { + bool isReadable() const override { return _impl->isReadable(); } - virtual bool isWriteable() const override { + bool isWriteable() const override { return _impl->isWriteable(); } - virtual bool isReadOnly() const override { + bool isReadOnly() const override { return _impl->isReadOnly(); } - - virtual mtca4u::TimeStamp getTimeStamp() const override { + + mtca4u::TimeStamp getTimeStamp() const override { return _impl->getTimeStamp(); } - virtual ChimeraTK::VersionNumber getVersionNumber() const override { + ChimeraTK::VersionNumber getVersionNumber() const override { return _impl->getVersionNumber(); } - virtual void doReadTransfer() override{ + void doReadTransfer() override{ _impl->doReadTransfer(); } - virtual bool doReadTransferNonBlocking() override{ + bool doReadTransferNonBlocking() override{ return _impl->doReadTransferNonBlocking(); } - virtual bool doReadTransferLatest() override{ + bool doReadTransferLatest() override{ return _impl->doReadTransferLatest(); } virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; - - virtual void postRead() override{ + + void postRead() override{ _impl->postRead(); convertAndCopyFromImpl(); } - - virtual void preWrite() override{ + + void preWrite() override{ convertAndCopyToImpl(); _impl->preWrite(); } - virtual void postWrite() override{ + void postWrite() override{ _impl->postWrite(); } - - virtual bool write(ChimeraTK::VersionNumber versionNumber={}) override { + + bool write(ChimeraTK::VersionNumber versionNumber={}) override { // don't call preWrite() here. It would trigger the preWrite on the impl, which is also // happening when _impl()->write() is called. convertAndCopyToImpl(); return _impl->write(versionNumber); } - virtual bool isArray() const override { + bool isArray() const override { return _impl->isArray(); } /// @todo FIXME: can we unite stuff in a transfer group? Compare with impl? - virtual bool isSameRegister(const boost::shared_ptr& e) const override { + bool isSameRegister(const boost::shared_ptr& e) const override { return _impl->isSameRegister(e); } - virtual std::vector > getHardwareAccessingElements() override { + std::vector > getHardwareAccessingElements() override { return _impl->getHardwareAccessingElements(); } - - virtual void replaceTransferElement(boost::shared_ptr e) override { + + void replaceTransferElement(boost::shared_ptr e) override { _impl->replaceTransferElement(e); } - - virtual void setPersistentDataStorage(boost::shared_ptr storage) override{ + + void setPersistentDataStorage(boost::shared_ptr storage) override{ _impl->setPersistentDataStorage(storage); } - virtual unsigned int getNInputQueueElements() const override { + unsigned int getNInputQueueElements() const override { return _impl->getNInputQueueElements(); } - virtual mtca4u::FixedPointConverter getFixedPointConverter() const override { + mtca4u::FixedPointConverter getFixedPointConverter() const override { return _impl->getFixedPointConverter(); } protected: - + boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; }; - /** This class is intended as a base class and intentionally does not call shutdown in the destructor in order not to shadow the + /** This class is intended as a base class and intentionally does not call shutdown in the destructor in order not to shadow the * shutdown detection for the child classes, so it is not forgotten there. * It provides only partial template specialisations for the string stuff. Don't directly instantiate this class. The factory would * fail when looping over all implementation types. @@ -167,7 +167,7 @@ namespace ChimeraTK { /// A sub-namespace in order not to expose the classes to the ChimeraTK namespace namespace csa_helpers{ - + template T stringToT(std::string const & input){ std::stringstream s; @@ -176,7 +176,7 @@ namespace ChimeraTK { s >> t; return t; } - + template std::string T_ToString(T input){ std::stringstream s; @@ -185,13 +185,13 @@ namespace ChimeraTK { s >> output; return output; } - + template struct Round { static S nearbyint ( S s ){ return round(s); } - + typedef boost::mpl::integral_c round_style ; }; }// namespace csa_helpers @@ -231,7 +231,7 @@ namespace ChimeraTK { virtual ~TypeChangingRangeCheckingDecorator(){ this->shutdown(); } - virtual DecoratorType getDecoratorType() const override{ + DecoratorType getDecoratorType() const override{ return DecoratorType::range_checking; } private: @@ -245,7 +245,7 @@ namespace ChimeraTK { errorMessage("Exception during type changing conversion in " + variableName + ": " + BOOST_EXCEPTION_T().what()){ } std::string errorMessage; - virtual const char* what() const throw() override{ + const char* what() const throw() override{ return errorMessage.c_str(); } }; @@ -266,12 +266,12 @@ namespace ChimeraTK { virtual ~TypeChangingRangeCheckingDecorator(){ this->shutdown(); } - virtual DecoratorType getDecoratorType() const override{ + DecoratorType getDecoratorType() const override{ return DecoratorType::range_checking; } }; - + /*********************************************************************************************************************/ /*** Implementations of member functions below this line *************************************************************/ /*********************************************************************************************************************/ @@ -285,7 +285,7 @@ namespace ChimeraTK { channel.resize(impl->getNumberOfSamples()); } } - + /*********************************************************************************************************************/ template @@ -328,7 +328,7 @@ namespace ChimeraTK { class TypeChangingDirectCastDecorator : public TypeChangingStringImplDecorator { public: using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; - virtual DecoratorType getDecoratorType() const override{ + DecoratorType getDecoratorType() const override{ return DecoratorType::C_style_conversion; } @@ -340,7 +340,7 @@ namespace ChimeraTK { } } } - + void convertAndCopyToImpl() override { for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ @@ -368,7 +368,7 @@ namespace ChimeraTK { virtual ~TypeChangingDirectCastDecorator(){ this->shutdown(); } - virtual DecoratorType getDecoratorType() const override{ + DecoratorType getDecoratorType() const override{ return DecoratorType::C_style_conversion; } }; @@ -379,7 +379,7 @@ namespace ChimeraTK { */ std::map< boost::shared_ptr, boost::shared_ptr > & getGlobalDecoratorMap(); - + template class DecoratorFactory{ public: @@ -388,12 +388,12 @@ namespace ChimeraTK { boost::shared_ptr< mtca4u::TransferElement > theImpl; DecoratorType wantedDecoratorType; mutable boost::shared_ptr > createdDecorator; - + template void operator()(PAIR&) const { typedef typename PAIR::first_type TargetImplType; if(typeid(TargetImplType) != theImpl->getValueType() ) return; - + if (wantedDecoratorType == DecoratorType::range_checking){ createdDecorator.reset( new TypeChangingRangeCheckingDecorator< UserType, TargetImplType> ( boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); @@ -425,7 +425,7 @@ namespace ChimeraTK { }else{ // sorry there is a decorator, but it's the wrong user type throw std::logic_error("ChimeraTK::ControlSystemAdapter: Decorator for TransferElement " + - transferElement.getName() +" already exists as a different decorator type."); + transferElement.getName() +" already exists as a different decorator type."); } }else{ // sorry there is a decorator, but it's the wrong user type @@ -433,13 +433,13 @@ namespace ChimeraTK { transferElement.getName() +" already exists with a different user type."); } } - + DecoratorFactory< UserType > factory( transferElement.getHighLevelImplElement(), decoratorType ); boost::fusion::for_each(mtca4u::userTypeMap(), factory); getGlobalDecoratorMap()[transferElement.getHighLevelImplElement()]=factory.createdDecorator; return factory.createdDecorator; - } - + } + } // namespace ChimeraTK #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H From bef8c2fdf14af11cdd0aab0eca5eb261fe5a5fe8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 22 Nov 2017 18:49:02 +0100 Subject: [PATCH 298/786] necessary changes for recent update in DeviceAccess: readAsync() default implementation was moved out of the NDRegisterAccessor base class. --- .../BidirectionalProcessArray.h | 23 +-- .../TypeChangingDecorator.h | 21 +-- .../UnidirectionalProcessArray.h | 152 +++++++----------- 3 files changed, 75 insertions(+), 121 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 0ed859e..7f94758 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -22,7 +22,7 @@ #include "PersistentDataStorage.h" namespace ChimeraTK { - + /** * Creates a bidirectional synchronized process array. A bidirectional * synchronized process array works as a pair of two process arrays, where @@ -63,7 +63,7 @@ namespace ChimeraTK { * Of the two returned process arrays, only the first one can take an * optional persistent data storage. Trying to set a persistent data storage * on the first one results in an exception. - * + * * Note: biderectional process arrays are still experimental, so experimental * features must be enable when using this function (see * ChimeraTK::ExperimentalFeatures in DeviceAccess). @@ -122,7 +122,7 @@ namespace ChimeraTK { * Of the two returned process arrays, only the first one can take an * optional persistent data storage. Trying to set a persistent data storage * on the first one results in an exception. - * + * * Note: biderectional process arrays are still experimental, so experimental * features must be enable when using this function (see * ChimeraTK::ExperimentalFeatures in DeviceAccess). @@ -147,7 +147,7 @@ namespace ChimeraTK { * directions. * * This class is not thread-safe and should only be used from a single thread. - * + * * Note: biderectional process arrays are still experimental, so experimental * features must be enable when using this class (see * ChimeraTK::ExperimentalFeatures in DeviceAccess). @@ -192,8 +192,6 @@ namespace ChimeraTK { ProcessVariableListener::SharedPtr sendNotificationListener, TimeStamp initialTimeStamp, VersionNumber initialVersionNumber); - ~BidirectionalProcessArray(); - TimeStamp getTimeStamp() const override { return _timeStamp; } @@ -209,7 +207,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; mtca4u::TransferFuture& readAsync() override; - + void postRead() override; bool write(ChimeraTK::VersionNumber versionNumber={}) override; @@ -365,11 +363,6 @@ namespace ChimeraTK { mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->accessChannel(0); } - template - BidirectionalProcessArray::~BidirectionalProcessArray() { - this->shutdown(); - } - /*********************************************************************************************************************/ template @@ -397,7 +390,7 @@ namespace ChimeraTK { mtca4u::TransferFuture& BidirectionalProcessArray::readAsync() { return _receiver->readAsync(); } - + /*********************************************************************************************************************/ template @@ -467,7 +460,7 @@ namespace ChimeraTK { throw std::runtime_error( "This process variable must not be sent destructively because it is a bidirectional process variable."); } - + /*********************************************************************************************************************/ template @@ -491,7 +484,7 @@ namespace ChimeraTK { write(); } } - + /*********************************************************************************************************************/ /*** Implementations of non-member functions below this line *********************************************************/ /*********************************************************************************************************************/ diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 44ba727..1fddd8f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -51,8 +51,6 @@ namespace ChimeraTK { * * This is the base class which has pure virtual convertAndCopyFromImpl and convertAndCopyToImpl functions. * Apart from this it implements all decorating functions which just pass through to the impl. - * It intentionally does not call shutdown in the constructor in order not to shadow the - * shutdown detection for the child classes, so it is not forgotten there. * * This class is not thread-safe and should only be used from a single thread. * @@ -96,6 +94,10 @@ namespace ChimeraTK { return _impl->doReadTransferLatest(); } + ChimeraTK::TransferFuture& readAsync() override { + return _impl->readAsync(); + } + virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; @@ -154,8 +156,7 @@ namespace ChimeraTK { boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; }; - /** This class is intended as a base class and intentionally does not call shutdown in the destructor in order not to shadow the - * shutdown detection for the child classes, so it is not forgotten there. + /** This class is intended as a base class. * It provides only partial template specialisations for the string stuff. Don't directly instantiate this class. The factory would * fail when looping over all implementation types. */ @@ -228,9 +229,6 @@ namespace ChimeraTK { using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; virtual void convertAndCopyFromImpl(); virtual void convertAndCopyToImpl(); - virtual ~TypeChangingRangeCheckingDecorator(){ - this->shutdown(); - } DecoratorType getDecoratorType() const override{ return DecoratorType::range_checking; } @@ -263,9 +261,6 @@ namespace ChimeraTK { virtual void convertAndCopyToImpl(){ TypeChangingStringImplDecorator::convertAndCopyToImpl(); } - virtual ~TypeChangingRangeCheckingDecorator(){ - this->shutdown(); - } DecoratorType getDecoratorType() const override{ return DecoratorType::range_checking; } @@ -348,9 +343,6 @@ namespace ChimeraTK { } } } - ~TypeChangingDirectCastDecorator(){ - this->shutdown(); - } }; /** Partial template specialisation for strings as impl type. @@ -365,9 +357,6 @@ namespace ChimeraTK { virtual void convertAndCopyToImpl(){ TypeChangingStringImplDecorator::convertAndCopyToImpl(); } - virtual ~TypeChangingDirectCastDecorator(){ - this->shutdown(); - } DecoratorType getDecoratorType() const override{ return DecoratorType::C_style_conversion; } diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 1b43645..424ec73 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -76,8 +76,6 @@ namespace ChimeraTK { ProcessVariableListener::SharedPtr sendNotificationListener, UnidirectionalProcessArray::SharedPtr receiver); - ~UnidirectionalProcessArray(); - TimeStamp getTimeStamp() const override { return _currentIndex->timeStamp; } @@ -264,8 +262,7 @@ namespace ChimeraTK { /** * The promise corresponding to the unfulfilled future in the _fullBufferQueue. - * This promise is basically only used by the sender. Only in the destructor of the receiver it is used to - * shutdown properly, thus it must be in the shared state. + * This promise is basically only used by the sender. */ TransferFuture::PromiseType _notificationPromise; @@ -307,6 +304,11 @@ namespace ChimeraTK { */ boost::shared_ptr _persistentDataStorage; + /** + * The last future returned by readAsync + */ + mtca4u::TransferFuture activeFuture; + /** * Variable ID for the persistent data storage */ @@ -442,45 +444,40 @@ namespace ChimeraTK { initialValue.size()), _maySendDestructively(false), _sharedState( boost::make_shared(numberOfBuffers)), _currentIndex( &(_sharedState->_buffers[0])), _tripleBufferIndex( - &(_sharedState->_buffers[4])) { - try { - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (!this->isReadable()) { - throw std::invalid_argument("This constructor may only be used for a receiver process variable."); - } - // We need at least two buffers for the queue (so four buffers in total) - // in order to guarantee that we never have to block. - if (numberOfBuffers < 2) { - throw std::invalid_argument("The number of buffers must be at least two."); - } - // We have to limit the number of buffers because we cannot allocate - // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { - throw std::invalid_argument("The number of buffers is too large."); - } - // We have to initialize the buffers by copying in the initial vectors. - for (auto &i : _sharedState->_buffers) { - i.value = initialValue; - } - // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the - // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of - // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. - for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { - _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); - } - } - catch(...) { - this->shutdown(); - throw; + &(_sharedState->_buffers[4])) + { + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (!this->isReadable()) { + throw std::invalid_argument("This constructor may only be used for a receiver process variable."); + } + // We need at least two buffers for the queue (so four buffers in total) + // in order to guarantee that we never have to block. + if (numberOfBuffers < 2) { + throw std::invalid_argument("The number of buffers must be at least two."); + } + // We have to limit the number of buffers because we cannot allocate + // more buffers than can be represented by size_t and the total number + // of buffers is the specified number plus two. These extra two buffers + // are needed because the sender and the receiver each hold one buffer + // at all times and thus only the rest remains for the queue. We use + // this definition of the number of buffers to keep it consistent with + // what ProcessScalarImpl uses. + if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { + throw std::invalid_argument("The number of buffers is too large."); + } + // We have to initialize the buffers by copying in the initial vectors. + for (auto &i : _sharedState->_buffers) { + i.value = initialValue; + } + // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the + // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of + // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. + for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { + _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); } } @@ -497,50 +494,25 @@ namespace ChimeraTK { maySendDestructively), _sharedState(receiver->_sharedState), _currentIndex( &(_sharedState->_buffers[1])), _tripleBufferIndex( &(_sharedState->_buffers[2])), _receiver(receiver), _timeStampSource( - timeStampSource), _sendNotificationListener(sendNotificationListener) { - try { - // It would be better to do the validation before initializing, but this - // would mean that we would have to initialize twice. - if (!this->isWriteable()) { - throw std::invalid_argument("This constructor may only be used for a sender process variable."); - } - if (!receiver) { - throw std::invalid_argument("The pointer to the receiver must not be null."); - } - if (!receiver->isReadable()) { - throw std::invalid_argument( - "The pointer to the receiver must point to an instance that is actually a receiver."); - } - // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; - // put future into the notification queue - _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); - } - catch(...) { - this->shutdown(); - throw; - } - } - -/*********************************************************************************************************************/ - - template - UnidirectionalProcessArray::~UnidirectionalProcessArray() { - // if this is a receiver, shutdown a potentially running readAsync - if(this->isReadable()) { - if(this->readAsyncThread.joinable()) { - this->readAsyncThread.interrupt(); - try { - _sharedState->_notificationPromise.set_value(0); - } - catch(boost::promise_already_satisfied) { - // ignore -> already notified - } - this->readAsyncThread.join(); - } - } - this->shutdown(); + timeStampSource), _sendNotificationListener(sendNotificationListener) + { + // It would be better to do the validation before initializing, but this + // would mean that we would have to initialize twice. + if (!this->isWriteable()) { + throw std::invalid_argument("This constructor may only be used for a sender process variable."); + } + if (!receiver) { + throw std::invalid_argument("The pointer to the receiver must not be null."); + } + if (!receiver->isReadable()) { + throw std::invalid_argument( + "The pointer to the receiver must point to an instance that is actually a receiver."); + } + // allocate and initialise buffer of the base class + mtca4u::NDRegisterAccessor::buffer_2D.resize(1); + mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; + // put future into the notification queue + _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); } /*********************************************************************************************************************/ @@ -616,7 +588,7 @@ namespace ChimeraTK { // If we already have a still-active future, we can return it directly. It is not possible that this future is // blocking and there is valid data to be read in the atomic triple buffer. // This is only a preformance optimisation, we would generate the same future again. - if(mtca4u::TransferElement::hasActiveFuture) return mtca4u::TransferElement::activeFuture; + if(mtca4u::TransferElement::hasActiveFuture) return activeFuture; if (!this->isReadable()) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); @@ -642,9 +614,9 @@ namespace ChimeraTK { } // return the future - mtca4u::TransferElement::activeFuture.reset(future, static_cast(this)); + activeFuture.reset(future, static_cast(this)); mtca4u::TransferElement::hasActiveFuture = true; - return mtca4u::TransferElement::activeFuture; + return activeFuture; } From f1762bff86cd79735710489520a4dd636b938dfb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 24 Nov 2017 15:19:09 +0100 Subject: [PATCH 299/786] necessary changes for the latest DeviceAccess update (readAsync() return type no longer a reference) --- .../BidirectionalProcessArray.h | 14 +- .../TypeChangingDecorator.h | 143 +++++------------- .../UnidirectionalProcessArray.h | 38 +++-- tests/src/testAsyncRead.cpp | 129 ++++++++-------- tests/src/testUnidirectionalProcessArray.cpp | 13 +- 5 files changed, 139 insertions(+), 198 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 7f94758..c6196e2 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -206,7 +206,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; - mtca4u::TransferFuture& readAsync() override; + mtca4u::TransferFuture readAsync() override; void postRead() override; @@ -244,6 +244,14 @@ namespace ChimeraTK { return _uniqueId; } + bool asyncTransferActive() override { + return _receiver->asyncTransferActive(); + } + + void clearAsyncTransferActive() override { + _receiver->clearAsyncTransferActive(); + } + private: /** @@ -387,8 +395,8 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - mtca4u::TransferFuture& BidirectionalProcessArray::readAsync() { - return _receiver->readAsync(); + mtca4u::TransferFuture BidirectionalProcessArray::readAsync() { + return {_receiver->readAsync(), this}; } /*********************************************************************************************************************/ diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 1fddd8f..c5da41d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -1,7 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H -#include +#include #include #include @@ -35,14 +35,10 @@ namespace ChimeraTK { //Everything from here are implementation details. The exact identity of the decorator should not matter to the user, it would break the abstraction. //=================================================================================================================================================== - // An intermediate base class which is only typed on the user type and has a function to - // provide the decorator type. It is needed to have something to cast to when the - // IMPL_T has already been abstracted away (the implementations are all templated to two parameters) - template - class DecoratorTypeAccessor : public mtca4u::NDRegisterAccessor{ - public: - using mtca4u::NDRegisterAccessor::NDRegisterAccessor; - virtual DecoratorType getDecoratorType() const = 0; + // Base class which just holds the decorator type. This allows + class DecoratorTypeHolder { + public: + virtual DecoratorType getDecoratorType() const = 0; }; /** @@ -50,110 +46,44 @@ namespace ChimeraTK { * different template type and accesses the original one as "backend" under the hood. * * This is the base class which has pure virtual convertAndCopyFromImpl and convertAndCopyToImpl functions. - * Apart from this it implements all decorating functions which just pass through to the impl. + * Apart from this it implements all decorating functions which just pass through to the target. * * This class is not thread-safe and should only be used from a single thread. * */ template - class TypeChangingDecorator : public DecoratorTypeAccessor { + class TypeChangingDecorator : public mtca4u::NDRegisterAccessorDecorator, public DecoratorTypeHolder { public: - TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept; - - bool isReadable() const override { - return _impl->isReadable(); - } - - bool isWriteable() const override { - return _impl->isWriteable(); - } - - bool isReadOnly() const override { - return _impl->isReadOnly(); - } - - mtca4u::TimeStamp getTimeStamp() const override { - return _impl->getTimeStamp(); - } - - ChimeraTK::VersionNumber getVersionNumber() const override { - return _impl->getVersionNumber(); - } - - void doReadTransfer() override{ - _impl->doReadTransfer(); - } - - bool doReadTransferNonBlocking() override{ - return _impl->doReadTransferNonBlocking(); - } - - bool doReadTransferLatest() override{ - return _impl->doReadTransferLatest(); - } - - ChimeraTK::TransferFuture& readAsync() override { - return _impl->readAsync(); - } + TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const &target) noexcept; virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; - void postRead() override{ - _impl->postRead(); + void postRead() override { + _target->postRead(); convertAndCopyFromImpl(); } - void preWrite() override{ + void preWrite() override { convertAndCopyToImpl(); - _impl->preWrite(); + _target->preWrite(); } - void postWrite() override{ - _impl->postWrite(); + void postWrite() override { } bool write(ChimeraTK::VersionNumber versionNumber={}) override { - // don't call preWrite() here. It would trigger the preWrite on the impl, which is also - // happening when _impl()->write() is called. + // don't call preWrite() here. It would trigger the preWrite on the target, which is also + // happening when _target()->write() is called. convertAndCopyToImpl(); - return _impl->write(versionNumber); - } - - bool isArray() const override { - return _impl->isArray(); - } - - /// @todo FIXME: can we unite stuff in a transfer group? Compare with impl? - bool isSameRegister(const boost::shared_ptr& e) const override { - return _impl->isSameRegister(e); - } - - std::vector > getHardwareAccessingElements() override { - return _impl->getHardwareAccessingElements(); - } - - void replaceTransferElement(boost::shared_ptr e) override { - _impl->replaceTransferElement(e); - } - - void setPersistentDataStorage(boost::shared_ptr storage) override{ - _impl->setPersistentDataStorage(storage); - } - - unsigned int getNInputQueueElements() const override { - return _impl->getNInputQueueElements(); - } - - mtca4u::FixedPointConverter getFixedPointConverter() const override { - return _impl->getFixedPointConverter(); + return _target->write(versionNumber); } protected: - boost::shared_ptr< mtca4u::NDRegisterAccessor > _impl; + using mtca4u::NDRegisterAccessorDecorator::_target; }; /** This class is intended as a base class. @@ -205,14 +135,14 @@ namespace ChimeraTK { virtual void convertAndCopyFromImpl(){ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = csa_helpers::stringToT(this->_impl->accessChannel(i)[j]); + this->buffer_2D[i][j] = csa_helpers::stringToT(this->_target->accessChannel(i)[j]); } } } virtual void convertAndCopyToImpl(){ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->_impl->accessChannel(i)[j] = csa_helpers::T_ToString(this->buffer_2D[i][j]); + this->_target->accessChannel(i)[j] = csa_helpers::T_ToString(this->buffer_2D[i][j]); } } } @@ -248,6 +178,7 @@ namespace ChimeraTK { } }; + using mtca4u::NDRegisterAccessorDecorator::_target; }; @@ -264,7 +195,11 @@ namespace ChimeraTK { DecoratorType getDecoratorType() const override{ return DecoratorType::range_checking; } - }; + + protected: + + using mtca4u::NDRegisterAccessorDecorator::_target; +}; /*********************************************************************************************************************/ @@ -272,14 +207,9 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const & impl) noexcept - : DecoratorTypeAccessor(impl->getName(), impl->getUnit(), impl->getDescription()), _impl(impl) - { - this->buffer_2D.resize(impl->getNumberOfChannels()); - for (auto & channel : this->buffer_2D){ - channel.resize(impl->getNumberOfSamples()); - } - } + TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const &target) noexcept + : mtca4u::NDRegisterAccessorDecorator(target) + {} /*********************************************************************************************************************/ @@ -291,7 +221,7 @@ namespace ChimeraTK { try{ for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = FromImplConverter::convert(this->_impl->accessChannel(i)[j]); + this->buffer_2D[i][j] = FromImplConverter::convert(_target->accessChannel(i)[j]); } } }catch(boost::numeric::positive_overflow &){ @@ -309,7 +239,7 @@ namespace ChimeraTK { boost::numeric::def_overflow_handler, csa_helpers::Round > ToImplConverter; for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->_impl->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); + _target->accessChannel(i)[j] = ToImplConverter::convert(this->buffer_2D[i][j]); } } } @@ -331,7 +261,7 @@ namespace ChimeraTK { //fixme: are iterartors more efficient? for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = this->_impl->accessChannel(i)[j]; + this->buffer_2D[i][j] = _target->accessChannel(i)[j]; } } } @@ -339,10 +269,13 @@ namespace ChimeraTK { void convertAndCopyToImpl() override { for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->_impl->accessChannel(i)[j] = this->buffer_2D[i][j]; + _target->accessChannel(i)[j] = this->buffer_2D[i][j]; } } } + + using mtca4u::NDRegisterAccessorDecorator::_target; + }; /** Partial template specialisation for strings as impl type. @@ -406,9 +339,11 @@ namespace ChimeraTK { if (decoratorMapEntry != getGlobalDecoratorMap().end()){ // There already is a decorator for this transfer element // The decorator has to have a matching type, otherwise we can only throw - auto castedType = boost::dynamic_pointer_cast< DecoratorTypeAccessor< UserType > >(decoratorMapEntry->second); + auto castedType = boost::dynamic_pointer_cast>(decoratorMapEntry->second); if (castedType){// User type matches, but the decorator type also has to match - if (castedType->getDecoratorType() == decoratorType){ + auto decoTypeHolder = boost::dynamic_pointer_cast(decoratorMapEntry->second); + assert(decoTypeHolder); + if (decoTypeHolder->getDecoratorType() == decoratorType){ // decorator matches, return it return castedType; }else{ diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 424ec73..a5430c1 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -90,7 +90,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; - mtca4u::TransferFuture& readAsync() override; + mtca4u::TransferFuture readAsync() override; void postRead() override; @@ -152,6 +152,14 @@ namespace ChimeraTK { return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state } + bool asyncTransferActive() override { + return hasActiveFuture; + } + + void clearAsyncTransferActive() override { + hasActiveFuture = false; + } + private: /** @@ -309,6 +317,11 @@ namespace ChimeraTK { */ mtca4u::TransferFuture activeFuture; + /** + * Flag whether acviteFuture is valid + */ + bool hasActiveFuture{false}; + /** * Variable ID for the persistent data storage */ @@ -531,13 +544,8 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferNonBlocking() { - - // Obtain future and check if a transfer is complete. - auto theFuture = readAsync().getBoostFuture(); - auto status = theFuture.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) return false; - return true; - + bool ret = readAsync().hasNewData(); + return ret; } /*********************************************************************************************************************/ @@ -554,7 +562,7 @@ namespace ChimeraTK { // discard data by moving the buffer to the empty buffer queue TransferFuture::Data *discardedBuffer = theFuture.get(); _sharedState->_fullBufferQueue.pop(); - mtca4u::TransferElement::hasActiveFuture = false; + hasActiveFuture = false; _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue theFuture = readAsync().getBoostFuture(); } @@ -583,12 +591,12 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - mtca4u::TransferFuture& UnidirectionalProcessArray::readAsync() { + mtca4u::TransferFuture UnidirectionalProcessArray::readAsync() { // If we already have a still-active future, we can return it directly. It is not possible that this future is // blocking and there is valid data to be read in the atomic triple buffer. // This is only a preformance optimisation, we would generate the same future again. - if(mtca4u::TransferElement::hasActiveFuture) return activeFuture; + if(hasActiveFuture) return activeFuture; if (!this->isReadable()) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); @@ -614,8 +622,8 @@ namespace ChimeraTK { } // return the future - activeFuture.reset(future, static_cast(this)); - mtca4u::TransferElement::hasActiveFuture = true; + activeFuture = TransferFuture(future, this); + hasActiveFuture = true; return activeFuture; } @@ -625,7 +633,9 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::postRead() { - mtca4u::TransferElement::hasActiveFuture = false; + // need to reset the hasActiveFuture flag ourselves, since we internally always use readAsync, but + // TransferFuture::wait() is not always called then. + hasActiveFuture = false; // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index b7f8461..ce24268 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -47,7 +47,7 @@ class AsyncReadTestSuite : public test_suite { test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/ [] ) { ChimeraTK::ExperimentalFeatures::enable(); - + framework::master_test_suite().p_name.value = "Async read test suite"; framework::master_test_suite().add(new AsyncReadTestSuite); @@ -66,66 +66,55 @@ void AsyncReadTest::testAsyncRead() { // obtain register accessor with integral type auto accessor = mtca4u::ScalarRegisterAccessor(receiver); auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); - + // simple reading through readAsync without actual need - TransferFuture *future; + TransferFuture future; senderAccessor = 5; senderAccessor.write(); - future = &(accessor.readAsync()); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); - future->wait(); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); + future = accessor.readAsync(); + future.wait(); BOOST_CHECK( accessor == 5 ); - + senderAccessor = 6; senderAccessor.write(); - future = &(accessor.readAsync()); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); - future->wait(); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); + future = accessor.readAsync(); + future.wait(); BOOST_CHECK( accessor == 6 ); // check that future's wait() function won't return before the read is complete for(int i=0; i<5; ++i) { senderAccessor = 42+i; - future = &(accessor.readAsync()); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); + future = accessor.readAsync(); std::atomic flag; flag = false; - std::thread thread([&future, &flag] { future->wait(); flag = true; }); + std::thread thread([&future, &flag] { future.wait(); flag = true; }); usleep(100000); BOOST_CHECK(flag == false); senderAccessor.write(); thread.join(); BOOST_CHECK( accessor == 42+i ); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); } - + // check that obtaining the same future multiple times works properly senderAccessor = 666; for(int i=0; i<5; ++i) { - future = &(accessor.readAsync()); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); + future = accessor.readAsync(); BOOST_CHECK( accessor == 46 ); // still the old value from the last test part } senderAccessor.write(); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); - future->wait(); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); + future.wait(); BOOST_CHECK( accessor == 666 ); - + // now try another asynchronous transfer senderAccessor = 999; - future = &(accessor.readAsync()); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); + future = accessor.readAsync(); std::atomic flag; flag = false; - std::thread thread([&future, &flag] { future->wait(); flag = true; }); + std::thread thread([&future, &flag] { future.wait(); flag = true; }); usleep(100000); BOOST_CHECK(flag == false); senderAccessor.write(); thread.join(); - BOOST_CHECK(&(future->getTransferElement()) == receiver.get()); BOOST_CHECK( accessor == 999 ); } @@ -166,11 +155,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s1.write(); thread.join(); @@ -182,11 +171,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s3.write(); thread.join(); @@ -198,11 +187,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s3 = 121; s3.write(); @@ -215,11 +204,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s2.write(); thread.join(); @@ -231,11 +220,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s4.write(); thread.join(); @@ -247,11 +236,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s4.write(); thread.join(); @@ -263,11 +252,11 @@ void AsyncReadTest::testReadAny() { // launch the readAny in a background thread std::atomic flag{false}; std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); - + // check that it doesn't return too soon usleep(100000); BOOST_CHECK(flag == false); - + // write register and check that readAny() completes s3 = 122; s3.write(); @@ -291,7 +280,7 @@ void AsyncReadTest::testReadAny() { BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); - + BOOST_CHECK(a1.readNonBlocking() == false); BOOST_CHECK(a2.readNonBlocking() == false); BOOST_CHECK(a3.readNonBlocking() == false); @@ -305,7 +294,7 @@ void AsyncReadTest::testReadAny() { s2 = 222; s2.write(); s3 = 333; - s3.write(); + s3.write(); s1 = 444; s1.write(); @@ -337,14 +326,14 @@ void AsyncReadTest::testReadAny() { BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 333); BOOST_CHECK(a4 == 111); - + BOOST_CHECK(a1.readNonBlocking() == false); BOOST_CHECK(a2.readNonBlocking() == false); BOOST_CHECK(a3.readNonBlocking() == false); BOOST_CHECK(a4.readNonBlocking() == false); } - + } /**********************************************************************************************************************/ @@ -359,16 +348,16 @@ void AsyncReadTest::testMixedRead() { // obtain register accessor with integral type auto accessor = mtca4u::ScalarRegisterAccessor(receiver); auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); - - TransferFuture *future; + + TransferFuture future; // plain readAsync and read mixed one after each other senderAccessor = 5; senderAccessor.write(); - future = &(accessor.readAsync()); - future->wait(); + future = accessor.readAsync(); + future.wait(); BOOST_CHECK( accessor == 5 ); - + senderAccessor = 6; senderAccessor.write(); accessor.read(); @@ -376,8 +365,8 @@ void AsyncReadTest::testMixedRead() { senderAccessor = 7; senderAccessor.write(); - future = &(accessor.readAsync()); - future->wait(); + future = accessor.readAsync(); + future.wait(); BOOST_CHECK( accessor == 7 ); senderAccessor = 8; @@ -385,10 +374,10 @@ void AsyncReadTest::testMixedRead() { accessor.read(); BOOST_CHECK( accessor == 8 ); - future = &(accessor.readAsync()); + future = accessor.readAsync(); senderAccessor = 9; senderAccessor.write(); - future->wait(); + future.wait(); BOOST_CHECK( accessor == 9 ); senderAccessor = 10; @@ -397,11 +386,11 @@ void AsyncReadTest::testMixedRead() { BOOST_CHECK( accessor == 10 ); BOOST_CHECK( accessor.readNonBlocking() == false ); - + // mixing with read() when future stays unfulfilled - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); - BOOST_CHECK(future->hasNewData() == false); + BOOST_CHECK(future.hasNewData() == false); senderAccessor = 11; senderAccessor.write(); @@ -417,10 +406,10 @@ void AsyncReadTest::testMixedRead() { BOOST_CHECK( accessor.readNonBlocking() == false ); // mixing with read() when future stays unfulfilled, different order - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); - BOOST_CHECK(future->hasNewData() == false); - + BOOST_CHECK(future.hasNewData() == false); + senderAccessor = 13; senderAccessor.write(); senderAccessor = 14; @@ -438,7 +427,7 @@ void AsyncReadTest::testMixedRead() { // readAsync with data already present, then discard the future senderAccessor = 15; senderAccessor.write(); - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); accessor.read(); BOOST_CHECK( accessor == 15 ); @@ -450,7 +439,7 @@ void AsyncReadTest::testMixedRead() { senderAccessor.write(); senderAccessor = 17; senderAccessor.write(); - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); accessor.read(); BOOST_CHECK( accessor == 16 ); @@ -460,10 +449,10 @@ void AsyncReadTest::testMixedRead() { BOOST_CHECK( accessor.readNonBlocking() == false ); // mixing with readNonBlocking() when future stays unfulfilled - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); - BOOST_CHECK(future->hasNewData() == false); - + BOOST_CHECK(future.hasNewData() == false); + senderAccessor = 18; senderAccessor.write(); senderAccessor = 19; @@ -479,12 +468,12 @@ void AsyncReadTest::testMixedRead() { BOOST_CHECK( accessor.readNonBlocking() == false ); // mixing with readNonBlocking() when future stays unfulfilled and a readNonBlocking() also is unsuccessfull - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); - BOOST_CHECK(future->hasNewData() == false); + BOOST_CHECK(future.hasNewData() == false); BOOST_CHECK( accessor.readNonBlocking() == false ); - + senderAccessor = 20; senderAccessor.write(); senderAccessor = 21; @@ -502,11 +491,11 @@ void AsyncReadTest::testMixedRead() { // readAsync with data already present, then discard the future - readNonBlocking() version senderAccessor = 22; senderAccessor.write(); - future = &(accessor.readAsync()); + future = accessor.readAsync(); usleep(10000); BOOST_CHECK( accessor.readNonBlocking() == true ); BOOST_CHECK( accessor == 22 ); BOOST_CHECK( accessor.readNonBlocking() == false ); - + } diff --git a/tests/src/testUnidirectionalProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp index 92b43dd..e736c5b 100644 --- a/tests/src/testUnidirectionalProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -164,7 +164,7 @@ namespace ChimeraTK { for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } - + } template @@ -242,7 +242,6 @@ namespace ChimeraTK { createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; - // If we send three values consecutively, they all should be received because the queue length is two and there is // the additional atomic triple buffer holding a third value sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER); @@ -388,7 +387,7 @@ namespace ChimeraTK { BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); BOOST_CHECK(receiver->accessChannel(0)[0] == 5); - + // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element // into the queue for(int i=0; i<10; ++i) { @@ -458,7 +457,7 @@ namespace ChimeraTK { sender->write(); BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 42); - + // readLatest with no element in the queue will return false and not change the value BOOST_CHECK(!receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 42); @@ -500,7 +499,7 @@ namespace ChimeraTK { BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 119); BOOST_CHECK(!(receiver->readNonBlocking())); - + // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element // into the queue - which should then be read by readLatest for(int i=0; i<10; ++i) { @@ -516,8 +515,8 @@ namespace ChimeraTK { BOOST_CHECK(receiver->readLatest()); BOOST_CHECK_EQUAL(receiver->accessData(0), 12); BOOST_CHECK(!(receiver->readLatest())); - - + + } } //namespace ChimeraTK From d2846daf2f336f8ff195e8a745e5e9d44c79d5cb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 27 Nov 2017 15:53:21 +0100 Subject: [PATCH 300/786] necessary update for latest DeviceAccess changes (write() -> doWriteTransfer()) --- .../ControlSystemAdapter/BidirectionalProcessArray.h | 6 +++--- .../ControlSystemAdapter/TypeChangingDecorator.h | 12 +++++------- .../UnidirectionalProcessArray.h | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index c6196e2..5c09390 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -210,7 +210,7 @@ namespace ChimeraTK { void postRead() override; - bool write(ChimeraTK::VersionNumber versionNumber={}) override; + bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; /** * Sends the current value to the receiver. Returns true if an @@ -430,7 +430,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - bool BidirectionalProcessArray::write( + bool BidirectionalProcessArray::doWriteTransfer( ChimeraTK::VersionNumber versionNumber) { // We have to copy our current value to the sender. We cannot swap it // because this would mean that we would lose the current value. @@ -489,7 +489,7 @@ namespace ChimeraTK { if (sendInitialValue) { mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); - write(); + doWriteTransfer(); } } diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index c5da41d..d99019a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -61,6 +61,10 @@ namespace ChimeraTK { virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; + void preRead() override { + _target->preRead(); + } + void postRead() override { _target->postRead(); convertAndCopyFromImpl(); @@ -72,13 +76,7 @@ namespace ChimeraTK { } void postWrite() override { - } - - bool write(ChimeraTK::VersionNumber versionNumber={}) override { - // don't call preWrite() here. It would trigger the preWrite on the target, which is also - // happening when _target()->write() is called. - convertAndCopyToImpl(); - return _target->write(versionNumber); + _target->postWrite(); } protected: diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index a5430c1..7c8c28b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -94,7 +94,7 @@ namespace ChimeraTK { void postRead() override; - bool write(ChimeraTK::VersionNumber versionNumber={}) override; + bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; /** * Sends the current value to the receiver. Returns true if an @@ -417,7 +417,7 @@ namespace ChimeraTK { * * If the maySendDestructively flag is true (it is * false by default), the {@link ProcessArray::writeDestructively()} method may - * be used to transfer values without copying but losing them on the sender + * be used to transfer values without copying but losing them on the senderdoWriteTransfer * side. * * The specified time-stamp source is used for determining the current time @@ -710,7 +710,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - bool UnidirectionalProcessArray::write( + bool UnidirectionalProcessArray::doWriteTransfer( ChimeraTK::VersionNumber versionNumber) { return writeInternal( _timeStampSource ? @@ -760,7 +760,7 @@ namespace ChimeraTK { mtca4u::NDRegisterAccessor::getNumberOfSamples()); if(sendInitialValue) { mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); - write(); + doWriteTransfer(); } } From a5a11e783d3b022f73858fb4a0bebd0dee14162e Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Fri, 8 Dec 2017 14:15:33 +0100 Subject: [PATCH 301/786] removed obsolete (and now impossible) TransferFutureDecorator, but left the tests in place to check that the postRead and preWrite are working --- .../TransferFutureDecorator.h | 49 ------------------- .../TypeChangingDecorator.h | 19 ------- tests/src/testTypeChangingDecorator.cpp | 2 +- 3 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h diff --git a/include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h deleted file mode 100644 index 7f01d87..0000000 --- a/include/ChimeraTK/ControlSystemAdapter/TransferFutureDecorator.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef CHIMERATK_TRANSFER_FUTURE_DECORATOR_H -#define CHIMERATK_TRANSFER_FUTURE_DECORATOR_H - -#include - -namespace mtca4u { - class TransferElement; - class TransferFutureIterator; -} - -namespace ChimeraTK { - template - class TypeChangingDecorator; - - template - class TransferFutureDecorator: public TransferFuture { - public: - - virtual void wait() override{ - _originalFuture->wait(); - _decorator->convertAndCopyFromImpl(); - _decorator->hasActiveFuture = false; - } - - TransferFutureDecorator() - : _originalFuture(nullptr), _decorator(nullptr) - {} - - /** Constructor accepting another transfer future. I think I don't need anything else.*/ - TransferFutureDecorator(TransferFuture * futureToBeDecorated, TypeChangingDecorator *decorator) - : _originalFuture(futureToBeDecorated), _decorator(decorator) - {} - - void reset(TransferFuture * futureToBeDecorated, TypeChangingDecorator *decorator){ - _originalFuture = futureToBeDecorated; - _decorator = decorator; - } - - protected: - // the decorated accessor will allways hold the instance of the future, so we just store a pointer - TransferFuture *_originalFuture; - - // The decorator will always hold the instance of this future, so we can use a plain pointer - TypeChangingDecorator *_decorator; - }; - -} /* namespace ChimeraTK */ - -#endif /* CHIMERATK_TRANSFER_FUTURE_DECORATOR_H */ diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index c327f45..d99019a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -4,7 +4,6 @@ #include #include #include -#include "TransferFutureDecorator.h" namespace ChimeraTK { /** There are three types of TypeChanging decorators which do different data conversions @@ -80,14 +79,9 @@ namespace ChimeraTK { _target->postWrite(); } - virtual TransferFuture& readAsync() override; - protected: using mtca4u::NDRegisterAccessorDecorator::_target; - TransferFutureDecorator _activeDecoratedFuture; - - friend TransferFutureDecorator; }; /** This class is intended as a base class. @@ -215,19 +209,6 @@ namespace ChimeraTK { : mtca4u::NDRegisterAccessorDecorator(target) {} - template - TransferFuture& TypeChangingDecorator::readAsync(){ - ChimeraTK::ExperimentalFeatures::check("asynchronous read"); - if(this->hasActiveFuture) return _activeDecoratedFuture; // the last future given out by this fuction is still active - //no active future. Call readAsync on the decorated transfer element, then decorate the - // future it returns - auto & originalFuture = _impl->readAsync(); - _activeDecoratedFuture.reset(&originalFuture, this); - this->hasActiveFuture=true; - - return _activeDecoratedFuture; - } - /*********************************************************************************************************************/ template diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index a4b5db7..fbf72da 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -107,7 +107,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue anotherScalarAccessor = startReadValue+2; anotherScalarAccessor.write(); - auto & future = decoratedScalar.readAsync(); + auto future = decoratedScalar.readAsync(); // nothing must change on the user buffer yet BOOST_CHECK( test_close(decoratedScalar.accessData(0), startWriteValue+1) ); future.wait(); // this calls the post-reads correctly From e7ec98de47a0e5d6ad2fb80a3b970162a6b8af98 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 14 Dec 2017 17:56:30 +0100 Subject: [PATCH 302/786] adapted new internal interface from DeviceAccess (postRead -> doPostRead etc.) --- .../BidirectionalProcessArray.h | 4 ++-- .../ControlSystemAdapter/ProcessArray.h | 15 +++++++----- .../TypeChangingDecorator.h | 24 +++++++++++++------ .../UnidirectionalProcessArray.h | 4 ++-- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 5c09390..94e2611 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -208,7 +208,7 @@ namespace ChimeraTK { mtca4u::TransferFuture readAsync() override; - void postRead() override; + void doPostRead() override; bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; @@ -402,7 +402,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - void BidirectionalProcessArray::postRead() { + void BidirectionalProcessArray::doPostRead() { _receiver->postRead(); // We only update the current value (stored in the sender) when the version // number of the data that we received is greater than the current version diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index d37a6f7..b0b6971 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -22,7 +22,7 @@ #include "PersistentDataStorage.h" namespace ChimeraTK { - + /** * Array version of the ProcessVariable. This class mainly exists for * historical reasons: Originally, there were different implementations for @@ -87,7 +87,7 @@ namespace ChimeraTK { bool isReadOnly() const override { return !isWriteable(); } - + /** * Sends the current value to the receiver. Returns true if an * empty buffer was available and false if no empty buffer was @@ -122,15 +122,18 @@ namespace ChimeraTK { return true; } - bool isSameRegister(const boost::shared_ptr& e) const override { - // only true if the very instance of the transfer element is the same - return e.get() == this; + bool mayReplaceOther(const boost::shared_ptr&) const override { + return false; // never true as we shall return false if instance is the same } std::vector > getHardwareAccessingElements() override { return { boost::enable_shared_from_this::shared_from_this() }; } - + + std::list> getInternalElements() override { + return {}; + } + void replaceTransferElement(boost::shared_ptr) override { // You can't replace anything here. Just do nothing. } diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index d99019a..6e2e16b 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -61,24 +61,33 @@ namespace ChimeraTK { virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; - void preRead() override { + void doPreRead() override { _target->preRead(); } - void postRead() override { + void doPostRead() override { _target->postRead(); convertAndCopyFromImpl(); } - void preWrite() override { + void doPreWrite() override { convertAndCopyToImpl(); _target->preWrite(); } - void postWrite() override { + void doPostWrite() override { _target->postWrite(); } + bool mayReplaceOther(const boost::shared_ptr &other) const override { + if(other.get() == this) return false; + auto casted = boost::dynamic_pointer_cast const>(other); + if(!casted) return false; + // the TypeChangingeDecorator implementations have no internal state and just depend on the template parameters, + // thus we don't need to check anything else here... + return true; + } + protected: using mtca4u::NDRegisterAccessorDecorator::_target; @@ -155,11 +164,12 @@ namespace ChimeraTK { class TypeChangingRangeCheckingDecorator: public TypeChangingStringImplDecorator{ public: using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; - virtual void convertAndCopyFromImpl(); - virtual void convertAndCopyToImpl(); + void convertAndCopyFromImpl() override; + void convertAndCopyToImpl() override; DecoratorType getDecoratorType() const override{ return DecoratorType::range_checking; } + private: /** Internal exceptions to overload the what() function of the boost exceptions in order to fill in the variable name. * These exceptions are not part of the external interface and cannot be caught explicitly because they are protected. @@ -339,7 +349,7 @@ namespace ChimeraTK { // The decorator has to have a matching type, otherwise we can only throw auto castedType = boost::dynamic_pointer_cast>(decoratorMapEntry->second); if (castedType){// User type matches, but the decorator type also has to match - auto decoTypeHolder = boost::dynamic_pointer_cast(decoratorMapEntry->second); + auto decoTypeHolder = boost::dynamic_pointer_cast(decoratorMapEntry->second); /// @todo eliminate this cast and change map to hold DecoratorTypeHolder assert(decoTypeHolder); if (decoTypeHolder->getDecoratorType() == decoratorType){ // decorator matches, return it diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 7c8c28b..b4a8a00 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -92,7 +92,7 @@ namespace ChimeraTK { mtca4u::TransferFuture readAsync() override; - void postRead() override; + void doPostRead() override; bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; @@ -631,7 +631,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - void UnidirectionalProcessArray::postRead() { + void UnidirectionalProcessArray::doPostRead() { // need to reset the hasActiveFuture flag ourselves, since we internally always use readAsync, but // TransferFuture::wait() is not always called then. From c41e211286cf693efd0d3779e7ce178ca0282aa8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 14 Dec 2017 17:57:17 +0100 Subject: [PATCH 303/786] added missing call to preRead and corrected the expected behaviour of mayReplaceOther() --- tests/src/testTypeChangingDecorator.cpp | 39 +++++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 6f53b6a..073dfce 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -24,7 +24,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto anotherImplTAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); - + auto ndAccessor = boost::dynamic_pointer_cast >(scalar.getHighLevelImplElement()); DECORATOR_TYPE decoratedScalar(ndAccessor); @@ -32,11 +32,11 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue BOOST_REQUIRE( decoratedScalar.getNumberOfSamples()==1); BOOST_CHECK( decoratedScalar.getName() == "/SOME/SCALAR" ); - + BOOST_CHECK( decoratedScalar.isReadable() ); BOOST_CHECK( decoratedScalar.isWriteable() ); BOOST_CHECK( !decoratedScalar.isReadOnly() ); - + anotherScalarAccessor = startReadValue; anotherScalarAccessor.write(); // check that the values are different at start so we know the test is sensitive @@ -53,15 +53,19 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue // repeat the read / write tests with all different functions // 1. The way a transfer group would call it: + /// @todo This is bad testing practise. It does not test whether the decorator works with a TransferGroup. This + /// becomes especially problematic when the behaviour of the TransferGroup is changed - in which case this test + /// does not notice the problem. Instead a real test with an actual TransferGroup should be implemented. anotherScalarAccessor = startReadValue+1; anotherScalarAccessor.write(); + decoratedScalar.preRead(); for (auto & hwAccessor : decoratedScalar.getHardwareAccessingElements()){ hwAccessor->read(); } // still nothing has changed on the user buffer - BOOST_CHECK( test_close(decoratedScalar.accessData(0), startWriteValue) ); + BOOST_CHECK( test_close(decoratedScalar.accessData(0), startWriteValue) ); decoratedScalar.postRead(); - BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue+1) ); + BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue+1) ); decoratedScalar.accessData(0) = startWriteValue + 1 ; decoratedScalar.preWrite(); @@ -81,7 +85,10 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue // implementation. Thus we intentionally do not call them to indicate them uncovered. // We would have to use the control system adapter implementations with the queues to test it. - BOOST_CHECK( decoratedScalar.isSameRegister( boost::dynamic_pointer_cast(anotherImplTAccessor.getHighLevelImplElement()) ) ); + BOOST_CHECK( !decoratedScalar.mayReplaceOther(anotherImplTAccessor.getHighLevelImplElement()) ); + auto anotherNdAccessor = boost::dynamic_pointer_cast >(anotherImplTAccessor.getHighLevelImplElement()); + auto anotherDecoratedScalar = boost::make_shared>(anotherNdAccessor); + BOOST_CHECK( decoratedScalar.mayReplaceOther(anotherDecoratedScalar) ); // OK, I give up. I would have to repeat all tests ever written for a decorator, incl. transfer group, persistentDataStorage and everything. All I can do is leave the stuff intentionally uncovered so a reviewer can find the places. } @@ -179,7 +186,7 @@ BOOST_AUTO_TEST_CASE( testLoops ){ loopTest(); loopTest(); } - + #define CHECK_THROW_PRINT( command , exception_type) \ try{ \ command;\ @@ -199,11 +206,11 @@ BOOST_AUTO_TEST_CASE( testRangeChecks ){ auto myIntDummy = d.getScalarRegisterAccessor("/SOME/INT"); // the second accessor for the test auto myUInt = d.getScalarRegisterAccessor("/SOME/UINT"); auto myUIntDummy = d.getScalarRegisterAccessor("/SOME/UINT"); - + auto intNDAccessor = boost::dynamic_pointer_cast >(myInt.getHighLevelImplElement()); TypeChangingRangeCheckingDecorator u2i(intNDAccessor); TypeChangingDirectCastDecorator directU2i(intNDAccessor); // don't try this at home: putting the same NDAccessor into different decorators can cause trouble - + auto uintNDAccessor = boost::dynamic_pointer_cast >(myUInt.getHighLevelImplElement()); TypeChangingRangeCheckingDecorator i2u(uintNDAccessor); TypeChangingDirectCastDecorator directI2u(uintNDAccessor); // don't try this at home: putting the same NDAccessor into different decorators can cause trouble @@ -213,14 +220,14 @@ BOOST_AUTO_TEST_CASE( testRangeChecks ){ myIntDummy.write(); myUIntDummy = 0xFFFFFFFF; myUIntDummy.write(); - + CHECK_THROW_PRINT( u2i.read(), boost::numeric::negative_overflow ); CHECK_THROW_PRINT( i2u.read(), boost::numeric::positive_overflow ); BOOST_CHECK_NO_THROW( directI2u.read() ); BOOST_CHECK_NO_THROW( directU2i.read() ); BOOST_CHECK( directU2i.accessData(0) == 0xFFFFFFFF ); BOOST_CHECK( directI2u.accessData(0) == -1 ); - + i2u.accessData(0) = 0xFFFFFFFE; u2i.accessData(0) = 0xFFFFFFFE; @@ -238,7 +245,7 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ wholeArray[0]=12345; wholeArray[1]=12346; wholeArray.write(); - + auto decorated0 = getDecorator(partial0); auto decorated1 = getDecorator(partial1); @@ -248,7 +255,7 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ group.read(); BOOST_CHECK( decorated0->accessData(0) == 12345 ); BOOST_CHECK( decorated1->accessData(0) == 12346 ); - + decorated0->accessData(0)=4321; decorated1->accessData(0)=4322; group.write(); @@ -277,15 +284,15 @@ BOOST_AUTO_TEST_CASE( testFactory ){ // but you can get the same decorator again if you ask for it auto sameDecorator = getDecorator(transferElement); BOOST_CHECK( sameDecorator.get() == decoratedScalar.get() ); - - // Test for direct convertion decorator type + + // Test for direct convertion decorator type // we have to use a different transfer element for this to work auto scalar2 = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto decoratedDirectConvertingScalar = getDecorator(scalar2, DecoratorType::C_style_conversion); BOOST_CHECK( decoratedDirectConvertingScalar ); auto castedDCScalar = boost::dynamic_pointer_cast< TypeChangingDirectCastDecorator< int, double> >( decoratedDirectConvertingScalar) ; BOOST_CHECK( castedDCScalar ); - + // fixme: at the moment we are throwing if a limiting decorator is requested auto scalar3 = d.getScalarRegisterAccessor("/SOME/SCALAR"); CHECK_THROW_PRINT( getDecorator(scalar3, DecoratorType::limiting), mtca4u::NotImplementedException ); From 0ba1b0cd64bbbf668cc0644ec52ced8667fc9fae Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 15 Dec 2017 10:40:02 +0100 Subject: [PATCH 304/786] changed some BOOST_CHECK into BOOST_CHECK_EQUAL for easier debugging --- tests/src/testUnidirectionalProcessArray.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/testUnidirectionalProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp index e736c5b..2f49f3e 100644 --- a/tests/src/testUnidirectionalProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -281,17 +281,17 @@ namespace ChimeraTK { BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 3); + BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 3); } BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 4); + BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 4); } BOOST_CHECK(receiver->readNonBlocking()); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 6); + BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 6); } // We have received all values, so no more values should be available. BOOST_CHECK(!receiver->readNonBlocking()); From 5a78203f4e5b1e00e6509cf4242393b2b5a6b6f8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 15 Dec 2017 10:40:24 +0100 Subject: [PATCH 305/786] added call to preRead() to make sure postRead() can be called at the end of the transaction --- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index b4a8a00..0e9fc28 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -602,6 +602,10 @@ namespace ChimeraTK { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } + // this is required to make sure postRead() can later run. + this->readTransactionInProgress = false; + this->preRead(); + // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this // function even if the future has not yet been used still result in the correct behaviour. From b34424a59530b71f4ddbfacebec8744880fbeafd Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 19 Dec 2017 14:04:39 +0100 Subject: [PATCH 306/786] needed update for the latest DeviceAccess changes --- .../BidirectionalProcessArray.h | 12 +- .../TypeChangingDecorator.h | 198 +++--- .../UnidirectionalProcessArray.h | 581 +++++++++--------- tests/src/testAsyncRead.cpp | 28 +- tests/src/testTypeChangingDecorator.cpp | 21 +- 5 files changed, 406 insertions(+), 434 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 94e2611..d4bab78 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -206,7 +206,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; - mtca4u::TransferFuture readAsync() override; + mtca4u::TransferFuture doReadTransferAsync() override; void doPostRead() override; @@ -244,14 +244,6 @@ namespace ChimeraTK { return _uniqueId; } - bool asyncTransferActive() override { - return _receiver->asyncTransferActive(); - } - - void clearAsyncTransferActive() override { - _receiver->clearAsyncTransferActive(); - } - private: /** @@ -395,7 +387,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - mtca4u::TransferFuture BidirectionalProcessArray::readAsync() { + mtca4u::TransferFuture BidirectionalProcessArray::doReadTransferAsync() { return {_receiver->readAsync(), this}; } diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 6e2e16b..6ecdb6a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -1,6 +1,7 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H +#include #include #include #include @@ -29,7 +30,14 @@ namespace ChimeraTK { * @param decoratorType The type of decorator you want (see description of DecoratorType) */ template - boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType = DecoratorType::range_checking ); + boost::shared_ptr> getDecorator(boost::shared_ptr &transferElement, + DecoratorType decoratorType = DecoratorType::range_checking); + + template + boost::shared_ptr> getDecorator(mtca4u::TransferElementAbstractor &transferElement, + DecoratorType decoratorType = DecoratorType::range_checking) { + return getDecorator(transferElement.getHighLevelImplElement(), decoratorType); + } //=================================================================================================================================================== //Everything from here are implementation details. The exact identity of the decorator should not matter to the user, it would break the abstraction. @@ -54,43 +62,43 @@ namespace ChimeraTK { template class TypeChangingDecorator : public mtca4u::NDRegisterAccessorDecorator, public DecoratorTypeHolder { - public: + public: - TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const &target) noexcept; + TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const &target) noexcept; - virtual void convertAndCopyFromImpl() = 0; - virtual void convertAndCopyToImpl() = 0; + virtual void convertAndCopyFromImpl() = 0; + virtual void convertAndCopyToImpl() = 0; - void doPreRead() override { - _target->preRead(); - } + void doPreRead() override { + _target->preRead(); + } - void doPostRead() override { - _target->postRead(); - convertAndCopyFromImpl(); - } + void doPostRead() override { + _target->postRead(); + convertAndCopyFromImpl(); + } - void doPreWrite() override { - convertAndCopyToImpl(); - _target->preWrite(); - } + void doPreWrite() override { + convertAndCopyToImpl(); + _target->preWrite(); + } - void doPostWrite() override { - _target->postWrite(); - } + void doPostWrite() override { + _target->postWrite(); + } - bool mayReplaceOther(const boost::shared_ptr &other) const override { - if(other.get() == this) return false; - auto casted = boost::dynamic_pointer_cast const>(other); - if(!casted) return false; - // the TypeChangingeDecorator implementations have no internal state and just depend on the template parameters, - // thus we don't need to check anything else here... - return true; - } + bool mayReplaceOther(const boost::shared_ptr &other) const override { + if(other.get() == this) return false; + auto casted = boost::dynamic_pointer_cast const>(other); + if(!casted) return false; + // the TypeChangingeDecorator implementations have no internal state and just depend on the template parameters, + // thus we don't need to check anything else here... + return true; + } - protected: + protected: - using mtca4u::NDRegisterAccessorDecorator::_target; + using mtca4u::NDRegisterAccessorDecorator::_target; }; /** This class is intended as a base class. @@ -104,7 +112,7 @@ namespace ChimeraTK { }; /// A sub-namespace in order not to expose the classes to the ChimeraTK namespace - namespace csa_helpers{ + namespace csa_helpers { template T stringToT(std::string const & input){ @@ -259,48 +267,48 @@ namespace ChimeraTK { */ template class TypeChangingDirectCastDecorator : public TypeChangingStringImplDecorator { - public: - using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; - DecoratorType getDecoratorType() const override{ - return DecoratorType::C_style_conversion; - } + public: + using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; + DecoratorType getDecoratorType() const override{ + return DecoratorType::C_style_conversion; + } - void convertAndCopyFromImpl() override { - //fixme: are iterartors more efficient? - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - this->buffer_2D[i][j] = _target->accessChannel(i)[j]; + void convertAndCopyFromImpl() override { + //fixme: are iterartors more efficient? + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + this->buffer_2D[i][j] = _target->accessChannel(i)[j]; + } } } - } - void convertAndCopyToImpl() override { - for (size_t i = 0; i < this->buffer_2D.size(); ++i){ - for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ - _target->accessChannel(i)[j] = this->buffer_2D[i][j]; + void convertAndCopyToImpl() override { + for (size_t i = 0; i < this->buffer_2D.size(); ++i){ + for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ + _target->accessChannel(i)[j] = this->buffer_2D[i][j]; + } } } - } - using mtca4u::NDRegisterAccessorDecorator::_target; + using mtca4u::NDRegisterAccessorDecorator::_target; }; /** Partial template specialisation for strings as impl type. */ template - class TypeChangingDirectCastDecorator: public TypeChangingStringImplDecorator{ - public: - using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; - virtual void convertAndCopyFromImpl(){ - TypeChangingStringImplDecorator::convertAndCopyFromImpl(); - } - virtual void convertAndCopyToImpl(){ - TypeChangingStringImplDecorator::convertAndCopyToImpl(); - } - DecoratorType getDecoratorType() const override{ - return DecoratorType::C_style_conversion; - } + class TypeChangingDirectCastDecorator : public TypeChangingStringImplDecorator { + public: + using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; + virtual void convertAndCopyFromImpl(){ + TypeChangingStringImplDecorator::convertAndCopyFromImpl(); + } + virtual void convertAndCopyToImpl(){ + TypeChangingStringImplDecorator::convertAndCopyToImpl(); + } + DecoratorType getDecoratorType() const override{ + return DecoratorType::C_style_conversion; + } }; /** Quasi singleton to have a unique, global map across UserType templated factories. @@ -311,64 +319,66 @@ namespace ChimeraTK { boost::shared_ptr > & getGlobalDecoratorMap(); template - class DecoratorFactory{ - public: - DecoratorFactory(boost::shared_ptr< mtca4u::TransferElement > theImpl_, DecoratorType wantedDecoratorType_) - : theImpl(theImpl_), wantedDecoratorType(wantedDecoratorType_){} - boost::shared_ptr< mtca4u::TransferElement > theImpl; - DecoratorType wantedDecoratorType; - mutable boost::shared_ptr > createdDecorator; - - template - void operator()(PAIR&) const { - typedef typename PAIR::first_type TargetImplType; - if(typeid(TargetImplType) != theImpl->getValueType() ) return; - - if (wantedDecoratorType == DecoratorType::range_checking){ - createdDecorator.reset( new TypeChangingRangeCheckingDecorator< UserType, TargetImplType> ( - boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); - }else if( wantedDecoratorType == DecoratorType::C_style_conversion){ - createdDecorator.reset( new TypeChangingDirectCastDecorator< UserType, TargetImplType> ( - boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); - }else{ - throw mtca4u::NotImplementedException("TypeChangingDecorator with range limitation is not implemented yet."); - } - } - + class DecoratorFactory { + public: + DecoratorFactory(boost::shared_ptr< mtca4u::TransferElement > theImpl_, DecoratorType wantedDecoratorType_) + : theImpl(theImpl_), wantedDecoratorType(wantedDecoratorType_){} + boost::shared_ptr< mtca4u::TransferElement > theImpl; + DecoratorType wantedDecoratorType; + mutable boost::shared_ptr > createdDecorator; + + template + void operator()(PAIR&) const { + typedef typename PAIR::first_type TargetImplType; + if(typeid(TargetImplType) != theImpl->getValueType() ) return; + + if (wantedDecoratorType == DecoratorType::range_checking){ + createdDecorator.reset( new TypeChangingRangeCheckingDecorator< UserType, TargetImplType> ( + boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); + }else if( wantedDecoratorType == DecoratorType::C_style_conversion){ + createdDecorator.reset( new TypeChangingDirectCastDecorator< UserType, TargetImplType> ( + boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); + }else{ + throw mtca4u::NotImplementedException("TypeChangingDecorator with range limitation is not implemented yet."); + } + } }; // the factory function. You don't have to care about the IMPL_Type when requesting a decorator. Implementation, declared at the top of this file template - boost::shared_ptr< mtca4u::NDRegisterAccessor< UserType > > getDecorator( mtca4u::TransferElement & transferElement, DecoratorType decoratorType){ + boost::shared_ptr> getDecorator(boost::shared_ptr transferElement, + DecoratorType decoratorType) { // check if there already is a decorator for the transfer element - auto decoratorMapEntry = getGlobalDecoratorMap().find( transferElement.getHighLevelImplElement() ); - if (decoratorMapEntry != getGlobalDecoratorMap().end()){ + auto decoratorMapEntry = getGlobalDecoratorMap().find(transferElement); + if (decoratorMapEntry != getGlobalDecoratorMap().end()) { // There already is a decorator for this transfer element // The decorator has to have a matching type, otherwise we can only throw auto castedType = boost::dynamic_pointer_cast>(decoratorMapEntry->second); - if (castedType){// User type matches, but the decorator type also has to match + if (castedType) { // User type matches, but the decorator type also has to match auto decoTypeHolder = boost::dynamic_pointer_cast(decoratorMapEntry->second); /// @todo eliminate this cast and change map to hold DecoratorTypeHolder assert(decoTypeHolder); - if (decoTypeHolder->getDecoratorType() == decoratorType){ + if (decoTypeHolder->getDecoratorType() == decoratorType) { // decorator matches, return it return castedType; - }else{ + } + else { // sorry there is a decorator, but it's the wrong user type throw std::logic_error("ChimeraTK::ControlSystemAdapter: Decorator for TransferElement " + - transferElement.getName() +" already exists as a different decorator type."); + transferElement->getName() +" already exists as a different decorator type."); } - }else{ + } + else { // sorry there is a decorator, but it's the wrong user type throw std::logic_error("ChimeraTK::ControlSystemAdapter: Decorator for TransferElement " + - transferElement.getName() +" already exists with a different user type."); + transferElement->getName() +" already exists with a different user type."); } } - DecoratorFactory< UserType > factory( transferElement.getHighLevelImplElement(), decoratorType ); + DecoratorFactory< UserType > factory(transferElement, decoratorType ); boost::fusion::for_each(mtca4u::userTypeMap(), factory); - getGlobalDecoratorMap()[transferElement.getHighLevelImplElement()]=factory.createdDecorator; + getGlobalDecoratorMap()[transferElement] = factory.createdDecorator; return factory.createdDecorator; } diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 0e9fc28..85bf254 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -33,310 +33,292 @@ namespace ChimeraTK { template class UnidirectionalProcessArray : public ProcessArray { - public: - - /** - * Type alias for a shared pointer to this type. - */ - typedef boost::shared_ptr SharedPtr; - - /** - * Creates a process array that acts as a receiver. A receiver is - * intended to work as a tandem with a sender and receives values that - * have been set to the sender. - * - * This constructor creates the buffers and queues that are needed for the - * send/receive process and are shared with the sender. - */ - UnidirectionalProcessArray( - typename ProcessArray::InstanceType instanceType, - const mtca4u::RegisterPath& name, const std::string &unit, - const std::string &description, const std::vector& initialValue, - std::size_t numberOfBuffers); - - /** - * Creates a process array that acts as a sender. A sender is intended - * intended to work as a tandem with a receiver and send set values to - * it. It uses the buffers and queues that have been created by the - * receiver. - * - * If the maySendDestructively flag is true, the - * {@link ProcessArray::writeDestructively()} method may be used to transfer - * values without copying but losing them on the sender side. - * - * The optional send-notification listener is notified every time the - * sender's ProcessArray::write() method is called. It can be - * used to queue a request for the receiver's - * readNonBlocking() method to be called. The process - * variable passed to the listener is the receiver and not the sender. - */ - UnidirectionalProcessArray( - typename ProcessArray::InstanceType instanceType, - bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener, - UnidirectionalProcessArray::SharedPtr receiver); - - TimeStamp getTimeStamp() const override { - return _currentIndex->timeStamp; - } - - ChimeraTK::VersionNumber getVersionNumber() const override { - return _currentIndex->_versionNumber; - } - - void doReadTransfer() override; - - bool doReadTransferNonBlocking() override; - - bool doReadTransferLatest() override; - - mtca4u::TransferFuture readAsync() override; - - void doPostRead() override; - - bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; - - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * The specified version number is passed to the receiver. If the receiver - * has a value with a version number greater than or equal to the specified - * version number, it silently discards this update. - * - * This version of the send operation moves the current value from the - * sender to the receiver without copying it. This means that after calling - * this method, the sender's value, time stamp, and version number are - * undefined. Therefore, this method must only be used if this process - * variable is not read (on the sender side) after sending it. - * - * The time-stamp of the sent value is retrieved from the time-stamp source. - * - * Throws an exception if this process variable is not a sender or if this - * process variable does not allow destructive sending. - */ - bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) override; /// @todo FIXME this function must be present in TransferElement already! - - /** - * Sends the current value to the receiver. Returns true if an - * empty buffer was available and false if no empty buffer was - * available and thus a previously sent value has been dropped in order to - * send the current value. - * - * The specified version number is passed to the receiver. If the receiver - * has a value with a version number greater than or equal to the specified - * version number, it silently discards this update. - * - * This version of the send operation moves the current value from the - * sender to the receiver without copying it. This means that after calling - * this method, the sender's value, time stamp, and version number are - * undefined. Therefore, this method must only be used if this process - * variable is not read (on the sender side) after sending it. - * - * The time-stamp source of this process array is ignored and the passed - * time stamp is used instead. - * - * Throws an exception if this process variable is not a sender or if this - * process variable does not allow destructive sending. - */ - bool writeDestructively(ChimeraTK::TimeStamp timeStamp, ChimeraTK::VersionNumber versionNumber); - - void setPersistentDataStorage(boost::shared_ptr storage) override ; - - /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the - * same variable but different for any other process variable within the same process. The unique ID will not be - * persistent accross executions of the process. */ - size_t getUniqueId() const override { - return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state - } - - bool asyncTransferActive() override { - return hasActiveFuture; - } - - void clearAsyncTransferActive() override { - hasActiveFuture = false; - } - - private: - - /** - * Type for the individual buffers. Each buffer stores a vector (wrapped in a Boost scoped pointer) and a time - * stamp. It extends the TransferFuture::Data type (which provides the version number) so it can be transported - * by a TransferFuture. - */ - struct Buffer : public TransferFuture::Data { + public: /** - * Default constructor. Has to be defined explicitly because we delete our copy constructor. - */ - Buffer() - : TransferFuture::Data({}) - {} + * Type alias for a shared pointer to this type. + */ + typedef boost::shared_ptr SharedPtr; + + /** + * Creates a process array that acts as a receiver. A receiver is + * intended to work as a tandem with a sender and receives values that + * have been set to the sender. + * + * This constructor creates the buffers and queues that are needed for the + * send/receive process and are shared with the sender. + */ + UnidirectionalProcessArray( + typename ProcessArray::InstanceType instanceType, + const mtca4u::RegisterPath& name, const std::string &unit, + const std::string &description, const std::vector& initialValue, + std::size_t numberOfBuffers); + + /** + * Creates a process array that acts as a sender. A sender is intended + * intended to work as a tandem with a receiver and send set values to + * it. It uses the buffers and queues that have been created by the + * receiver. + * + * If the maySendDestructively flag is true, the + * {@link ProcessArray::writeDestructively()} method may be used to transfer + * values without copying but losing them on the sender side. + * + * The optional send-notification listener is notified every time the + * sender's ProcessArray::write() method is called. It can be + * used to queue a request for the receiver's + * readNonBlocking() method to be called. The process + * variable passed to the listener is the receiver and not the sender. + */ + UnidirectionalProcessArray( + typename ProcessArray::InstanceType instanceType, + bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener, + UnidirectionalProcessArray::SharedPtr receiver); + + TimeStamp getTimeStamp() const override { + return _currentIndex->timeStamp; + } + + ChimeraTK::VersionNumber getVersionNumber() const override { + return _currentIndex->_versionNumber; + } + + void doReadTransfer() override; + + bool doReadTransferNonBlocking() override; + + bool doReadTransferLatest() override; + + mtca4u::TransferFuture doReadTransferAsync() override; + + void doPostRead() override; + + bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * The time-stamp of the sent value is retrieved from the time-stamp source. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool writeDestructively(ChimeraTK::VersionNumber versionNumber={}) override; /// @todo FIXME this function must be present in TransferElement already! + + /** + * Sends the current value to the receiver. Returns true if an + * empty buffer was available and false if no empty buffer was + * available and thus a previously sent value has been dropped in order to + * send the current value. + * + * The specified version number is passed to the receiver. If the receiver + * has a value with a version number greater than or equal to the specified + * version number, it silently discards this update. + * + * This version of the send operation moves the current value from the + * sender to the receiver without copying it. This means that after calling + * this method, the sender's value, time stamp, and version number are + * undefined. Therefore, this method must only be used if this process + * variable is not read (on the sender side) after sending it. + * + * The time-stamp source of this process array is ignored and the passed + * time stamp is used instead. + * + * Throws an exception if this process variable is not a sender or if this + * process variable does not allow destructive sending. + */ + bool writeDestructively(ChimeraTK::TimeStamp timeStamp, ChimeraTK::VersionNumber versionNumber); + + void setPersistentDataStorage(boost::shared_ptr storage) override ; + + /** Return a unique ID of this process variable, which will be indentical for the receiver and sender side of the + * same variable but different for any other process variable within the same process. The unique ID will not be + * persistent accross executions of the process. */ + size_t getUniqueId() const override { + return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state + } + + private: /** - * Delete all copy and move constructors and assignment operators. In C++11, elements of a std::vector no longer - * have to be copy-constructable if the size of the vector is fixed and specified in the constructor. - */ - Buffer(const Buffer &other) = delete; - Buffer& operator=(const Buffer &other) = delete; - Buffer(Buffer &&other) = delete; - Buffer& operator=(Buffer &&other) = delete; - - /** The actual data contained in this buffer. */ - std::vector value; - - /** Flag whether this buffer currently contains valid data. This flag is used only for the atomic triple buffer - * and not set for buffers on the queues (since the queue the buffer is in already tells whether it contains - * valid data or not). */ - bool _isValid{false}; + * Type for the individual buffers. Each buffer stores a vector (wrapped in a Boost scoped pointer) and a time + * stamp. It extends the TransferFuture::Data type (which provides the version number) so it can be transported + * by a TransferFuture. + */ + struct Buffer : public TransferFuture::Data { + + /** + * Default constructor. Has to be defined explicitly because we delete our copy constructor. + */ + Buffer() + : TransferFuture::Data({}) + {} + + /** + * Delete all copy and move constructors and assignment operators. In C++11, elements of a std::vector no longer + * have to be copy-constructable if the size of the vector is fixed and specified in the constructor. + */ + Buffer(const Buffer &other) = delete; + Buffer& operator=(const Buffer &other) = delete; + Buffer(Buffer &&other) = delete; + Buffer& operator=(Buffer &&other) = delete; + + /** The actual data contained in this buffer. */ + std::vector value; + + /** Flag whether this buffer currently contains valid data. This flag is used only for the atomic triple buffer + * and not set for buffers on the queues (since the queue the buffer is in already tells whether it contains + * valid data or not). */ + bool _isValid{false}; + + /** + * Extra version number used to sort the values within the process variable (so this version is not globally + * valid). This number is needed in addition to the globally valid _versionNumber defined in + * TransferFuture::Data, because the globally valid version numebr can be controlled by the application and + * values with an older version number might be written to this process variable later. That later data with + * the older version number shall still be considered as last data e.g. in the context of readLatest(), i.e. + * whatever has been written to the process variable last should be read with readLatest(), even if it has + * the older version number. + */ + uint64_t _internalVersionNumber; + + /** Time stamp associated with this buffer */ + TimeStamp timeStamp; + + }; /** - * Extra version number used to sort the values within the process variable (so this version is not globally - * valid). This number is needed in addition to the globally valid _versionNumber defined in - * TransferFuture::Data, because the globally valid version numebr can be controlled by the application and - * values with an older version number might be written to this process variable later. That later data with - * the older version number shall still be considered as last data e.g. in the context of readLatest(), i.e. - * whatever has been written to the process variable last should be read with readLatest(), even if it has - * the older version number. - */ - uint64_t _internalVersionNumber; - - /** Time stamp associated with this buffer */ - TimeStamp timeStamp; - - }; - - /** - * Number of elements that each vector (and thus this array) has. - */ - std::size_t _vectorSize; - - /** - * Flag indicating whether the {@code sendDestructively} methods may be used - * on this process array. - */ - bool _maySendDestructively; - - /** - * Last internal version number written. See Buffer::_internalVersionNumber for more details. - */ - uint64_t _lastInternalVersionNumber{0}; - - /** - * The state shared between the sender and the receiver - */ - struct SharedState { - - SharedState(size_t numberOfBuffers) - : _buffers(numberOfBuffers + 2 + 3), // two additional buffers owned by sender and receiver, 3 more additional - // buffers for the atomic triple buffer - _tripleBufferIndex(&(_buffers[3])), - _fullBufferQueue(numberOfBuffers+1), - _emptyBufferQueue(numberOfBuffers) - {} + * Number of elements that each vector (and thus this array) has. + */ + std::size_t _vectorSize; /** - * Buffers that hold the actual values. + * Flag indicating whether the {@code sendDestructively} methods may be used + * on this process array. */ - std::vector _buffers; + bool _maySendDestructively; /** - * Special atomic triple buffer to be filled when the queue runs over - * (i.e. all normal buffers are full). After construction, buffer 2 is - * owned by the sender, 3 by the shared state and 4 by the receiver. A - * buffer is considered containing valid data if its _isValid flag is - * set. The buffer's default constructor sets _isValid to false. - */ - std::atomic _tripleBufferIndex; + * Last internal version number written. See Buffer::_internalVersionNumber for more details. + */ + uint64_t _lastInternalVersionNumber{0}; + + /** + * The state shared between the sender and the receiver + */ + struct SharedState { + + SharedState(size_t numberOfBuffers) + : _buffers(numberOfBuffers + 2 + 3), // two additional buffers owned by sender and receiver, 3 more additional + // buffers for the atomic triple buffer + _tripleBufferIndex(&(_buffers[3])), + _fullBufferQueue(numberOfBuffers+1), + _emptyBufferQueue(numberOfBuffers) + {} + + /** + * Buffers that hold the actual values. + */ + std::vector _buffers; + + /** + * Special atomic triple buffer to be filled when the queue runs over + * (i.e. all normal buffers are full). After construction, buffer 2 is + * owned by the sender, 3 by the shared state and 4 by the receiver. A + * buffer is considered containing valid data if its _isValid flag is + * set. The buffer's default constructor sets _isValid to false. + */ + std::atomic _tripleBufferIndex; + + /** + * Queue holding the indices of the full buffers. Those are the buffers + * that have been sent but not yet received. We can use an spsc_queue for + * this queue because it is only filled by the sending process array and + * only consumed by the receiving process array. In fact, we have to use + * an spsc_queue because the standard boost::lockfree::queue cannot hold + * futures (as they are not trivially assignable / destructable). + */ + boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; + + /** + * Queue holding the empty buffers. Those are the buffers that have been + * returned to the sender by the receiver. + */ + boost::lockfree::spsc_queue _emptyBufferQueue; + + /** + * The promise corresponding to the unfulfilled future in the _fullBufferQueue. + * This promise is basically only used by the sender. + */ + TransferFuture::PromiseType _notificationPromise; + + }; + boost::shared_ptr _sharedState; + + /** + * Index into the _buffers array that is currently owned by this instance + * and used for read and (possibly) write operations. + */ + Buffer *_currentIndex; + + /** + * Index into the _buffers array that is currently owned by this instance. + * The _tripleBufferIndex is different from the _currentIndex because it is + * only used as a fallback when a value cannot be transferred through the + * _fullBufferQueue because the _emptyBufferQueue is empty. + */ + Buffer *_tripleBufferIndex; /** - * Queue holding the indices of the full buffers. Those are the buffers - * that have been sent but not yet received. We can use an spsc_queue for - * this queue because it is only filled by the sending process array and - * only consumed by the receiving process array. In fact, we have to use - * an spsc_queue because the standard boost::lockfree::queue cannot hold - * futures (as they are not trivially assignable / destructable). + * Pointer to the receiver associated with this sender. This field is only + * used if this process variable represents a sender. */ - boost::lockfree::spsc_queue< TransferFuture::PlainFutureType > _fullBufferQueue; + boost::shared_ptr _receiver; /** - * Queue holding the empty buffers. Those are the buffers that have been - * returned to the sender by the receiver. + * Time-stamp source used to update the time-stamp when sending a value. */ - boost::lockfree::spsc_queue _emptyBufferQueue; + boost::shared_ptr _timeStampSource; /** - * The promise corresponding to the unfulfilled future in the _fullBufferQueue. - * This promise is basically only used by the sender. + * Listener that is notified when the process variable is sent. */ - TransferFuture::PromiseType _notificationPromise; - - }; - boost::shared_ptr _sharedState; - - /** - * Index into the _buffers array that is currently owned by this instance - * and used for read and (possibly) write operations. - */ - Buffer *_currentIndex; - - /** - * Index into the _buffers array that is currently owned by this instance. - * The _tripleBufferIndex is different from the _currentIndex because it is - * only used as a fallback when a value cannot be transferred through the - * _fullBufferQueue because the _emptyBufferQueue is empty. - */ - Buffer *_tripleBufferIndex; - - /** - * Pointer to the receiver associated with this sender. This field is only - * used if this process variable represents a sender. - */ - boost::shared_ptr _receiver; - - /** - * Time-stamp source used to update the time-stamp when sending a value. - */ - boost::shared_ptr _timeStampSource; - - /** - * Listener that is notified when the process variable is sent. - */ - boost::shared_ptr _sendNotificationListener; - - /** - * Persistent data storage which needs to be informed when the process variable is sent. - */ - boost::shared_ptr _persistentDataStorage; - - /** - * The last future returned by readAsync - */ - mtca4u::TransferFuture activeFuture; - - /** - * Flag whether acviteFuture is valid - */ - bool hasActiveFuture{false}; - - /** - * Variable ID for the persistent data storage - */ - size_t _persistentDataStorageID{0}; - - /** - * Internal implementation of the various {@code send} methods. All these - * methods basically do the same and only differ in whether the data in the - * sent array is copied to the buffer used as the new current buffer and - * which version number is used. - * - * The return value will be true, if older data was overwritten during the send operation, or otherwise false. - */ - bool writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, - bool shouldCopy); + boost::shared_ptr _sendNotificationListener; + + /** + * Persistent data storage which needs to be informed when the process variable is sent. + */ + boost::shared_ptr _persistentDataStorage; + + /** + * Variable ID for the persistent data storage + */ + size_t _persistentDataStorageID{0}; + + /** + * Internal implementation of the various {@code send} methods. All these + * methods basically do the same and only differ in whether the data in the + * sent array is copied to the buffer used as the new current buffer and + * which version number is used. + * + * The return value will be true, if older data was overwritten during the send operation, or otherwise false. + */ + bool writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, + bool shouldCopy); }; @@ -459,6 +441,7 @@ namespace ChimeraTK { &(_sharedState->_buffers[0])), _tripleBufferIndex( &(_sharedState->_buffers[4])) { + mtca4u::ExperimentalFeatures::enable(); // allocate and initialise buffer of the base class mtca4u::NDRegisterAccessor::buffer_2D.resize(1); mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; @@ -509,6 +492,7 @@ namespace ChimeraTK { &(_sharedState->_buffers[2])), _receiver(receiver), _timeStampSource( timeStampSource), _sendNotificationListener(sendNotificationListener) { + mtca4u::ExperimentalFeatures::enable(); // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (!this->isWriteable()) { @@ -535,7 +519,7 @@ namespace ChimeraTK { // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call // TransferFuture::wait(). - readAsync().getBoostFuture().wait(); + mtca4u::TransferElement::readAsync().getBoostFuture().wait(); boost::this_thread::interruption_point(); } @@ -544,7 +528,7 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferNonBlocking() { - bool ret = readAsync().hasNewData(); + bool ret = mtca4u::TransferElement::readAsync().hasNewData(); return ret; } @@ -556,15 +540,15 @@ namespace ChimeraTK { // As long as there is more than one valid element on the queue, discard it. // Due to our implementation there is always one unfulfilled future in the queue, so // we must pop until there are two elements left in order not to flush out the newest valid value. - auto theFuture = readAsync().getBoostFuture(); + auto theFuture = mtca4u::TransferElement::readAsync().getBoostFuture(); while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout && _sharedState->_fullBufferQueue.read_available() > 2 ) { // discard data by moving the buffer to the empty buffer queue TransferFuture::Data *discardedBuffer = theFuture.get(); _sharedState->_fullBufferQueue.pop(); - hasActiveFuture = false; + mtca4u::TransferElement::hasActiveFuture = false; _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue - theFuture = readAsync().getBoostFuture(); + theFuture = mtca4u::TransferElement::readAsync().getBoostFuture(); } // Check whether data is present in the atomic triple buffer. If it is newer, we also need to discard the last // valid element in the queue. If it is older, we need to discard the data in the triple buffer. @@ -591,21 +575,12 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - mtca4u::TransferFuture UnidirectionalProcessArray::readAsync() { - - // If we already have a still-active future, we can return it directly. It is not possible that this future is - // blocking and there is valid data to be read in the atomic triple buffer. - // This is only a preformance optimisation, we would generate the same future again. - if(hasActiveFuture) return activeFuture; + mtca4u::TransferFuture UnidirectionalProcessArray::doReadTransferAsync() { if (!this->isReadable()) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } - // this is required to make sure postRead() can later run. - this->readTransactionInProgress = false; - this->preRead(); - // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this // function even if the future has not yet been used still result in the correct behaviour. @@ -626,9 +601,7 @@ namespace ChimeraTK { } // return the future - activeFuture = TransferFuture(future, this); - hasActiveFuture = true; - return activeFuture; + return TransferFuture(future, this); } @@ -637,10 +610,6 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::doPostRead() { - // need to reset the hasActiveFuture flag ourselves, since we internally always use readAsync, but - // TransferFuture::wait() is not always called then. - hasActiveFuture = false; - // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index ce24268..50563bf 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -22,7 +22,7 @@ class AsyncReadTest { /// test normal asychronous read void testAsyncRead(); - /// test the TransferElement::readAny() function on ProcessArrays + /// test the readAny() function on ProcessArrays void testReadAny(); /// test mixing asynchronous with synchronous reads @@ -154,7 +154,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -170,7 +170,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -186,7 +186,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -203,7 +203,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -219,7 +219,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -235,7 +235,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -251,7 +251,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { TransferElement::readAny({a1,a2,a3,a4}); flag = true; }); + std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -271,12 +271,12 @@ void AsyncReadTest::testReadAny() { s2 = 777; s2.write(); // no point to use a thread here - auto r = TransferElement::readAny({a1,a2,a3,a4}); + auto r = readAny({a1,a2,a3,a4}); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 123); - r = TransferElement::readAny({a1,a2,a3,a4}); + r = readAny({a1,a2,a3,a4}); BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); @@ -299,28 +299,28 @@ void AsyncReadTest::testReadAny() { s1.write(); // no point to use a thread here - auto r = TransferElement::readAny({a1,a2,a3,a4}); + auto r = readAny({a1,a2,a3,a4}); BOOST_CHECK(a4.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); - r = TransferElement::readAny({a1,a2,a3,a4}); + r = readAny({a1,a2,a3,a4}); BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); - r = TransferElement::readAny({a1,a2,a3,a4}); + r = readAny({a1,a2,a3,a4}); BOOST_CHECK(a3.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 333); BOOST_CHECK(a4 == 111); - r = TransferElement::readAny({a1,a2,a3,a4}); + r = readAny({a1,a2,a3,a4}); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 444); BOOST_CHECK(a2 == 222); diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 073dfce..f0748b3 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -8,6 +8,7 @@ using namespace ChimeraTK; #include #include +#include // always test close for floating point values. Don't rely on exact binary representations bool test_close(double a, double b, double tolerance = 0.0001){ @@ -246,30 +247,30 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ wholeArray[1]=12346; wholeArray.write(); - auto decorated0 = getDecorator(partial0); - auto decorated1 = getDecorator(partial1); + mtca4u::ScalarRegisterAccessor decorated0(getDecorator(partial0)); + mtca4u::ScalarRegisterAccessor decorated1(getDecorator(partial1)); TransferGroup group; - group.addAccessor(*decorated0); - group.addAccessor(*decorated1); + group.addAccessor(decorated0); + group.addAccessor(decorated1); group.read(); - BOOST_CHECK( decorated0->accessData(0) == 12345 ); - BOOST_CHECK( decorated1->accessData(0) == 12346 ); + BOOST_CHECK( decorated0 == 12345 ); + BOOST_CHECK( decorated1 == 12346 ); - decorated0->accessData(0)=4321; - decorated1->accessData(0)=4322; +/* decorated0 = 4321; // test disabled since TransferGroup is read-only. Reason: overlapping registers. + decorated1 = 4322; group.write(); wholeArray.read(); BOOST_CHECK( test_close( wholeArray[0], 4321) ); - BOOST_CHECK( test_close( wholeArray[1], 4322) ); + BOOST_CHECK( test_close( wholeArray[1], 4322) ); */ } BOOST_AUTO_TEST_CASE( testFactory ){ mtca4u::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); - mtca4u::TransferElement & transferElement = scalar; + mtca4u::TransferElementAbstractor & transferElement = scalar; auto decoratedScalar = getDecorator(transferElement); BOOST_CHECK( decoratedScalar ); From ae8d1a521040bb530fa0ecfbae10037519ec874b Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 19 Dec 2017 15:45:54 +0100 Subject: [PATCH 307/786] expect a const reference in getDecorator() instead of a non-const reference, to allow passing rhs references --- .../ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 6ecdb6a..3c99f24 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -30,7 +30,7 @@ namespace ChimeraTK { * @param decoratorType The type of decorator you want (see description of DecoratorType) */ template - boost::shared_ptr> getDecorator(boost::shared_ptr &transferElement, + boost::shared_ptr> getDecorator(const boost::shared_ptr &transferElement, DecoratorType decoratorType = DecoratorType::range_checking); template @@ -347,7 +347,7 @@ namespace ChimeraTK { // the factory function. You don't have to care about the IMPL_Type when requesting a decorator. Implementation, declared at the top of this file template - boost::shared_ptr> getDecorator(boost::shared_ptr transferElement, + boost::shared_ptr> getDecorator(const boost::shared_ptr &transferElement, DecoratorType decoratorType) { // check if there already is a decorator for the transfer element From cf5edb0b0a2afd745efbcd9d5262156363e6c345 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 21 Dec 2017 16:04:39 +0100 Subject: [PATCH 308/786] fixed mayReplaceOther() of the TypeChangingDecorator, which was leading to wrong behaviour inside a TransferGroup --- .../ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h | 5 +---- tests/src/testTypeChangingDecorator.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 3c99f24..109adf0 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -88,12 +88,9 @@ namespace ChimeraTK { } bool mayReplaceOther(const boost::shared_ptr &other) const override { - if(other.get() == this) return false; auto casted = boost::dynamic_pointer_cast const>(other); if(!casted) return false; - // the TypeChangingeDecorator implementations have no internal state and just depend on the template parameters, - // thus we don't need to check anything else here... - return true; + return _target->mayReplaceOther(casted->_target); } protected: diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index f0748b3..aa74bed 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -88,6 +88,7 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue BOOST_CHECK( !decoratedScalar.mayReplaceOther(anotherImplTAccessor.getHighLevelImplElement()) ); auto anotherNdAccessor = boost::dynamic_pointer_cast >(anotherImplTAccessor.getHighLevelImplElement()); + BOOST_CHECK(anotherNdAccessor->mayReplaceOther(ndAccessor)); // unrelated check just to make sure the test is doing the right thing... auto anotherDecoratedScalar = boost::make_shared>(anotherNdAccessor); BOOST_CHECK( decoratedScalar.mayReplaceOther(anotherDecoratedScalar) ); @@ -253,17 +254,18 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ TransferGroup group; group.addAccessor(decorated0); group.addAccessor(decorated1); + group.read(); BOOST_CHECK( decorated0 == 12345 ); BOOST_CHECK( decorated1 == 12346 ); -/* decorated0 = 4321; // test disabled since TransferGroup is read-only. Reason: overlapping registers. + decorated0 = 4321; decorated1 = 4322; group.write(); wholeArray.read(); BOOST_CHECK( test_close( wholeArray[0], 4321) ); - BOOST_CHECK( test_close( wholeArray[1], 4322) ); */ + BOOST_CHECK( test_close( wholeArray[1], 4322) ); } BOOST_AUTO_TEST_CASE( testFactory ){ From b82ad203e4763cebe03ef603d38d27abe9ec146f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 8 Jan 2018 12:43:45 +0100 Subject: [PATCH 309/786] increased minor version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 820e368..2ebe62b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 10) +set(${PROJECT_NAME}_MINOR_VERSION 11) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) From 32c5a62a7cd0faa3ba677d59518d5104788ad4cf Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 8 Jan 2018 14:25:57 +0100 Subject: [PATCH 310/786] updated versions of dependencies --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ebe62b..ef22e4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.28 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.29 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) From 2d8b472d5705178157a97e269d4cd359dd8513e0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Feb 2018 18:29:12 +0100 Subject: [PATCH 311/786] Fixed (usually uncaught) exceptions when reading the persistency file if the variable household changed. Added tests for different scenarios. --- CMakeLists.txt | 9 +- .../PersistentDataStorage.h | 48 ++--- .../UnidirectionalProcessArray.h | 3 + tests/changedType.persist | 56 ++++++ tests/changedVectorSize.persist | 54 ++++++ tests/renamedVariable.persist | 56 ++++++ tests/src/testPersistentDataStorage.cc | 181 +++++++++++++++--- 7 files changed, 352 insertions(+), 55 deletions(-) create mode 100644 tests/changedType.persist create mode 100644 tests/changedVectorSize.persist create mode 100644 tests/renamedVariable.persist diff --git a/CMakeLists.txt b/CMakeLists.txt index ef22e4f..087d8f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ if(TESTING_IS_ENABLED) get_filename_component(executableName ${testSourceFile} NAME_WE) add_executable(${executableName} ${testSourceFile} ${testAuxSources}) target_include_directories(${executableName} PRIVATE ${CMAKE_SOURCE_DIR}/tests/include/ChimeraTK/ControlSystemAdapter/Testing) - #The ReferenceTestApplication is written to work with the control system adapter as installed, so + #The ReferenceTestApplication is written to work with the control system adapter as installed, so #we add that path as system just for the tests. target_include_directories(${executableName} SYSTEM BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/include) target_link_libraries(${executableName} ${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} pthread) @@ -90,13 +90,16 @@ if(TESTING_IS_ENABLED) ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") FILE(COPY tests/decoratorTest.map DESTINATION ${PROJECT_BINARY_DIR}) + FILE(COPY tests/changedType.persist DESTINATION ${PROJECT_BINARY_DIR}) + FILE(COPY tests/changedVectorSize.persist DESTINATION ${PROJECT_BINARY_DIR}) + FILE(COPY tests/renamedVariable.persist DESTINATION ${PROJECT_BINARY_DIR}) endif(TESTING_IS_ENABLED) add_library(${PROJECT_NAME} SHARED ${library_sources} ) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_FULL_LIBRARY_VERSION} SOVERSION ${${PROJECT_NAME}_SOVERSION}) target_link_libraries(${PROJECT_NAME} ${LibXML++_LDFLAGS} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES} ${LibXML++_LIBRARIES} ${glib_LIBRARIES}) -#change the install prefix to the source directory in case the user has not specified a destination +#change the install prefix to the source directory in case the user has not specified a destination #i. e. CMAKE_INSTALL_PREFIX is not set manually IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) SET(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR} CACHE PATH "Install directory @@ -118,7 +121,7 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include #We additionally install the reference test application, it's header only install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include FILES_MATCHING - PATTERN "ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h") + PATTERN "ChimeraTK/ControlSystemAdapter/Testing/ReferenceTestApplication.h") #Create the config files by filling the correct variables into the template (*.cmake.in). #All variables @VARIABLE@ are replaced with the current values, but they have to be set first.... diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index 1c9b550..840989f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -16,18 +16,18 @@ namespace xmlpp { } namespace ChimeraTK { - + class ControlSystemPVManager; - + /** * Persistent data storage for process variables. - * + * * The PersistentDataStorage will create a file in the current working directory based on the provided application * name (".persist"). This file will be an XML file containing all values of the registered * variables. The file is written when the PersistentDataStorage is destroyed and read when it is constructed. * After reading the file, all variables will be updated with the current values taken from the file. This will be * seen as a value received by the application just like any other update. - * + * * @todo TODO list: * * thread safety (only an issue when having multiple ControlSystemPVManagers) * * automatic periodic commits @@ -37,10 +37,10 @@ namespace ChimeraTK { class PersistentDataStorage { public: - + /** Constructor: Open and parse the storage file. */ PersistentDataStorage(std::string const &applicationName); - + /** Destructor: Store variables to the file. */ ~PersistentDataStorage(); @@ -51,32 +51,32 @@ namespace ChimeraTK { /** Retrieve the current value for the variable with the given ID */ template - const std::vector& retrieveValue(size_t id) const; + const std::vector& retrieveValue(size_t id); /** Notify the storage system about a new value of the variable with the given ID (as returned by * registerVariable) */ template void updateValue(int id, std::vector const &value); - + protected: - + /** Write out the file containing the persistent data */ void writeToFile(); - + /** Read the file containing the persistent data */ void readFromFile(); - + /** Generate XML tags for the given value */ template void generateXmlValueTags(xmlpp::Element *parent, size_t id); - + /** Read value from XML tags */ template void readXmlValueTags(const xmlpp::Element *parent, size_t id); - + /** Application name */ std::string _applicationName; - + /** File name to store the data to */ std::string _filename; @@ -97,7 +97,7 @@ namespace ChimeraTK { boost::thread writerThread; void writerThreadFunction(); - + }; /*********************************************************************************************************************/ @@ -106,33 +106,35 @@ namespace ChimeraTK { size_t PersistentDataStorage::registerVariable(mtca4u::RegisterPath const &name, size_t nElements) { // check if already existing auto position = std::find(_variableNames.begin(), _variableNames.end(), name); - + + size_t id = position - _variableNames.begin(); + // create new element - if(position == _variableNames.end()) { - + if(position == _variableNames.end() || boost::fusion::at_key(_dataMap.table).count(id) == 0) { + // store name and type _variableNames.push_back(name); _variableTypes.push_back(&typeid(DataType)); // create value vector - size_t id = _variableNames.size()-1; + id = _variableNames.size()-1; std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; value.resize(nElements); - + // return id return id; } // return existing id else { - return position - _variableNames.begin(); + return id; } } /*********************************************************************************************************************/ template - const std::vector& PersistentDataStorage::retrieveValue(size_t id) const { - return boost::fusion::at_key(_dataMap.table).at(id); + const std::vector& PersistentDataStorage::retrieveValue(size_t id) { + return boost::fusion::at_key(_dataMap.table)[id]; } /*********************************************************************************************************************/ diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 85bf254..6a513aa 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -732,8 +732,11 @@ namespace ChimeraTK { _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), mtca4u::NDRegisterAccessor::getNumberOfSamples()); if(sendInitialValue) { + if( _persistentDataStorage->retrieveValue(_persistentDataStorageID).size() == + mtca4u::NDRegisterAccessor::buffer_2D[0].size() ) { mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); doWriteTransfer(); + } } } diff --git a/tests/changedType.persist b/tests/changedType.persist new file mode 100644 index 0000000..2728d35 --- /dev/null +++ b/tests/changedType.persist @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/changedVectorSize.persist b/tests/changedVectorSize.persist new file mode 100644 index 0000000..b7dca0c --- /dev/null +++ b/tests/changedVectorSize.persist @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/renamedVariable.persist b/tests/renamedVariable.persist new file mode 100644 index 0000000..c9ae869 --- /dev/null +++ b/tests/renamedVariable.persist @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/src/testPersistentDataStorage.cc b/tests/src/testPersistentDataStorage.cc index 56e203d..22dcc4c 100644 --- a/tests/src/testPersistentDataStorage.cc +++ b/tests/src/testPersistentDataStorage.cc @@ -1,7 +1,4 @@ -// Define a name for the test module. #define BOOST_TEST_MODULE PersistentDataStorageTest - -// Only after defining the name include the unit test header. #include using namespace boost::unit_test_framework; @@ -24,8 +21,6 @@ class MyTestApplication : public ApplicationBase { void run() {}; }; - -// Create a test suite which holds all your tests. BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) /** @@ -33,9 +28,9 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) */ -// Define one test after another. Each one needs a unique name. - BOOST_AUTO_TEST_CASE( testStoreAndRetrieve ) { - +// Test storing data in first instance of PersistentDataStorage and retrieveing it in a second instance +BOOST_AUTO_TEST_CASE( testStoreAndRetrieve ) { + // remove persistency file from previous test run boost::filesystem::remove("myTestApplication.persist"); @@ -62,11 +57,11 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) for(int i=0; i<100; ++i) BOOST_CHECK_CLOSE( myVar2stored[i], -120 + 7*i, 0.0001 ); } // the first PersistentDataStorage is destroyed at this point, in its destructor the file is written - + // create another PersistentDataStorage and read the previously stored variables from it { PersistentDataStorage storage{"myTestApplication"}; - + // check MyVar1 std::vector myVar1(10); int id1 = storage.registerVariable("MyVar1", 10); @@ -78,17 +73,17 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) int id2 = storage.registerVariable("/some/path.with.dots/to/MyVar2", 100); myVar2 = storage.retrieveValue(id2); for(int i=0; i<100; ++i) BOOST_CHECK_CLOSE( myVar2[i], -120 + 7*i, 0.0001 ); - + // modify some elements of myVar1 myVar1[7] = 42; myVar1[3] = 120; storage.updateValue(id1, myVar1); } - + // create another PersistentDataStorage and read the previously stored variables from it { PersistentDataStorage storage{"myTestApplication"}; - + // check MyVar1 std::vector myVar1(10); int id1 = storage.registerVariable("MyVar1", 10); @@ -110,20 +105,21 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) int id2 = storage.registerVariable("/some/path.with.dots/to/MyVar2", 100); myVar2 = storage.retrieveValue(id2); for(int i=0; i<100; ++i) BOOST_CHECK_CLOSE( myVar2[i], -120 + 7*i, 0.0001 ); - + // modify some elements of myVar1 myVar1[7] = 42; myVar1[3] = 120; storage.updateValue(id1, myVar1); } - - } - BOOST_AUTO_TEST_CASE( testUsageInPVManager ) { +} + +// test integration in PVManager +BOOST_AUTO_TEST_CASE( testUsageInPVManager ) { // remove persistency file from previous test run boost::filesystem::remove("myTestApplication.persist"); - + // first application instance: initialise the variables with some values and store them in the persistency file { @@ -139,10 +135,10 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); - + // enable persist data storage csManager->enablePersistentDataStorage(); - + // obtain the process variables, send some values to the variables auto v1 = csManager->getProcessArray("SomeCsToDevVar"); for(int i=0; i<7; ++i) v1->accessChannel(0)[i] = i*17; @@ -155,9 +151,9 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) auto v3 = devManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored for(int i=0; i<7; ++i) v3->accessChannel(0)[i] = 9*i + 666; v3->write(); - + } - + // second application instance: check if stored values are properly retrieved { @@ -173,13 +169,13 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); - + // enable persist data storage csManager->enablePersistentDataStorage(); - + // obtain all variables from the manager to initialise them with the persistent data storage csManager->getAllProcessVariables(); - + // obtain the process variables, send some values to the variables auto v1 = devManager->getProcessArray("SomeCsToDevVar"); v1->readNonBlocking(); @@ -192,11 +188,138 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored v3->readNonBlocking(); for(int i=0; i<7; ++i) BOOST_CHECK( v3->accessChannel(0)[i] == 0 ); - + + } + +} + +// test opening .persist files which contain a different variable household +BOOST_AUTO_TEST_CASE( testChangedVariableHousehold ) { + + // check with changed data type + boost::filesystem::remove("myTestApplication.persist"); + boost::filesystem::copy("changedType.persist", "myTestApplication.persist"); + + // check if stored values are properly retrieved + { + + // create instance of test application + MyTestApplication myTestApplication{"myTestApplication"}; + + // create PV managers + auto pvManagers = createPVManager(); + auto csManager = pvManagers.first; + auto devManager = pvManagers.second; + + // create some variables + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); + devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); + + // enable persist data storage + csManager->enablePersistentDataStorage(); + + // obtain all variables from the manager to initialise them with the persistent data storage + csManager->getAllProcessVariables(); + + // obtain the process variables, send some values to the variables + auto v1 = devManager->getProcessArray("SomeCsToDevVar"); + v1->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK_EQUAL( v1->accessChannel(0)[i], 0 ); + + auto v2 = devManager->getProcessArray("AnotherCsToDevVar"); + v2->readNonBlocking(); + for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->accessChannel(0)[i], 0, 0.00001 ); + + auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored + v3->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK( v3->accessChannel(0)[i] == 0 ); + + } + + // check with changed data type + boost::filesystem::remove("myTestApplication.persist"); + boost::filesystem::copy("changedVectorSize.persist", "myTestApplication.persist"); + + // check if stored values are properly retrieved + { + + // create instance of test application + MyTestApplication myTestApplication{"myTestApplication"}; + + // create PV managers + auto pvManagers = createPVManager(); + auto csManager = pvManagers.first; + auto devManager = pvManagers.second; + + // create some variables + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); + devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); + + // enable persist data storage + csManager->enablePersistentDataStorage(); + + // obtain all variables from the manager to initialise them with the persistent data storage + csManager->getAllProcessVariables(); + + // obtain the process variables, send some values to the variables + auto v1 = devManager->getProcessArray("SomeCsToDevVar"); + v1->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK_EQUAL( v1->accessChannel(0)[i], 0 ); + + auto v2 = devManager->getProcessArray("AnotherCsToDevVar"); + v2->readNonBlocking(); + for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->accessChannel(0)[i], 0, 0.00001 ); + + auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored + v3->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK( v3->accessChannel(0)[i] == 0 ); + } - - - } + + // check with changed data type + boost::filesystem::remove("myTestApplication.persist"); + boost::filesystem::copy("renamedVariable.persist", "myTestApplication.persist"); + + // check if stored values are properly retrieved + { + + // create instance of test application + MyTestApplication myTestApplication{"myTestApplication"}; + + // create PV managers + auto pvManagers = createPVManager(); + auto csManager = pvManagers.first; + auto devManager = pvManagers.second; + + // create some variables + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "SomeCsToDevVar", 7); + devManager->createProcessArray(SynchronizationDirection::controlSystemToDevice, "AnotherCsToDevVar", 42); + devManager->createProcessArray(SynchronizationDirection::deviceToControlSystem, "SomeDevToCsVar", 7); + + // enable persist data storage + csManager->enablePersistentDataStorage(); + + // obtain all variables from the manager to initialise them with the persistent data storage + csManager->getAllProcessVariables(); + + // obtain the process variables, send some values to the variables + auto v1 = devManager->getProcessArray("SomeCsToDevVar"); + v1->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK_EQUAL( v1->accessChannel(0)[i], i*17 ); + + auto v2 = devManager->getProcessArray("AnotherCsToDevVar"); + v2->readNonBlocking(); + for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->accessChannel(0)[i], 0, 0.00001 ); + + auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored + v3->readNonBlocking(); + for(int i=0; i<7; ++i) BOOST_CHECK( v3->accessChannel(0)[i] == 0 ); + + } + +} // After you finished all test you have to end the test suite. BOOST_AUTO_TEST_SUITE_END() From ac4775d31ffda9c383670ff513ce6737ae024b21 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 12 Feb 2018 18:30:44 +0100 Subject: [PATCH 312/786] increased patch level --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 087d8f8..f4bc075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 11) -set(${PROJECT_NAME}_PATCH_VERSION 00) +set(${PROJECT_NAME}_PATCH_VERSION 01) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From 193ff10898f16e7811a1a66394aecd80fffff10b Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Mon, 19 Feb 2018 12:55:22 +0100 Subject: [PATCH 313/786] added ddaq as component to FindDOOCS.cmake --- cmake/Modules/FindDOOCS.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake/Modules/FindDOOCS.cmake b/cmake/Modules/FindDOOCS.cmake index 4e0e527..deb0e51 100644 --- a/cmake/Modules/FindDOOCS.cmake +++ b/cmake/Modules/FindDOOCS.cmake @@ -58,6 +58,13 @@ if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";server;") set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} EqServer) endif() +if (";${DOOCS_FIND_COMPONENTS};" MATCHES ";ddaq;") + FIND_PATH(DOOCS_DIR_SERVER libDOOCSddaq.so + ${DOOCS_DIR} + ) + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSddaq timinginfo daqevstat DAQFSM TTF2XML xerces-c BM TTF2evutl) +endif() + set(DOOCS_LIBRARIES ${DOOCS_LIBRARIES} DOOCSapi nsl dl pthread m rt ldap) # now set the required variables based on the determined DOOCS_DIR From e38899e0e7901c35c8a8a3b7eaea7e0d63d1273f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 14:12:00 +0100 Subject: [PATCH 314/786] added an option to enable a thread safety check, which raises an assert when a ProcessArray object is used from different threads --- .../UnidirectionalProcessArray.h | 48 +++++++++++++++++++ src/UnidirectionalProcessArray.cc | 10 ++++ 2 files changed, 58 insertions(+) create mode 100644 src/UnidirectionalProcessArray.cc diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 6a513aa..01962ed 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,18 @@ namespace ChimeraTK { + namespace detail { + + /** Global flag if thread safety check shall performed on each read/write. */ + extern std::atomic processArrayEnableThreadSafetyCheck; // std::atomic defaults to false + + } + + /** Globally enable or disable the thread safety check on each read/write. This will throw an assertion if the + * thread id has been changed since the last read/write operation which has been executed with the safety check + * enabled. This will only have an effect if debug compiler flags are enabled. */ + void setEnableProcessArrayThreadSafetyCheck(bool enable); + /** * Implementation of the process array that transports data in a single * direction. This implementation is used for both sides (sender and @@ -320,6 +333,36 @@ namespace ChimeraTK { bool writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, bool shouldCopy); + + /** Check thread safety. This function is used in various places inside an assert(). */ + bool checkThreadSafety() { + + // Only perform check if enableThreadSafetyCheck is enabled + if(!detail::processArrayEnableThreadSafetyCheck) return true; + + // If threadSafetyCheckLastId has already been filled, perform the check + if(threadSafetyCheckInitialised) { + bool isOk = ( threadSafetyCheckLastId == std::hash()(std::this_thread::get_id()) ); + if(!isOk) { + threadSafetyCheckLastId = std::hash()(std::this_thread::get_id()); + std::cout << "ProcessArray thread safety check failed for variable " << this->getName() << std::endl; + } + return true; + } + else { + // ThreadSafetyCheckLastId not yet filled: fill it and set the flag. + threadSafetyCheckLastId = std::hash()(std::this_thread::get_id()); + threadSafetyCheckInitialised = true; + return true; + } + } + + /** Hash of the last known thread id for the thread safety check */ + std::atomic threadSafetyCheckLastId; + + /** Flag whether threadSafetyCheckLastId has been filled already */ + std::atomic threadSafetyCheckInitialised; // std::atomic defaults to false + }; /*********************************************************************************************************************/ @@ -609,6 +652,7 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::doPostRead() { + assert(checkThreadSafety()); // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into @@ -745,6 +789,10 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::writeInternal( TimeStamp newTimeStamp, VersionNumber newVersionNumber, bool shouldCopy) { + + // thread safety check, if enabled (only active with debug flags enabled) + assert(checkThreadSafety()); + if(!this->isWriteable()) { throw std::logic_error( "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); diff --git a/src/UnidirectionalProcessArray.cc b/src/UnidirectionalProcessArray.cc new file mode 100644 index 0000000..16a4188 --- /dev/null +++ b/src/UnidirectionalProcessArray.cc @@ -0,0 +1,10 @@ +#include "ProcessArray.h" + +namespace ChimeraTK { + namespace detail { + std::atomic processArrayEnableThreadSafetyCheck; + } + void setEnableProcessArrayThreadSafetyCheck(bool enable) { + detail::processArrayEnableThreadSafetyCheck = enable; + } +} From 1561152293e70d01dd24e99663d0230474cb57bb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 14:13:00 +0100 Subject: [PATCH 315/786] Added a stress test checking that multiple concurrent read and write operations on many process variables in many threads do not fail. Note: This test is currently failing due to a bug in the UnidirectionalProcessArray implementation! It runs into the assert(queueHasData || tripleBufferHasData) in doPostRead(), which means no data is present. --- tests/src/stresstest.cpp | 143 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 tests/src/stresstest.cpp diff --git a/tests/src/stresstest.cpp b/tests/src/stresstest.cpp new file mode 100644 index 0000000..6ae38c1 --- /dev/null +++ b/tests/src/stresstest.cpp @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include "ProcessArray.h" + +using namespace ChimeraTK; + +// std::ceil isn't constexpr for some reason... +constexpr size_t constceil(double num) { + return (static_cast(static_cast(num)) == num) + ? static_cast(num) + : static_cast(num) + ((num > 0) ? 1 : 0); +} + +std::atomic terminate; +std::atomic nSendOps; +std::atomic nReceiveOps; + + +extern "C" void sigAbortHandler(int /*signal_number*/) { + terminate = true; + std::cout << "SIGABORT caught. nSendOps = " << nSendOps << " nReceiveOps = " << nReceiveOps << std::endl; + exit(1); +} + + +int main() { + + // catch SIGABRT to print some useful information before terminating + signal(SIGABRT, &sigAbortHandler); + + constexpr size_t nSenders = 100; // same number of receivers + constexpr size_t nVarsPerSender = 100; // same number of variables per receiver + constexpr size_t runForSeconds = 10; + + nSendOps = 0; + nReceiveOps = 0; + + // create process variables and distribute them to the threads + for(size_t i=0; i>, boost::shared_ptr> >> pvars; + for(size_t k=0; k(1, name)); + } + + // create sender thread + boost::thread sender( [pvars] { + std::random_device rd; + std::mt19937 gen(rd()); + + // Random value will be used to determine action on receiver side (this value will be send through the variable): + // The value is the number of microseconds to sleep after each receive operation. + // The value modulo 4 determines the next receive operation type: + // 0 - read() on the next variable + // 1 - readNonBlocking() on the next variable + // 2 - readLatest() on the next variable + // 3 - readAny() on all variables + std::uniform_int_distribution<> disValue(1, 1000); + + // A second random value used to determin the number of microseconds to sleep after each send operation + std::uniform_int_distribution<> disSleep(1, 500); + + // loop until termination request + while(!terminate) { + for(auto &pv : pvars) { + pv.first->accessData(0) = disValue(gen); + pv.first->write(); + ++nSendOps; + usleep(disSleep(gen)); + } + } + + } ); // end of sender thread definition via lambda + sender.detach(); + + // create receiver thread + boost::thread receiver( [pvars] { + + // see comment in sender thread + int mode = 0; + + // iterator pointing to the current pvar + auto pviter = pvars.begin(); + + // fill list of app receivers for readAny() + std::list> varList; + std::map< mtca4u::TransferElementID, boost::shared_ptr> > varMap; + for(auto &pvar : pvars) { + varList.emplace_back(*(pvar.second)); + varMap[pvar.second->getId()] = pvar.second; + } + + // loop until termination request + while(!terminate) { + + int sleepTime; + + //if(mode == 0) mode = 2; + if(mode == 1) mode = 2; + if(mode == 3) mode = 2; + + + if(mode == 0) { + pviter->second->read(); + sleepTime = pviter->second->accessData(0); + } + else if(mode == 1) { + pviter->second->readNonBlocking(); + sleepTime = pviter->second->accessData(0); + } + else if(mode == 2) { + pviter->second->readLatest(); + sleepTime = pviter->second->accessData(0); + } + else { // mode == 3 + auto id = readAny(varList); + sleepTime = varMap[id]->accessData(0); + } + ++nReceiveOps; + mode = (sleepTime % 4); + + // iterate to next variable + ++pviter; + if(pviter == pvars.end()) pviter = pvars.begin(); + + usleep(sleepTime); + } + + } ); // end of sender thread definition via lambda + receiver.detach(); + + } // thread-launching loop + + sleep(runForSeconds); + terminate = true; + + return 0; + +} From 08acbcc8c0c2463e288c1e8d568989a18030de70 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 14:23:15 +0100 Subject: [PATCH 316/786] removed code for testing which shouldn't have been committed --- tests/src/stresstest.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/src/stresstest.cpp b/tests/src/stresstest.cpp index 6ae38c1..03e7506 100644 --- a/tests/src/stresstest.cpp +++ b/tests/src/stresstest.cpp @@ -98,12 +98,6 @@ int main() { while(!terminate) { int sleepTime; - - //if(mode == 0) mode = 2; - if(mode == 1) mode = 2; - if(mode == 3) mode = 2; - - if(mode == 0) { pviter->second->read(); sleepTime = pviter->second->accessData(0); From ecc269082d0817f303a3c6c6586f4b144c46e12b Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 18:35:59 +0100 Subject: [PATCH 317/786] improved readability --- tests/src/stresstest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/stresstest.cpp b/tests/src/stresstest.cpp index 03e7506..c374725 100644 --- a/tests/src/stresstest.cpp +++ b/tests/src/stresstest.cpp @@ -41,7 +41,7 @@ int main() { for(size_t i=0; i>, boost::shared_ptr> >> pvars; + std::vector< std::pair< boost::shared_ptr>, boost::shared_ptr> > > pvars; for(size_t k=0; k(1, name)); From 4d42d9579c43853ff07c9c8a5356bb9e08acc6d7 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 18:36:15 +0100 Subject: [PATCH 318/786] Added test for the rare bug running into the assert(queueHasData || tripleBufferHasData) in doPostRead() when using readLatest(). --- tests/src/testUnidirectionalProcessArray.cpp | 26 +++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/src/testUnidirectionalProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp index 2f49f3e..ecd7641 100644 --- a/tests/src/testUnidirectionalProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -508,7 +508,7 @@ namespace ChimeraTK { } BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK_EQUAL(receiver->accessData(0), 33); - BOOST_CHECK(receiver->readNonBlocking()); // after this line the buffer should be empty + BOOST_CHECK(receiver->readNonBlocking()); // after this line the queue should be empty BOOST_CHECK_EQUAL(receiver->accessData(0), 34); sender->accessData(0) = 12; sender->write(); @@ -516,6 +516,30 @@ namespace ChimeraTK { BOOST_CHECK_EQUAL(receiver->accessData(0), 12); BOOST_CHECK(!(receiver->readLatest())); + // provoke that the future obtained in the very beginning of doReadTransferLatest() is pointing to the buffer on + // the triple buffer + for(int i=0; i<10; ++i) { + sender->accessData(0) = 11+i; + sender->write(); + } + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 11); + BOOST_CHECK(receiver->readNonBlocking()); // after this line the queue should be empty + BOOST_CHECK_EQUAL(receiver->accessData(0), 12); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 20); + BOOST_CHECK(!(receiver->readLatest())); + for(int i=0; i<100; ++i) { // check that all buffers are still properly working + for(int k=0; k<3; ++k) { + sender->accessData(0) = 10*i + k; + sender->write(); + } + for(int k=0; k<3; ++k) { + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), T(10*i + k)); + } + } + } From f6a1ba337a7bbe7e1bcf1259f2eea12ddfa26969 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 18:37:19 +0100 Subject: [PATCH 319/786] properly mark overridden functions --- .../ControlSystemAdapter/TypeChangingDecorator.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 109adf0..4d0354d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -144,14 +144,14 @@ namespace ChimeraTK { class TypeChangingStringImplDecorator: public TypeChangingDecorator{ public: using TypeChangingDecorator::TypeChangingDecorator; - virtual void convertAndCopyFromImpl(){ + void convertAndCopyFromImpl() override { for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ this->buffer_2D[i][j] = csa_helpers::stringToT(this->_target->accessChannel(i)[j]); } } } - virtual void convertAndCopyToImpl(){ + void convertAndCopyToImpl() override { for (size_t i = 0; i < this->buffer_2D.size(); ++i){ for (size_t j = 0; j < this->buffer_2D[i].size(); ++j){ this->_target->accessChannel(i)[j] = csa_helpers::T_ToString(this->buffer_2D[i][j]); @@ -199,10 +199,10 @@ namespace ChimeraTK { class TypeChangingRangeCheckingDecorator: public TypeChangingStringImplDecorator{ public: using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; - virtual void convertAndCopyFromImpl(){ + void convertAndCopyFromImpl() override { TypeChangingStringImplDecorator::convertAndCopyFromImpl(); } - virtual void convertAndCopyToImpl(){ + void convertAndCopyToImpl() override { TypeChangingStringImplDecorator::convertAndCopyToImpl(); } DecoratorType getDecoratorType() const override{ @@ -297,10 +297,10 @@ namespace ChimeraTK { class TypeChangingDirectCastDecorator : public TypeChangingStringImplDecorator { public: using TypeChangingStringImplDecorator::TypeChangingStringImplDecorator; - virtual void convertAndCopyFromImpl(){ + void convertAndCopyFromImpl() override { TypeChangingStringImplDecorator::convertAndCopyFromImpl(); } - virtual void convertAndCopyToImpl(){ + void convertAndCopyToImpl() override { TypeChangingStringImplDecorator::convertAndCopyToImpl(); } DecoratorType getDecoratorType() const override{ From 12ed1a6f3f1ca973d59f149dbcb220a98193c9ac Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 21 Feb 2018 18:38:37 +0100 Subject: [PATCH 320/786] Fixed the rare bug running into the assert(queueHasData || tripleBufferHasData) in doPostRead when using readLatest(). This bug was caused by not treating the triple buffer properly, which could result in putting the triple buffer index on the emptyBufferQueue while keeping it as the current triple buffer index. --- .../UnidirectionalProcessArray.h | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 01962ed..bd7f677 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -527,13 +527,16 @@ namespace ChimeraTK { typename ProcessArray::InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, - UnidirectionalProcessArray::SharedPtr receiver) : - ProcessArray(instanceType, receiver->getName(), receiver->getUnit(), - receiver->getDescription()), _vectorSize(receiver->_vectorSize), _maySendDestructively( - maySendDestructively), _sharedState(receiver->_sharedState), _currentIndex( - &(_sharedState->_buffers[1])), _tripleBufferIndex( - &(_sharedState->_buffers[2])), _receiver(receiver), _timeStampSource( - timeStampSource), _sendNotificationListener(sendNotificationListener) + UnidirectionalProcessArray::SharedPtr receiver) + : ProcessArray(instanceType, receiver->getName(), receiver->getUnit(), receiver->getDescription()), + _vectorSize(receiver->_vectorSize), + _maySendDestructively(maySendDestructively), + _sharedState(receiver->_sharedState), + _currentIndex(&(_sharedState->_buffers[1])), + _tripleBufferIndex(&(_sharedState->_buffers[2])), + _receiver(receiver), + _timeStampSource(timeStampSource), + _sendNotificationListener(sendNotificationListener) { mtca4u::ExperimentalFeatures::enable(); // It would be better to do the validation before initializing, but this @@ -563,7 +566,7 @@ namespace ChimeraTK { // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call // TransferFuture::wait(). mtca4u::TransferElement::readAsync().getBoostFuture().wait(); - boost::this_thread::interruption_point(); + boost::this_thread::interruption_point(); /// @todo probably redundant } @@ -584,13 +587,18 @@ namespace ChimeraTK { // Due to our implementation there is always one unfulfilled future in the queue, so // we must pop until there are two elements left in order not to flush out the newest valid value. auto theFuture = mtca4u::TransferElement::readAsync().getBoostFuture(); - while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout + while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout /// @todo probably redundant check && _sharedState->_fullBufferQueue.read_available() > 2 ) { // discard data by moving the buffer to the empty buffer queue TransferFuture::Data *discardedBuffer = theFuture.get(); - _sharedState->_fullBufferQueue.pop(); - mtca4u::TransferElement::hasActiveFuture = false; - _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue + if(discardedBuffer != _tripleBufferIndex) { // buffer is coming from fullBufferQueue + _sharedState->_fullBufferQueue.pop(); + mtca4u::TransferElement::hasActiveFuture = false; + _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue + } + else { // buffer is coming from triple buffer + _tripleBufferIndex->_isValid = false; + } theFuture = mtca4u::TransferElement::readAsync().getBoostFuture(); } // Check whether data is present in the atomic triple buffer. If it is newer, we also need to discard the last @@ -603,7 +611,10 @@ namespace ChimeraTK { if(_tripleBufferIndex->_isValid) { uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; uint64_t queueVersion = static_cast(theFuture.get())->_internalVersionNumber; - if(tripleBufferVersion > queueVersion) { + if(theFuture.get() == _tripleBufferIndex) { // the future is pointing us to the triple buffer (queue was empty, latest data on triple buffer) + // don't invalidate any data + } + else if(tripleBufferVersion > queueVersion) { TransferFuture::Data *discardedBuffer = theFuture.get(); _sharedState->_fullBufferQueue.pop(); _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); From 4f95afffe999a2ef5aaa71a38049301849e88392 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 22 Feb 2018 13:59:39 +0100 Subject: [PATCH 321/786] increase minor version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4bc075..ff7453e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 11) -set(${PROJECT_NAME}_PATCH_VERSION 01) +set(${PROJECT_NAME}_MINOR_VERSION 12) +set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From ef75c92d6dea05ce4e71847783bcad202e186c3f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 22 Feb 2018 14:03:01 +0100 Subject: [PATCH 322/786] thread safety check: let the assertion fail instead of printing only a warning. --- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index bd7f677..c7be233 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -342,12 +342,7 @@ namespace ChimeraTK { // If threadSafetyCheckLastId has already been filled, perform the check if(threadSafetyCheckInitialised) { - bool isOk = ( threadSafetyCheckLastId == std::hash()(std::this_thread::get_id()) ); - if(!isOk) { - threadSafetyCheckLastId = std::hash()(std::this_thread::get_id()); - std::cout << "ProcessArray thread safety check failed for variable " << this->getName() << std::endl; - } - return true; + return ( threadSafetyCheckLastId == std::hash()(std::this_thread::get_id()) ); } else { // ThreadSafetyCheckLastId not yet filled: fill it and set the flag. From f7442d6313f89d36ff86ba84591083baf5715509 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 2 Mar 2018 16:21:24 +0100 Subject: [PATCH 323/786] corrected code formatting --- .../UnidirectionalProcessArray.h | 57 +++++++------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index c7be233..3663cb5 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -330,8 +330,7 @@ namespace ChimeraTK { * * The return value will be true, if older data was overwritten during the send operation, or otherwise false. */ - bool writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, - bool shouldCopy); + bool writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, bool shouldCopy); /** Check thread safety. This function is used in various places inside an assert(). */ @@ -408,10 +407,8 @@ namespace ChimeraTK { std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 2, - bool maySendDestructively = false, - TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = - ProcessVariableListener::SharedPtr()); + bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr() ); /** * Creates a synchronized process array. A synchronized process array works @@ -460,8 +457,7 @@ namespace ChimeraTK { const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = - ProcessVariableListener::SharedPtr()); + ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr()); /*********************************************************************************************************************/ /*** Implementations of member functions below this line *************************************************************/ @@ -733,48 +729,39 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - bool UnidirectionalProcessArray::doWriteTransfer( - ChimeraTK::VersionNumber versionNumber) { - return writeInternal( - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(), - versionNumber, true); + bool UnidirectionalProcessArray::doWriteTransfer(ChimeraTK::VersionNumber versionNumber) { + auto timestamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); + return writeInternal(timestamp, versionNumber, true); } /*********************************************************************************************************************/ template - bool UnidirectionalProcessArray::writeDestructively( - ChimeraTK::VersionNumber versionNumber) { - if (!_maySendDestructively) { - throw std::runtime_error( - "This process variable must not be sent destructively because the corresponding flag has not been set."); + bool UnidirectionalProcessArray::writeDestructively(ChimeraTK::VersionNumber versionNumber) { + if(!_maySendDestructively) { + throw std::runtime_error("This process variable must not be sent destructively because the corresponding flag " + "has not been set."); } - return writeInternal( - _timeStampSource ? - _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(), - versionNumber, false); + auto timestamp = _timeStampSource ? _timeStampSource->getCurrentTimeStamp() : TimeStamp::currentTime(); + return writeInternal(timestamp, versionNumber, false); } /*********************************************************************************************************************/ template - bool UnidirectionalProcessArray::writeDestructively( - ChimeraTK::TimeStamp timeStamp, - ChimeraTK::VersionNumber versionNumber) { - if (!_maySendDestructively) { - throw std::runtime_error( - "This process variable must not be sent destructively because the corresponding flag has not been set."); + bool UnidirectionalProcessArray::writeDestructively(ChimeraTK::TimeStamp timeStamp, + ChimeraTK::VersionNumber versionNumber) { + if(!_maySendDestructively) { + throw std::runtime_error("This process variable must not be sent destructively because the corresponding flag " + "has not been set."); } - return writeInternal(timeStamp, versionNumber, - false); + return writeInternal(timeStamp, versionNumber, false); } /*********************************************************************************************************************/ template - void UnidirectionalProcessArray::setPersistentDataStorage( - boost::shared_ptr storage) { + void UnidirectionalProcessArray::setPersistentDataStorage(boost::shared_ptr storage) { if(!this->isWriteable()) return; bool sendInitialValue = false; if(!_persistentDataStorage) sendInitialValue = true; @@ -793,8 +780,8 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - bool UnidirectionalProcessArray::writeInternal( - TimeStamp newTimeStamp, VersionNumber newVersionNumber, bool shouldCopy) { + bool UnidirectionalProcessArray::writeInternal(TimeStamp newTimeStamp, VersionNumber newVersionNumber, + bool shouldCopy) { // thread safety check, if enabled (only active with debug flags enabled) assert(checkThreadSafety()); From 124a5ecab9fe6e128735ceb8d62ab4d7ddd93062 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 2 Mar 2018 16:21:42 +0100 Subject: [PATCH 324/786] fixed some bugs in the PersistentDataStorage which could lead to unneeded entries in the .persist files after changing the variable household --- .../PersistentDataStorage.h | 77 ++++++++++++++++++- src/PersistentDataStorage.cc | 53 ++++++------- tests/src/testPersistentDataStorage.cc | 26 +++++-- 3 files changed, 121 insertions(+), 35 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index 840989f..7b45920 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -7,7 +7,10 @@ #include #include #include + #include +#include + #include #include @@ -45,9 +48,11 @@ namespace ChimeraTK { ~PersistentDataStorage(); /** Register a variable to be stored to and retrieved from the data storage. The returned value is the ID which - * must be passed to the other functions. */ + * must be passed to the other functions. The last argument "fromFile" should be false when the function is + * called from within UnidirectionalProcessArray::setPersistentDataStorage() etc. and true when called from + * within readFromFile(). */ template - size_t registerVariable(mtca4u::RegisterPath const &name, size_t nElements); + size_t registerVariable(mtca4u::RegisterPath const &name, size_t nElements, bool fromFile=false); /** Retrieve the current value for the variable with the given ID */ template @@ -83,6 +88,11 @@ namespace ChimeraTK { /** Vector of variable names. The index is the ID of the variable. */ std::vector _variableNames; + /** Vector of flags whether the variable was registers from the application. The index is the ID of the variable. + * This flag is used to clean up variables only coming from the file and are no longer present in the + * application. */ + std::vector _variableRegisteredFromApp; + /** Vector of data types. The index is the ID of the variable. */ std::vector _variableTypes; @@ -98,24 +108,49 @@ namespace ChimeraTK { void writerThreadFunction(); + /** A functor needed in registerVariable() */ + struct registerVariable_oldTypeRemover { + template + void operator()(PAIR &pair) const; + const std::type_info *type; + size_t id; + }; }; /*********************************************************************************************************************/ + template + void PersistentDataStorage::registerVariable_oldTypeRemover::operator()(PAIR &pair) const { + + // search for the right type + if(*type != typeid(typename PAIR::first_type)) return; + + // remove entry from data map + pair.second.erase(id); + + } + + /*********************************************************************************************************************/ + template - size_t PersistentDataStorage::registerVariable(mtca4u::RegisterPath const &name, size_t nElements) { + size_t PersistentDataStorage::registerVariable(mtca4u::RegisterPath const &name, size_t nElements, bool fromFile) { // check if already existing auto position = std::find(_variableNames.begin(), _variableNames.end(), name); size_t id = position - _variableNames.begin(); // create new element - if(position == _variableNames.end() || boost::fusion::at_key(_dataMap.table).count(id) == 0) { + if(position == _variableNames.end()) { + // output information + if(!fromFile) std::cout << "PersistentDataStorage: registering new variable " << name << std::endl; // store name and type _variableNames.push_back(name); _variableTypes.push_back(&typeid(DataType)); + // set flag whether this variable has been registered from the application + _variableRegisteredFromApp.push_back(!fromFile); + // create value vector id = _variableNames.size()-1; std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; @@ -124,8 +159,42 @@ namespace ChimeraTK { // return id return id; } + // replace element (changed data type) + else if(boost::fusion::at_key(_dataMap.table).count(id) == 0) { + std::cout << "PersistentDataStorage: changing type of variable " << name << std::endl; + assert(_variableTypes[id] != &typeid(DataType)); + assert(!fromFile); + + // remove value vector from old type + boost::fusion::for_each(_dataMap.table, registerVariable_oldTypeRemover({_variableTypes[id], id})); + + // update type + _variableTypes[id] = &typeid(DataType); + + // update flag that this variable has been registered from the application + _variableRegisteredFromApp[id] = true; + + // create value vector + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + value.resize(nElements); + + // return id + return id; + } // return existing id else { + assert(!fromFile); + + // update flag that this variable has been registered from the application + _variableRegisteredFromApp[id] = true; + + // check if resize required + std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; + if(value.size() != nElements) { + std::cout << "PersistentDataStorage: changing size of variable " << name << std::endl; + value.resize(nElements); + } + return id; } } diff --git a/src/PersistentDataStorage.cc b/src/PersistentDataStorage.cc index c8d031e..41c44ca 100644 --- a/src/PersistentDataStorage.cc +++ b/src/PersistentDataStorage.cc @@ -25,7 +25,7 @@ namespace ChimeraTK { } /*********************************************************************************************************************/ - + PersistentDataStorage::~PersistentDataStorage() { writerThread.interrupt(); writerThread.join(); @@ -33,7 +33,7 @@ namespace ChimeraTK { } /*********************************************************************************************************************/ - + void PersistentDataStorage::writerThreadFunction() { while(true) { for(int i=0; i<30; ++i) { @@ -46,7 +46,7 @@ namespace ChimeraTK { } /*********************************************************************************************************************/ - + void PersistentDataStorage::writeToFile() { // create XML document with root node and a flat list of variables below this root @@ -55,6 +55,7 @@ namespace ChimeraTK { rootElement->set_attribute("application", _applicationName); for(size_t i=0; i<_variableNames.size(); ++i) { + if(!_variableRegisteredFromApp[i]) continue; // exclude variables no longer present in the application // create XML element for the variable and set name attribute xmlpp::Element *variable = rootElement->add_child("variable"); @@ -101,7 +102,7 @@ namespace ChimeraTK { else { /// @todo TODO what todo here? } - + // set type attribute variable->set_attribute("type",dataTypeName); @@ -116,7 +117,7 @@ namespace ChimeraTK { template void PersistentDataStorage::generateXmlValueTags(xmlpp::Element *parent, size_t id) { - + // obtain the data vector from the map std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; @@ -131,16 +132,16 @@ namespace ChimeraTK { /*********************************************************************************************************************/ void PersistentDataStorage::readFromFile() { - + // check if file exists - struct stat buffer; + struct stat buffer; if(stat(_filename.c_str(), &buffer) != 0) { // file does not exist: print message and do nothing std::cerr << "ChimeraTK::PersistentDataStorage: Persistency file '" << _filename << "' does not exist. " "It will be created when exiting the application." << std::endl; return; } - + try { xmlpp::DomParser parser; //parser.set_validate(); @@ -150,22 +151,22 @@ namespace ChimeraTK { // obtain root node const xmlpp::Node *rootElement = parser.get_document()->get_root_node(); // object will be deleted by DomParser /// @todo TODO check if the application name is correct? - + // iterate through variables for(auto &elem : rootElement->get_children()) { const xmlpp::Element *child = dynamic_cast(elem); if(!child) continue; // comment or white spaces... std::string name = child->get_attribute("name")->get_value(); std::string type = child->get_attribute("type")->get_value(); - if(type == "int8") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "uint8") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "int16") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "uint16") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "int32") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "uint32") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "float") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "double") { readXmlValueTags(child, registerVariable(name, 0)); } - else if(type == "string") { readXmlValueTags(child, registerVariable(name, 0)); } + if(type == "int8") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "uint8") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "int16") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "uint16") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "int32") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "uint32") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "float") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "double") { readXmlValueTags(child, registerVariable(name, 0, true)); } + else if(type == "string") { readXmlValueTags(child, registerVariable(name, 0, true)); } else { /* @todo TODO ??? */ } } } @@ -175,15 +176,15 @@ namespace ChimeraTK { } catch(const std::exception& ex) { // @todo TODO proper exception handling std::cout << "Exception caught: " << ex.what() << std::endl; - } - + } + } /*********************************************************************************************************************/ template void PersistentDataStorage::readXmlValueTags(const xmlpp::Element *parent, size_t id) { - + // obtain the data vector from the map std::vector &value = boost::fusion::at_key(_dataMap.table)[id]; @@ -191,23 +192,23 @@ namespace ChimeraTK { for(auto &valElems : parent->get_children()) { const xmlpp::Element *valChild = dynamic_cast(valElems); if(!valChild) continue; // comment or white spaces... - + // obtain index and value as string std::string s_idx = valChild->get_attribute("i")->get_value(); std::string s_val = valChild->get_attribute("v")->get_value(); - + // convert to data type size_t idx = boost::lexical_cast(s_idx); DataType val = boost::lexical_cast(s_val); - + // resize vector if needed if(value.size() <= idx) value.resize(idx+1); - + // store value value[idx] = val; } } /*********************************************************************************************************************/ - + } /* namespace ChimeraTK */ diff --git a/tests/src/testPersistentDataStorage.cc b/tests/src/testPersistentDataStorage.cc index 22dcc4c..ea83230 100644 --- a/tests/src/testPersistentDataStorage.cc +++ b/tests/src/testPersistentDataStorage.cc @@ -26,7 +26,7 @@ BOOST_AUTO_TEST_SUITE( PersistentDataStorageTestSuite ) /** * @todo FIXME No exception tests are yet done. They should be implemented asap! */ - +/* // Test storing data in first instance of PersistentDataStorage and retrieveing it in a second instance BOOST_AUTO_TEST_CASE( testStoreAndRetrieve ) { @@ -192,6 +192,12 @@ BOOST_AUTO_TEST_CASE( testUsageInPVManager ) { } } +*/ + +size_t countLinesInFile(const std::string &filename) { + std::ifstream inFile(filename); + return std::count(std::istreambuf_iterator(inFile), std::istreambuf_iterator(), '\n'); +} // test opening .persist files which contain a different variable household BOOST_AUTO_TEST_CASE( testChangedVariableHousehold ) { @@ -237,7 +243,10 @@ BOOST_AUTO_TEST_CASE( testChangedVariableHousehold ) { } - // check with changed data type + // check that the number of lines in the file didn't change + BOOST_CHECK_EQUAL( countLinesInFile("changedType.persist"), countLinesInFile("myTestApplication.persist") ); + + // check with changed vector size boost::filesystem::remove("myTestApplication.persist"); boost::filesystem::copy("changedVectorSize.persist", "myTestApplication.persist"); @@ -266,11 +275,12 @@ BOOST_AUTO_TEST_CASE( testChangedVariableHousehold ) { // obtain the process variables, send some values to the variables auto v1 = devManager->getProcessArray("SomeCsToDevVar"); v1->readNonBlocking(); - for(int i=0; i<7; ++i) BOOST_CHECK_EQUAL( v1->accessChannel(0)[i], 0 ); + for(int i=0; i<4; ++i) BOOST_CHECK_EQUAL( v1->accessChannel(0)[i], i*17 ); + for(int i=4; i<7; ++i) BOOST_CHECK_EQUAL( v1->accessChannel(0)[i], 0 ); auto v2 = devManager->getProcessArray("AnotherCsToDevVar"); v2->readNonBlocking(); - for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->accessChannel(0)[i], 0, 0.00001 ); + for(int i=0; i<42; ++i) BOOST_CHECK_CLOSE(v2->accessChannel(0)[i], i*3.1415 * 1e30, 2e23); auto v3 = csManager->getProcessArray("SomeDevToCsVar"); // this one won't get stored v3->readNonBlocking(); @@ -278,7 +288,10 @@ BOOST_AUTO_TEST_CASE( testChangedVariableHousehold ) { } - // check with changed data type + // check that the number of lines in the file changed in the right way (SomeCsToDevVar changed from 4 to 7 elements) + BOOST_CHECK_EQUAL( countLinesInFile("changedVectorSize.persist")+2, countLinesInFile("myTestApplication.persist") ); + + // check with renamed variable boost::filesystem::remove("myTestApplication.persist"); boost::filesystem::copy("renamedVariable.persist", "myTestApplication.persist"); @@ -319,6 +332,9 @@ BOOST_AUTO_TEST_CASE( testChangedVariableHousehold ) { } + // check that the number of lines in the file didn't change + BOOST_CHECK_EQUAL( countLinesInFile("renamedVariable.persist"), countLinesInFile("myTestApplication.persist") ); + } // After you finished all test you have to end the test suite. From c057a4313c8b24cf05517342ace2f932ab3116b8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 2 Mar 2018 16:23:32 +0100 Subject: [PATCH 325/786] increased patch level --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7453e..1e6b421 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) set(${PROJECT_NAME}_MINOR_VERSION 12) -set(${PROJECT_NAME}_PATCH_VERSION 00) +set(${PROJECT_NAME}_PATCH_VERSION 01) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From 6758eefc0cb9827512b78e6ac7ec42c8c72b2cb2 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 6 Mar 2018 13:54:08 +0100 Subject: [PATCH 326/786] increased minor version, since the last patch release should not have been a patch release (not binary compatible) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e6b421..0e79267 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 12) -set(${PROJECT_NAME}_PATCH_VERSION 01) +set(${PROJECT_NAME}_MINOR_VERSION 13) +set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow From 019594b7b6cd211ce5d5c31d8a71ac2b2e741b40 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 13 Mar 2018 10:36:40 +0100 Subject: [PATCH 327/786] added cmake module to search for readline --- cmake/Modules/Findreadline.cmake | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 cmake/Modules/Findreadline.cmake diff --git a/cmake/Modules/Findreadline.cmake b/cmake/Modules/Findreadline.cmake new file mode 100644 index 0000000..46ba6c0 --- /dev/null +++ b/cmake/Modules/Findreadline.cmake @@ -0,0 +1,15 @@ +# Search for the path containing library's headers +find_path( readline_ROOT_DIR NAMES include/readline/readline.h ) + +# Search for include directory +find_path( readline_INCLUDE_DIR NAMES readline/readline.h + HINTS ${Readline_ROOT_DIR}/include ) + +# Search for library +find_library( readline_LIBRARY NAMES readline + HINTS ${Readline_ROOT_DIR}/lib ) + +# use a macro provided by CMake to check if all the listed arguments are valid and set readline_FOUND accordingly +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(readline DEFAULT_MSG readline_INCLUDE_DIR readline_LIBRARY ) + From 3f9e8352382634774f55e8c2afc0bef28f88b4f4 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 13 Mar 2018 10:43:48 +0100 Subject: [PATCH 328/786] fixed wrong case (just a cosmetic change) --- cmake/Modules/Findreadline.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/Findreadline.cmake b/cmake/Modules/Findreadline.cmake index 46ba6c0..8f432fc 100644 --- a/cmake/Modules/Findreadline.cmake +++ b/cmake/Modules/Findreadline.cmake @@ -3,11 +3,11 @@ find_path( readline_ROOT_DIR NAMES include/readline/readline.h ) # Search for include directory find_path( readline_INCLUDE_DIR NAMES readline/readline.h - HINTS ${Readline_ROOT_DIR}/include ) + HINTS ${readline_ROOT_DIR}/include ) # Search for library find_library( readline_LIBRARY NAMES readline - HINTS ${Readline_ROOT_DIR}/lib ) + HINTS ${readline_ROOT_DIR}/lib ) # use a macro provided by CMake to check if all the listed arguments are valid and set readline_FOUND accordingly include(FindPackageHandleStandardArgs) From e78f16e112b7422edf817163a57e6b1e2f7db0eb Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Fri, 16 Mar 2018 15:03:40 +0100 Subject: [PATCH 329/786] Ignore configuration files for Visual Studio Code. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 84f373c..adf444b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ build .project .kdev4 *.kdev4 +.vscode #files ending with ~ are emacs backups *~ #files ending with .swp might show up when using vi From 915a678e93fce42c2ffe08a0a9a8148278c614c9 Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Sat, 17 Mar 2018 19:52:40 +0100 Subject: [PATCH 330/786] Use correct linker flags on macOS. On macOS, the format of the -rpath linker option is -rpath /path/to/dir, not -rpath=/path/to/dir like on Linux. In addition to that, the linker on macOS does not understand the --enable-new-dtags flag. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e79267..5d0703b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,11 @@ set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUD set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}") set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS} ${mtca4u-deviceaccess_LIBRARY_DIRS}") set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11 ${mtca4u-deviceaccess_CXX_FLAGS}") +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") +set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath,${CMAKE_INSTALL_PREFIX}/lib ${mtca4u-deviceaccess_LINKER_FLAGS}") +else() set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath=${CMAKE_INSTALL_PREFIX}/lib,--enable-new-dtags ${mtca4u-deviceaccess_LINKER_FLAGS}") +endif() include(${CMAKE_SOURCE_DIR}/cmake/create_cmake_config_files.cmake) From 7677b472d7eab8053adc88c4046f8a82b575b2da Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 28 Mar 2018 09:36:29 +0200 Subject: [PATCH 331/786] removed c++11 flag as it interferes with the c++14 flag coming from DeviceAccess --- CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d0703b..342f2fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,10 +9,7 @@ set(${PROJECT_NAME}_MINOR_VERSION 13) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -Wextra -Wshadow -#-Weffc++ -ansi -pedantic -Wuninitialized -std=c++11") -#skip -Weffc++ for the moment. It gives too many false positives -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow -pedantic -Wuninitialized -std=c++11") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow -pedantic -Wuninitialized") #use -DCMAKE_BUILD_TYPE=Debug in your cmake command to turn on the coverage option set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 --coverage") @@ -130,7 +127,7 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/include/ DESTINATION include set(${PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIRS}") set(${PROJECT_NAME}_LIBRARIES "${PROJECT_NAME} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${mtca4u-deviceaccess_LIBRARIES}") set(${PROJECT_NAME}_LIBRARY_DIRS "${CMAKE_INSTALL_PREFIX}/lib ${Boost_LIBRARY_DIRS} ${mtca4u-deviceaccess_LIBRARY_DIRS}") -set(${PROJECT_NAME}_CXX_FLAGS "-std=c++11 ${mtca4u-deviceaccess_CXX_FLAGS}") +set(${PROJECT_NAME}_CXX_FLAGS "${mtca4u-deviceaccess_CXX_FLAGS}") if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(${PROJECT_NAME}_LINKER_FLAGS "-Wl,-rpath,${CMAKE_INSTALL_PREFIX}/lib ${mtca4u-deviceaccess_LINKER_FLAGS}") else() From e8246719e3c5e29bde680d413090dd486a5916fe Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Thu, 12 Apr 2018 10:38:12 +0200 Subject: [PATCH 332/786] updated the DESY logo --- doc/DESY_logo.png | Bin 7156 -> 8789 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/DESY_logo.png b/doc/DESY_logo.png index 1a68c33660eaba69b35d4d1d8cc68e86f673842d..5289ef1db22e8c1405b4b510a35f55eb8db5350e 100644 GIT binary patch literal 8789 zcmW-n1yCG68^)KzDQ@NP!`+L!ySuc-rMSDhySqd2;#Sn7HrTD zaZwc74=t#)Zy=}^3d*RctZ*Pq{R<*{d*eeoqg2|@N=wJ>w=XW21%B2%Ce(TUUylWn zD3Tllf*yO1^B;R7c8gHbHIV>o+p*Ue7Zd;-T>HulpNtAQs|i|v7U!aH^%ZA41Um1m zkUzpxp3G8)M?7HaN-_Y5bdb{RQh*8w1ti0k`l-VlpZo+G*` z6{m-WM-$x)-L-UKLY`)%vBMOA4^Q4*>$UadXAM6wmjLrn#ok-dj)Zv=j8gaO(cq?5 zx1{1w!6#!k4n0THTgW@X1HaYA*W46QOVV2aCLmpkx=h+h#+TbSbTJhNS4-!Giom~R z>Njl@u)9~{Eg;K?7ag5a%M6CdzLBD~AQ~v%Mgs2OPH4j?%;__{^Km8b$Td(CP|7mS zW)o7(W#W?qL`Qp?KQKC8GdB!NeD|TLUSF52on_ae+Y%8W)u0#1vFVG8QT5;~k;Ni0 z)}6ytxor|1!As?DM<`A)#Wrib&{{Mf%g0uzgd1JL(f!Y2)e>d*5as>~flb#hp;&S+i@~3Z)-D#(JvJ%-M#_;c|u&@MW&bKe@9ZsRF7 zbKW0|q`KUB9Y<<+TW}QkeWtP-x6MtHjVt%n%_&8IeknhZ`XW@%nX=fdQLTjJkL8_= z?N8}MI1`Pz{~B``+=J?6Pu!c<(}~uDoyhL|?pEqM@WPXALZk_zI3eMhhM2{~hV#{- zvht|PT$_NgT$tjd6zk$-qo=&3Xif`2$uWRPAp9bUD5sma&(Rajx+bLw^h6Q(FEH=- zk>@o0N{KFKhOo<}Gt*xMJgRn(<^8lkHpMkd=0Zt`PXoFEcU;NeXm>W8`03aIb=4uV zkrMKVt!*;)ihcu)>0+K7h8>ksEJ;l3+{%wFcjs-OF5A>#w2hjaPw0yHL1)QV{(04o zzp}t+!PesJHBjWsB1bX7s_0)b&&zAbCLVAn%SL{X@yyUZfr@3gV#3_qTp$7a{P)lX zNd@iB7MY{*5?dxSemM2O_jjO|z_Nsyo0bUQ^xme8s@;@KhJY?98Z<?>JC7D_dmnuS7j)3}4G$Qr6*8TtV=g z>dXs%xj)OdGdx?;340<5{CSFtnM(tT_kW=irnh8krHggRg))&l#w@&Dm!_%}?@CCy zEV2>`^b4!cuqj73%9W58;?az}?Qc%hz>{QTMN~&lh$pr&m$h&9T4>ZZepRp;YnK?s z0FHH>?`pa*xn|Rf7A(+&y^KewS#d})=vMJD*pME1ez@$@z}Xi%b?!9oJ`|XCH|Ws; zO~vULz0U1!JNR!m!k%Of6l}s<;rV4zz!R5=#IiCd74D=wT=Cncv}N8ZJ^cSA7VJer zVp?=OBSi(LRT~-!Zu)JlrAIA|Lf3e!Ej*q>H8Fz>c1OJZ{`Otfv!QleqAw*jDaQJg zKA)kF?|sLMFy}yRPW^_IkiPAInZ*YY1gF6%hss8CrZL4ZF?8L2S(w>rkIldzsI@B; z<=atE2`7srid}ZQ;jBUNZr-5#b5&+s)VYdqVruz z^vybbobaFEok_(%^_L&eLQGWNiJz0D6{l#R6_3y_-Y9h-9KU;u=4+)jdV1ajY|%)>=2=AwjC*aKeH7u{{srklKM#u9eqSnTrBi;nOf%yVFS7 z2~I%nM&pBkSgC06s>C^g>^uXa`Z~BoXL{w&G2i24A(N04U*py8g+?kIphg~Zq=E$a zb5-x2t}NE?qXY-#nMvJp@iEzlJ6`AsA2(vnT_!H9{gFzGfMf}p=nCx6e>&0HUX%`jsj zYx|pO1-B#VaE;E&?il>s6c`=#l<2^f%ERpd@uTfJvuQ!=Xe-%&#}9504JZNW`SA-= z?57qpcP;KOEknt0z--@5^&}8{!7w`3KSbnE)d{vY*n`~08u;=Tud!`n3ztzVo0~K6 z{2+3z{cf2>;F^-_kzhJgspyZ(`qd^a=pv=qEKhIdo67W7xi9{gTxt6=_WVOXbGSww zTWbA|WtAhs<~x0P0=6yE{PQeZ0UpWNlF5l=J(c#=Sw&aEjdYhVGhkE$kv7X7b!OvT zogm33dVp7R9)5xuT?TqV%8)^?5Ou^>1uGPF+vF!znhE-gyS`_`I&MDmA`xV?NuSjn zY$Yu#>HgWFt%3l|*ZVK9Falz&^Ai@l2)h&xiPnnCK~w6=mT44-wLj5U*j&;Sg`~uO zX7s)OHSk7)dP$hKELr9d8WTR|1GDn{}Z_2KP6s9JbjY%^&u1aSF!0BE=WM5AoFC~?Wh1^uPzl~x~sQ$CH&3?*;sv1WYr9_SyY~| zJ@jv1lBQs`V;b`zec3$v*zo4Z8_<>dfZta8LcByw%ja63-@h2C{r0BGaM?D9NhKl) zf`k}N72JR@u=_Ve0@D2dh|5`=T@a2FrW^DOU4+Y__xO(;r9ReBFaCB2i|v`3TR1)vQYfuXjMDZu z_d`)6Jo_E|VeqRzv7%pm6m@6)`w9PM^v<&P!9wFQ|6cs4?tWkH5A~DB=jh8{nC3@@ zVxkJBO4LAM(IpK5uVe>0!$nn+Ed4M|1wy4Tv(^?jBcyxvqd=>Ov-!q%ZtRLtF;VP# z#NeX&cKS9Ik#D#%3?#hnz26|FjqEQ>&&VpC%A^sjwPS<37qWh;$L?~Bj+^A6YKcKv ziA}MeI}jliC(o9k!PkOcl)%++K#D@2hMoTmx&AYnJ2*+awxd;$dZgXtHNdO0hOgF@ zdECX_L*=INp-q+ZR~%g^$h)4qPtQ6>{OU$Pwev>bs)s)`Yu=-jXIMWYT2D|KJ$h2C zu1ta;4_UL(w&62DQ(b=93)SJpI-X;Am&30R##NWeIWmdphMl?#K}X=CPqe~crAMEQ zW1#U!Ahmz1NqVthytbP**{8m_I)3<%lCglprS3Y)-{z5oh>ETxvH->jj^sIC@#4v7 z>tM0gxI^S;Mdie%3Em(2sdZ6A2HG1OW;#{BSQaeQ`&h-u7yj_HknM0ri@Vm zZ+)+41>sJuhIx^mbPIxEeHTaDK<>{uw!8}{!9T$5`PRM$#zxfY28ce@w1VKutOSxu z|7qNLr3b@EIa5ZSAjZ|bV$I*jsx|4f111R%=|wg*;=8j_^#^8;On4}PZk2dsZ{P0y z;4bL2Y{9;z?XzqNw!Z)R@_T0UdwNJ<3bdxIrhb_3JF3l%Mc~n&${(n5!FqxtEoBn0 z@BOt!XUrwKr}pB>xQ>F<&BCvf){wrI5|&#wEr`W%pj~Kfd7y)>%V_wfwZ7LD3NGmWh_bGf7l)3Cy_9cAH7bFMdAxH#;6jrX-UZvl76m{VhZxd zG{)qWjWrEria3Ht8u%u4_Y_YxA|xvG{w%pRfXi~8&GfsyvBw;PmCNhh&#LSVB#Y-} zk&c`n`nz5GRQ;_AM%C{HLdMr=iNS#SxkXcJ{&WsHkgYWWoiWSAR7wx{FI*pkyLaer zs-%*y;vRFXbp}3e|2B@yj27?@(0m{%7R$F9FZ)8eyOhuQeRh|`160c7{4x%CE~ibs zS0cp4A9#V~;n)MVG{KW+7zTU6erOr60te5xY$xari%PhqlFg7OZzAA2T)*#)!va~+ zKVdom=&8-9%OdITv*2>jY2F=L2E7MQnQG+9sIL&GMwdDd z^k%mA_JI9A+1-NX>O!D49f6~p8lQb;bv0sK7DYGy>z-HH<4fmF8D*gu8&q9<^wS$< z4^G_^JVv$++>xrt1zc#V;YK zaGA})IY**h2mo6h!-%T8ow5F)>98xGT>(1p{q)mA!)7m+Kxpe3C)n|sCOqMVtFjwx zZ?XsG=0YdpGH#q)CQd&9?Q$tIj%g9yn$yoz)0$2PL*bIMGI;c69$KDkB1mAGbg=fi zOQoPF?{daq*583D$xdi+_zNkx13-h5ip={>fW^WU$>{Yq*pwL(>4*cM_yxpvP}kj5 zfJ(mfB9iWXE1sI$O|lkp6TQsE_iS_TY|tI>Xe;ZWvX1MLVVOdZ+!(5L9%%Hg@>vVC zY2iJ%0CCv6yPr?Y!|(DvOpgLhz4>Jk__1}X(&ngnCc4R7G}d8U z4GYOkN)h}3+1+P0Ap6u(uIaA}Ey=MhaaEy6o~3uIpfe|hO!ZXE*H<+m z#r;b^`J=EfvG%pP62fJEx_RL>Ov}C1b9vh1@tdhn2X#K|WBZmJEnj4~nQ*id6bj>x zl9*J|{@%+;)cEyr?yluvmJKxu6#K>s_^k7=>mFxrCNAp5Uo8k%%$QI^L-J%9+a^*I z@w>;Is$YL{FCpRr2WePD^c^)MM)ya-Fq&>1)<=F*>qe0}XpSz9ll$aa`?myQWO&fW z)U?64%!pGV`ig8c{~H^8)jTSU%tdC#70j1onv(w`la z4c>)YNOG!)7h|jdSCV<*mWNagH8@S(vIA%Mm<4ZjU2WXC`H47uKSw)+(LubHt?!(s z(GY^uXqVZwpjix;hcOcJ@$ma%2${W*;n*p--0^Y!p^T<&Wzy(+b;c$;C60!G`?4ot zPE6e|8DJDf{MA6xp)QKw*Ra4SW&CM@FyKH~@^#J8kpiZ7^;0XfKJZ?h5L>HjCJ2Af z{lUF*z#4fsnQU`_2{w}aiR!lH9EqLG*Hhm4pt~x69Q|R&BYv%Ilec;|Pe{_ne?BTp zFg5SDQ!WjSN9ogQ023)AZ($+w<=@SnSNM!wW9@_Tq(Zlg5RyBjaT<3l6 zRrF?PnlN@^LLT{BOdP~u1_mz>dYH$QAPjN)5H`##P6%vFLu0xyH{Y}Gz99)~4qkj{ zicQn-7{UBl8mD0iN7jcyMB#%vKM_5DDFauHK11^%_AhdqJS8q1cKT^HCKUxOe_n#8 z-G7V0pqs1`eeSXBQ*zI62cAW> zDO?$s5M-|mP!T$#_n&fw32nZ#MWchVkQB{2PweFr>25s1D9i!KI*1@&Q+sZW( zI`R~MiR?acubpOv8r>D-t5V=I;@8r4L+HRRU~N3q7*3sYi&EHfxUVkGTCFz`@Q13? z>x1{m+u!xwcWwb0pf@Z3$vp?^m0={!eeNLQ=rKv=tu#2HA1&L_9bEFgtA}z|2 z>vO$wm@q0@D6IO!&CwP&s4Pr>ev5_Fn$_UONdqot_|ZX3xbWcCr1U!3^=Qz zC5o~GDT~e$FZ;ZfH#jCIceA3()wG2 z3GfnrYS-RbHJRv_>ne7c%0!-ZLZ05W7FhceT$r&3@juk-5=&0(>t^xzL2RLKNrBh; z4yPPJ-0=MzCTQTdsDS0)p&%sx_q;+nX!d8TTyIzz&5=u{!MQUS|Ba)d4k>f7fUxs~ z>%PJ$7(5lN9~Z(hKALF+v_Al+cQeI^@ao+!vrRfJ-Y4^(=$^-LoHD^68>?eCu4daA4Zx zQGu*9#s6>I5p(3>Z&__Sz3X))CGp>j;c~A4{btc%IV@RjSqyq9wRC70dCnF3N$s^H zpEB1o#~~(VAvX17nS^}#y6Q9Yl)%?=5PL#M)NiMxm`-muKnizM2hZo(d~&TP*-(QG zyUyfntnD2rk|s8e5BZYt}+^0ERJnYjy)0u@9f~o4)^r)tEJmtnf0LL zfQ}RP{WCmqg9SdfSP3C=v_ughkI4RoAst8HX{UY217~>u`G^cM!Q|{!cI4tH_U^B@ zOB3h78*8EKvQy8A+6UvPZ*_ezF+AOYbe)ABQmaeU(j%Kz;UW8J!p{P0@!VRU+c6R@ zTyGVn;C&(k1s0Qq7rE~WyVlQZ#^wfcO@@Xw;t}{i)SRZmT+=RwRaFQBNtGMsswg?d zn!A;1k_IYBo*2w)oaM=8g$#Ns_}w4pYRy{OSJ)K%L*Z-`$f8P;{$dgi{=<;Km9x^r z5>5^X>t|9i)M+@oZzZXvBhqDbg)*}dYqw36QPsqs{OWxN>G4Avs%Q@Mn#C$Ca;71F z;_=6i&L4FtKX}+MK9WLwIny9E0toP9laDGLWM7(#8{MO6OuX8m{e7mJU&V007-;}O zr|DOd#i5LjNF-UG;O+WPVl;)9n4pw2PCJ`8Q;c|9d&2gfa+(JiyjLHU z!kO@&)}>44Q`JPny}{#<9PQtxEAuEPZ&9~5ESxM+PhQ(&EP{~UTs4@|I?VWrn&T8( zhUZ=PW6e{%U6M77sXZzc+z+7Owa$@YEBa5m8@p0O6!Fu#%n)ZW?0nNk)ejAY?ED?M zmBlWl&+H)NLIqG}u37?CSJ47W(Kh?Jm6rniD!@ZHO1EoQ>&xu>aU@|U^QJAU^qR(< zoY@47S~vQClvQv(sFh60Yle{6MQqct-VNwGjacNd#01q=owVf>8TCHfXO>kAjZK{U zTBgo<@_fx!`7abRbN7<~=0O0}==Tjt0opcBY|U0TZk(J?$=HCgVZn1gjJDdJq$$8~ zD@DO6fB2J3N1OZ%3Qb0Ce|(2-<~96P*80RkkjfV_ePRqIm^hygamQIHAnS@_sWoG}T8w8^}BX~QMFt;C@ z1%o;*+>-kPG^ygI^j|9v3zI&kE}-rfZ)29%3Z&j>eOEgPH8>ha9#8{zU>W+DaV72Z zS&8a&2u0C5ltT5(eubOy(IopYh&&$pD-D;CAo^y=U@m}TRzoz~^{1j9Kv#dU;08}@85qxnF|1{5KmVG)$FB^) zHbwp#KKYVlCO;$5C;qrkDvwQTKK;l+{2MXTpM7Am%;v^MK#jcUe4vEg`)YJ5<-BV4 zh3jJDEw5_>X{}dtABWz)$b!XMK~1$jFY_0?rsafCa%0zf>Akh60M~17xzQ=5c7&5L zOY+pqbt~W?l9qo?C&RUgExgfQBO5HfiyXerL7`>JJi%Yh#qYZOo<6&;+=3 zyo!TRw7JJyfy5V%^H}uZbR%otjIEa6<){$ZP@l4qdF)_F;m50lhz1jDe3z0ki45ab zM){=({DZKwgUR!op(p?R{cj)TOFM0}lF8rHS_E>w^>qmOL|^qCpA&`iAj3nZ!`NN9 z=Mahho&?jE|J&nV&DA+NERrAZ>XJui;RJIT?WknUFBL~xKx01n@zwfW5OFaQU6`gK zdD);^RppcW9YO@5$N5mk#B8AwEy`^ZX5e&M9(X&*6>|6Nk>a+B&quO#t~Jqe50^VR zm&OW17>JBzdbZTf{MU>BPRv6`z?MLH%JqfW@tmU2X^4TtxM9|rH=E_XD{;iVAkjE0 z-+82CRu0sxhnLOGiEnwe)FaR^`o~*2p4YlXXus`4@0)Og=B^K8P^%zi6$j(J?e|VA zn4GmgiEeDE!w=HEn2v36AS1~2V_;qQ;;fZoDqG}oK6Pl>2Qxh*AbpL#Q9xsz*}4q$A|Wq7;< zY=qQ0lsY}`a;N7;W+7Wxp-X@!9ArE)%$kx;BCWNF9=KpJzWPZnvfeUIM6|x?AX0Sp z#Z3cz{yO|)E+DO-nc(TTv8fzRvF2^=nWBO3{b||ada9LrDw(It~8-ZMblm*`fq&6Vce;{qYYYAa#tH- zAB*=WV>=mify(!{#DpbF2RjqcQ^h|KN#Nzm{oNKZ&|r#))ggWQnZ6Y|LJIFIRlo3O zDJLOYV4Iy6UwA_LS zoAJF!Nyg>&H+Jq|gZ@7urw?UcUUR0sj8Ysz{;VWx-lS#PY!wNX-eJI`|u`0TfW z9$|)8_P*ttOj_*8M7B*e}45guCuww>Pxi~)vFOd`R0nrEy7}Z!;yVtx_Dy6g;a{Eh>!fj zg!x3!ilF`Tp7CrfFb6%_%7Sw~xPf&1zlH(^_YdzhPFkK+49IZf3;N0be8t%Z0JyP# z{{m$D5!1JD;va=^u44F-F-v(EoTrYsPM^bU;%$PG!hduL#*!@DzCvx^d}egm9ThHd zOdRoqgXfYFuOv#F5 zIWJHyUz`nx83fL@V4*b|q-i4Z6WtF+VJPN@4)^`1YzO)&XjRJ1kymSy7%yV;jaEQv@qV6Fnso_+dM*NY+P zt0h1akEj}#^kv&Teu#60DxQ3#N=Qvs-q?|S#G0$6vbM=eUS1}8=UZJ|9~|&fRVqgV zZ5@|ByXUSuCpa&5s^%&?E~lcoNR?NOUc^KHZb8;+ejX?NVM8lnNi`a-&mxRuJ(o5G z+uUAZrqB-C{1+sqcIpHt#ymnI8cTeD7}r_wx(h;sgZke|LWn3BGO4Z0B^eJVj6x}m zGRca5hQH^gB+qqXu4eX4m0E`!X71B0#$X&(nX^p1eZ^X?n{nvjP$`4`0s<(=s7g0T HehmE|fLvzq literal 7156 zcmVT>Os z`=V58Uqef5^CJ|gkW$K0s>;UAeCOQrl%WWP-lrSy-Fx4?F9}W4@Gb#a5vooAK&cb8 zBENUgzo+{W0IUG`N=pM^09a5%SyV~Pp!P>hLMKwb;2z`R5$Hsf zJP&F~bfT6-zS6R&jB}Er5iwD*L`B&W7ZppQpjZ+G*^(-#78^|{ZkCcfEG2n4O7d~E z^1 zN*jZ0dV4RM+SbEm7>Y1DB0(oHW0EAQ1SU|`!We)kF2MY`5CSa#0;m$`g-)I!LPZQM zB#zA$`Tdika5z*D=5n%anPACp?dDQjyV*>agD$&l)OjDPvZ4{uylgoeFLSR=iNc%V zqDxUVa$ScleOV8i-qpvaoK{kSPD%zkajA+VOcF3zfSq3SOGomu#m-O)XCa!}bc(7G2SWw>t5c$I zI8+o$ibmZs)a?K3pck1By40M@Cr zWPSWdvg>fD;NI28$8O&&%=);+Ni*ZFUxwD+y2cT{VTB{n%J~|A7e{jLe|{_7Eh`%F z(eBJ%;W{3lNLa&7J8Beh5pIWROjyT zynkcBmU{T+&XfHDb5YHQMj!xOKWI;0Gr*^wK9%wP)3H>~vuCq`Pi_>ZcP`^HmG5pg z+!-u%8<#}lQV6u6N}kaMP^*ddlaUhl?S0WfFGrRC>Y9!-8-ljC#eiK5G)ZV5S?7x0 zyvh;($7AW>L;GWcM`roxU6*;MNdg|!dOr0=I{-|h#mBi-0xi(TJVW!0)=VJ#{BX|m z=)pwa4TJW`XSaH0&S%iQ-QX;wdgm5*=!!lr`QYB@is=`_>|b2jG3v0Ac2qU5-s1su zgMA7uOf0R8fdqhV=md5`C(dXC00J7oJbEzE{lajr8O)Yc`Y*5ck5q<~GV~dE4nP?C z<7>1b05dp`iPH*!9ncBvgic}?bn=1#f(1#n{q^4Hz<9FE-*<(7P6LHd*FAG!I-F6zjb}^%yLhA6NRdQxcB++(1DqPM_a^| z6D5t_GoJT#aCEVqdr>5TU44A2Bx~AZhm!q6erq;prxoa9q7kq##~l^uvv;Z4XOfHD z&+u&x`_3>`jF$bM2NK=msWNx}wH?Ffv*{MY6GxMQ(PWu#`mMo}kyP==h zD9l~AoR56(<>>O+tjs`nE6sf~_t}R<=8DA#G+Z;oJu_@P9{~HW&SpKo8Oe2gd6$1A zXs65Vc4jgqS)V?g?OMt;xw1k(dNAJ8?mYqEuFJgB0|Fy{b5CTjAgd&F|J>{h7%r*1 za=am$5okk|I78+?_*l5e|F1*IzB{+LCpH9ac~t{3mMq)a>Bynkf0Dp0ne^gOHe0N{0zjC;ypC)lb=VamMH2HiZo@drTHvmseR4m_pIXZa53VY<{ zRr+kZZ&GyL`+RuK-U-pAY9RjUtyJKPzn&fX*w3am{mCz8);{z~Y*|8-8$Jdj@ls2V zZap$3)e_CimZe^&0eJjKvU4&eSvd=-e*GH%@Yz_&@x;+ouyV?LUs3J93r)1JQKxuR zLs90qo*RJm$48Q#ex5FUZmVa;xZtWjE}ba~lmC5xZ23PNrsRYu&xe{qS(OcCW%fX* z;J$B{e`HhL3>wPGZD}q5j3p(TpKpEa&6QQ^+k2w}Gg--Y`7%BQ09_7C`SaVoW8Z%z zHhAMod%T0EQR%A@S2O~$4I|r)G|*t&h@`kHuiX*8X zW}n{dp4mSsdPfpv8+23391J!B!Vc&h&7=MiHlJ8S;_*n4e{;4VJoLWKla0Rc$w-lF zbi5wJiBipZn>*RuPv1;osX?LDekR-Oy)&^A`?cRjhKjOE5r9UKz-KS@jNi0~hqT6T z6}NP;$sZj~_I&fbfnhWz&){T!rO&K^##C;sUD!<*5RV^8b?@rqV*~ES25ec;7Bh)V z=hbTc3J+7bVbDI;%C!b;G^u>20!zCFn7jSE8y!c;E&A}`O!q#e*0!&w%N_s_*-+~20aTO**mv*LP0y- zvS)GIM%Q%E-u&^E>#QQ4S@_5{!ZMeow5yMgJ#{J*FfK(M7pU?Cpq4-55Tg2 zQWRGEteM4vp?!j-wAyD~bdiRp2=%>#_GJBWD@~~1yQ%Z^Wj*YoT4HA?`R?uB(SNtv z8EbaVzDdz_C?vYAG^qf(w*LDy&Za+nJ+2wx6DOl3XHnKj=us$x2QxSGW4$p363`7r zW4X~_*$x1QLj~dHRgUvMW`DNRJ8|#x;nkUvy1+go2xv42?WaeRo%dYspQMS}D2K~R zDt~!Z$Jp-iTyoEt=ow3v`9wisXcF2o7b6aOnA~j}TwxDKH`FE#z>kk4JD)w14c>8y zd-8oN9q}XKqJ6c`DlWDpP>)TWY|cs%>g&^@^QwM6g@tR`Mpgv@Kr^B-HUNVF`T+D- zqz^#>$9MKc z`vGY8UE!aA9#9wuFi~lZ*CPOmh6eJeP53chQ~M_JLcg2Ix4M=gu%!IeD?7$D05fUH z>gQ?69K(C)eVwNt_+4cA7oVG5{f%oo&Mjt&5XAXc*{9Ny^{@9t26+o5|F@fir#x(9 z4P6Sh^>C?24<`Cl4HF~*3JZC$LFcS!jDc2@*i3>$p@MK}7n@q@mmmQ2xGbgm0H~cI zl?QJK4zKbuxqF|VTXQU2Y|E7cfc=ws=e^H|*R1wiv){QXc-d(pb2PS+U1}4+*Z$cI43d9Y{37xUI?vi zGP{eCM#evW{=1kU`D=YKv8UX@9r=_$hXv?{*wPRkoAX`L9C4Y82)lvUBc7{?uFyx3E zf4?-E6HdoU_DEh~d>mC?3_0Tjq5HVcoXL0r-JIHJ@|331lJ%9zybA!XTW(Lb z>O1>5x*Qi3V=^@7?wC0u4Fb)%dB$r?RFo|=NvN%43>bL&bS4;S5Fj7`TsLTs+`Zj9 zWgPqY$xLUWpfvQIJM47v14E9u2H=6;MV2256>9v@7lwgfpUdaIb$#HRogtOOp`z<_ ztmN#l=Zio5VD|~4GI;*vNV4nonSv*1r;Gn#y|8F_-V6^%mB$kELEd>zP|FiZqq;Ih z*9j396^mda+ln-A?O_uuJxso=Xynmw(KQk;+g}{XbqF?6`qUIf*$?A^zhVT{FZIqK;mcl8zMTHIQh)4+(%W2sB7E3laszBG{<5 z2;3C3Wo{XA#sT0{K=^N7kN3PVoC~}-lI!@yM)w?P8nbrva;a;U+Z#?)!-+Bn0PFqM z%!k(K2K1&*TQ0!UrI*I?zC=OqjKvNht@b_2B8M-xWqkkV5Z=n$w=(4 z&t*M($3-6id~%a8gC)YxQ{`+~wI~`0O8>DNaWqdLP&K&-<8`W_wylBnFxz`kQL>l` zeClMT>#38q6a7{FeDu#R_f1|sU{5`BCRdorO6)UdvYpSI$#yanp|18@Gap#xjBe{? zGsYJW*lFp;mG-%3&SX0uJ(%p5RgGBNVar_F#Ws!HG=q<$B~1eoqD)sNo_StOEw$*% zmW?KqqO8>?d$)x|L7Bf7M^iPn;h=}fx!Ky;afO!=0f1*n<&j&uPQ5xMI`@p_JSXN# z&b*}3$HGP7Shy%$*~i7cw97wXTzA_BVQ%-h=+BiDngDS7=A~;N0Em(v&EYKMqHZ7! z-BkgqEAX1}+RakZ$w;Z~kh>WtBUOXB(@`5q{@{>fuGts}=yu~3%eeFv%eb@#;KW>s z|34?wfkUB!`{hZ|`=^pN#a8@Xo*|W;y=?r2;ap(A&E(hk+H>oodD&v62$iq8oAbPw zTE!sHypU|P@UWDWEH*YXmI4jH-U-nI04uyqZYgWX5AKQd9}E>eyOwdWFJA7OBmk@r zSo2m|m^DlrMbm1WzRH*difAtl1OTF-^PEY!s#|Spf~$l}LciM7| zwC~CRd$Px6DSFxVEr>)xVSL=Y3p>vVYI#x(0?VjY|IF85xU3Z8w35=?HD}T&_jX^g3`n1^a5o;IEoy&T@{@b}B zmL`=QJ&nl&fV~rUttbFcl)^jnx-&nG2i;A-C@?~5-fo73` zYDm9ilA=kly_Ze>;EhDT$s%tXss`c*Z^XNIkLCS$ZS{;_*I<2QDY$KeFxP3POMmx9 zVjz^2Y@gZU2^sIDODc6bTH<*NsjTudB15)5hQBf?3d`M0&cn_JQOul}xtb?1kJ^$^ zPPTq#i+kqp-$?eFCnlOCv|Cm?W^do*4%d&vjc{P5AZ+VljUU&j zf->_`L?y+;SQ8-t48XJ&VruR8zZ6;SB{xS(-`*P? z4DgoHSFiGq*2e`6$Qp>B9!&;cofO@3dD&)Us`3meFLyKfJ1%k0tnf4@KAe-7Z~Qj8 zGMbmHa+3_#OS`$`*RS@EF7~}+qGI{X)6*Lsxw-53GQm=UUN$z4>Y9krjseh&Uspr5 z-nPE#(SBJEn||^{rW1Ph`gu`SHR6w-o>`x)#QI1A+6^o0b9Zg^HVr9_Cd;;m_QwV$ zQ__MAhkRM3-~2R(R)YdO6UE4`zjt$Bd8kit8>|o z9le~f3TV{bnK=<*DpE8PD4I?(svUCx@=hzMd|=2K{@-t=x|Jr6hZ!tg`-svcp*j|c zYCrN?thdoxax*-2D%*7~US`e5MiOP4+(M$}M5^5MNBY&-tT$UyEgxI&G9uRtxImRB z0JQ|Fm6OaD7$u|HN7p+e1zDqhc{)>*T~&|aM45f!ShB}%C1nDDg(TGX4>`ik#=T?V zB7b1Iup~1?Q8nVRL&=&HU(<}aD4b2gb7e&%{`Ew<>(({S*-8MxC{{FcVem8WW7p4c z#s!AVmyEJB>H6?mXXwcj>8=}B+7m3jFwFDPXioUS8;OA{`}x@Cw|S>S8HsV&D7hJV z0B}4~v^RTiF^nY3yoPEB!Ozo@m!nE?QL!#|-AWJB(2{uaM7o0_2<_Ij&S>S3xm>oM zlX=v;Q6d_U2fbue`{0lxZlg)%XK$qg=8H4{KRlS|`rFqN1Alahd-Cq>-YJrR*5k62 z79$S;h7*gAd(BXiRcgF0FZ+`lg+(Vhk|4DAue2}7tI3vB>KCW8!T+?uHACz6q6WRs zjYzEVa2olcMsacq)gt8-fFwl{+Q&DzW}ZEh4Gt%&W6=FCMF#gxh~5X@8#r_Gs_If? zD@YgGi}nDz$zlHc+U54tt!tg3Mz)@vA*Fsdoqu3YbY&%pavp#CM!eV0Qzg^vJl|(5 z(5W7gI5jk2^&qS1fglSZxTX%0<+|F-P7?RHOFdIIy4J(k+{xxP1lhEvX&ONgTDQYe*wM=-zjS%W_=i?Iqth9Q`NipM;N~Gm zi~#VD$5MekyD$UN-24bx%dQO>L*FYQ$H5Gq+OH zG<@fV;5m8;Zc;sdbvEn$`vZyorC#UbsM0^&+H<^}duB>1<+PH@-toNa;aB5>U%A3} zZd(s$teY4mUel&_8oJ@UAcwJ8D5;3*{sQI_`8a?CO%mGIuJVtjiYoK){`j(XI$>aY z51ZyKttCL#TvxT#mJFwrRL;gr>?5zo2X5Qonlh1B(@&Em(WLx!v9#P6s%7%#4~_xI zxonjDU#{{GAD9u{|NK^ZL25>;@Nl$z`(|P4{AX6t<)909T;gsyX|f(MQL#L@C%R&L z5107JI+ro)=Aie@OrX*KrU{*k5h$a&pMZIT2BR%3cQeI%cKAmB<#@X1hldhf?LNmh zt#l-I_Hr#vKD>pLzp&FcZfSGaI2SLo_dY*66tvUDdv?sHnG{glH*=pC-DBp0%UuC5 zN5e(?_g;=JU*WOlzjB3dj6Kg%U^6^>Cgc6_(R8noAYPC61Z?Shc6cY2Qe0!k-toNa zk=Nq`JA1jr-P^rWl>Q@JP`T?xDYyo}%w}cgn|mUIng)FB>W-1lC1iA&5y{Jz{ga|= zG*RYWnG(I74!XFdlTEGdu;sS(EPa98z!OJP!KY4Tx<0W{nEG(lSbj$et^wVI=|yer z@Pd;6f~=C?-52XW8!y>Ezs);dDQIpRcmCg*bst;joNaa2jz&nA6zXqYODsDcDLViB za^I*aOTl<-Bd8K+v|+SZ^51IBtpxoSF~-dLUDXr-ewHTf-?*k@^ogUX;P+pN4X*Go zxleC)&#bw?^@(;+k~Q*QPoz73c_tg|w9|#}-xxe)TGz>;*2bHwwBbq{LW>*mETw3< z2wk-eG}@BR2f*{mO+xs_mG=1KN0MD%{Y`lFmM%7V`(}4&nak34U0kbBHQ?E^S?`m_ z(_J)0XrH~*J#pRgn)qJ=wHxltK_}86Zx&0~fM=;y-+9!MXe`PbD^|vah@K9f#+P^b zCq@$GxgQ-)_I%;l(7IiHeC(P5KC!8j&5ajGT&}3`b1u;>vygZS2J$EkKkt?Z| zTh}^6x2|!{|<3eH!jMB!Mt zD9EZttn^y5ex53O=C`?{%YKfMyc{hRWR;5M6-&HkYdnP+OP0B4UbgiKOnz%Oo7&aK zrRwVV-qo9r&0mi;x4#3b8;6~YlTYz3n0u8(yH=#AiyXc$Hz8wS_`2S)X Date: Thu, 12 Apr 2018 10:43:36 +0200 Subject: [PATCH 333/786] added ChimeraTK logo --- doc/ChimeraTK_Logo_whitebg.png | Bin 0 -> 9997 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/ChimeraTK_Logo_whitebg.png diff --git a/doc/ChimeraTK_Logo_whitebg.png b/doc/ChimeraTK_Logo_whitebg.png new file mode 100644 index 0000000000000000000000000000000000000000..30b8adf52f9ea143966c34cea545bc904199a87e GIT binary patch literal 9997 zcmWlf1yCDp5QZVRyGzmH?vUbA+#QNbaSBC>ySuv=cP$z$xEBcS?zFh{&!3sgO>Q!m z$?bmoKKt$$rKTc>fl7)B0|SF0|5;iCdbaxiKt_bVYx%OxKu_>)lJZ)}(3dZ=WhC@7 zipytxH|S-G{|{Jwq$6(VMG|)zJ$FrKYj;nxZ&om#o}TPqo$TE#%v`M4oxj=STmng9 zV5nf^r6shyaxeUBOpO-j?%kX=>NZ&w+bn-QY*KL$Hv~c4$#9YB#LYpW*cyx|m_Y*Y zxSu~WA_8C~sUY}p>=0~8q=(IYX2qr-E5?pp7r9p>i*+8YUGoa*4o5G&M_9H!K3BhN zrn9nm{!AJL^H5aVIdHP3$PC1dW{i48(bDpbBfg*?CJO=3kO(<59q^FRj$K>lj#>JrX#lRw+=UG|lB5>!Au^ct9g(dXZ?f?5fkljSzYMg1oU4 zS+E;tr~OZ^>=cW*W}u{TtHn{#Bn%1p(?Ll83HEq2;s{JcODOIB^wX31YFr52*>Cjs zR4iH&4{!9>rv}_t&yvfc*tcKca?uc(U%i?_;hGGo+I(%m>3jRQopeJ5%Qa)Grd;|w zl2slYAnBmnh}K;cZFIpAosT*p8))Sg0c9`5%2 zcS5Q8;MMM}1GZD0!i$isdf)01N**YT{bWEn-=yPndad$ja^cf~F^xsq3sIQ;#-?NK z^GSZYW-x_Q9)|vvt4+Pfziy7BM(5wl&%!>it-XBUu>kug-W)cm*DJ1l2DE}{m*Mch zfcE9bmiT9)yRZn4+vfIxl&r6VM4p&Q;jceJWiC&HwIxtPU_%_s-vc29oWw56mxcER zX&x0M=d6>e&Y!Z*@^aNS?a34SV@y7YE(u>F4JXeo>J|uBZ8LNQW20QpGP3w{Xa-BA+!!zxmBs`!9ZosI9vUpHBKaq<1fKRV|w z#b!F+W`|-n8#A+qXS@1k&@&>1H+MgVAsj?#Bxz5C1%hzhaCQEwZgDUeNZCo zD7l*X5f^ya%)dAC>4}CJ4nN>i@pc>@vpcGh2GwpnHTO^JYse+P%U&ByJOX12eB*jS z`q<-w9t)wX4aU6{MVUtWA1J*BfiT`JmJ~N*M%RuX=T7h zn8bR+txoGiz4R(^;y^1o0-#o1A-m;tZW=>}pem^Sk4#PWiFms7HV-i!Knsy1{t?5$ zjjZF*{sVt&cJ;%d1h+p>8r*Q-(5n!{5YMP)`%)?_$XkuBY&R0`wc8M!#%B;~aE6YC zWt2#3p0Es(!$73NBtC1pw^ZmHQVyZ(u<1>!xkf=kE53IfpjD{BdUuyzw=*X2tW@D&r#|9&-7~&!eVZJqCkV?MR$U!zJ=o znYq&zsVO7+`ao+d6yy)g_LBH(8;ETk7`#mA%vLVQKc9&+z&p7PuuF_FXG_FiD%5U# z!-6$LkkSAJ0w>$ZoD6b-c?^2G^%gBLgAyOYEQ7m?vB@BZ20y06xG>k&}Zvy;4_%(7(G7A8t1`$qwc)Xr;a|dI4PEJ37?{ zsK_F|6$DwQnMri{k!DksWc99wbVZD9i@Q8D^fuCtHIyxa!c1PyvWHFeJtZcc9Ho*J z{V(G+gm?&oV4%M!CI2B0Vuktmr7<)@RzE3cJJ$8?qZsCka*o*!^7Qy24lgmcbg)4A zN>_KA0&G2adye>vaNYJTtoe}j9nw$B>kgaD+_!yo)@t~=tJ5yycuBAFgBoC#k*{vn zI&DND&t~0Rq1ykeqyisWbTr;C^z|hrgLoOyX&a+;?n2u#O&w$VUE$S zPtg@09>mU$r@8eT{&W}}e{9M%Vl3dKR^ENTyQMSyK{L2XP__Cge=99Qt)8>npwhg{ zaKz6?3bI}`Sk4oQ9?n)N~BkT%&{1)4hfz53+SfV*P=G_3L` z!0G4ph^&IA*+Nb&c*gusKmQ&-o1Hr=irzT>c=!VOi%$8SsH?#vonnE<%P4a>AiVdF zAIBI`^Plrg`mx>=J;t-MbKHm*N=nDZyT3oNG9nr(7ePL)!iZ}^)-S`#uBX4B+5WMay}n(#ffsLwU;5&c)6IppgoIPov6V#J?#`1e>_yH zKr{UxBiKSE0&u<7g}-*4R&YSs!WezTcKyzB1};~eXoz}3lT!rO8fDRNj)>OQsGo^Y zE;q=A+z6N{MN&IWUyaYhr;)~ZFi2?n+2}Q%VfsA+iUSgepm2c%_v@)mqf$5Oy9xeG z3AZ?x5VP0}@wHWRe31vHPQIyTb^Mwq;!XJ=s)zR^XsITWG+@Qo8c!uX zbv)R7o4=+$#tm;TG}$J@sjphegD!g0BZ7hw&hz5(+Wea(CUkuv6aS2UF4mU=!HoOi zON8{B9u{5R!qU{SC99RIb(@s;7Yz7@cP6dbYuhD^1TZIk#9asKwP6;fuL=J%tofgy zhEhZVv}o?e37+1U#Fa90xeK)|Mh*7*LEkA|V0sPd%*FA=I=IFEUq|-Z%)RaOf#2_h z854PDiNUE(#N5t#vo5SI272X8dp1^0qao`EU~>=gST%CIjf|-YoCTwv26{f~=sZfd zCqn%xPH-p{c3m6JtBiO|hlB&$nUT<(JKv60rpD^8u`Nk>#mxv1AKU0>m(c&RE+|sG3%j)3}@va1d+-hPv-& zX-P9m!ykBV=XU{VeI;>Rn5D^e!f@z5f$&5jH7LNb!Yh75h(>}Dj3tfpo2x`-mgnV>Qt)n4-Bt}Qa?Yd z5tOcP|T4z3T5*-pX8bl0{qD zsB%2Jmc;wqgTonno%pYti@4|MgE?}({|?1HyB~bt7yh?IA;i?nyM6l3kp+SOjiq~yq^u?tYQ54AN zAh1#s&nz%(@aY$Todx^mm>R(*;Bv5!Y}a#gVBqo%2MTU$@jRW`n9Bq)!66J{$p0Lt zr6EB~{>KmJeZR14+@c z37%g;a8%>lccWZ&9CG$T(-<<-NV=X-S&c$Rue~*2PajUu3;0je&jF z#uDfE;3{fB%CBV9bPAE?88){j8M3R4BiM_lR2r5((=Tb>QSI}l|Te9Pdn}Oy$a`u)c&&Y2{9;@_6jIAh8Dr2DE zNEh$OyYWuJ%!AGA@rwiatf8m2tEax8w^X1@gapxEXwfB6NzpJiyJhB&4Q>*Vm%h@`3bko8Fs&M@|Ki@1@`jTrI;-`y!hdQh{qRqNV8C#M4%s9g=_fe3N_umo; zHZO6wu;6F%{>=)Pu4E8*TQ2c6{ge8V;UkkR2}E zO%5D*fYJnZL#fulB3zm1PGp#?2R~L^l0J-hLlu}`x@nm_|4cz7D@N3$$o%Hsi!e@; z^P(SHBw`giWSUr3(EI$B*mX-DJ}47_zPCz)@-KHD$LI~(W(k?Nn2ID7o-c>htw`hu%m?LtUT)0UOUAn zKllICp$J>rJW%$MoE4g%tK#l9sotrQY*Za5vbID8uc79B-uK*y`9N;v{hT3n;Lp+i z{8L6QWPp=HH?XY0{Z9Z7B@mnYz1>KEfIx3FbWVl6QXJn75d%+M?4QeLi9i zEdlu|zVD>g0f&QDWUqR@;RR$WL<$$=1+-?_RNpKuU4#FOeW?%HqD0==Fkr*2r8wNa zuHGT^t=9tteXTHD6Vg!-DEga?9kc)YEIIdj#(lk)CvsWyGfGX)1*ip`GKT+&He!>n z`8;#QALv!4F|6vDh3005n9$lxzP+~C_<593qNv8pltE`3sxZ3Fs6+J0n9qz(5K@>R z`L`%p&(}IKBzZMPspfH)g~E3RroTu4^_T{h(2{LYilnQEytG08XsBT9ERf7g2V3De z;BeFpfr9|vlB0pNo#b^i;;tL%m_d`1_%4n~k^Ff(!>9xnM!-6@=z`x3aZo;3l!(?K zBeD?i0<7l)Tn%i*y>nn$RMqUR5?gd_1kXzt-CSG|1<*sXBtFMy7QI~-55f*wJ`t@6 z*NvIxC#;i7L9xeLi)((^+dTAA3y)i0I8Z;!;|_$0RLm(aMiK|u+Bi?x&;62V8{-Z3 zMMH`k_!vpIBrRnsq-q_+}Z2j!5SV`IxplkKqq(JU}p(itx@V?R&ZR ze`b7IN!iy-ap6ifR(C#lyUxnFIwplGqDim!HkY{XBm;1qm0EY^LQvnKah)|y6x=?! z;a>-KSeS8UhF(I^W_CS4XC?(DpVWC!TU|o2X&wXSagdlLTtslafe)q~d=2`H2Gzyu zbuI)J72662&nzK;Q8LHw!5DvO6ud4$bkdol?HsKnlbef|rTemta~j~`63yIAq}I+g z3ZI7`6szU}-wjHFNH6ylyC2e)uyFwzJa)L=?3@|HBNj`!*SpyQrQD2}e#NlZ^Fo&| z*l6#~07FXDLCP@|LxaiXWg3PMKQd{&#^efZXXjRJ^6r!%{L10JWcD=j_?C0CcCKNn zL{4K|bKaY^KAyoz4YAv>dIwthy19Ri4CNDdYnGVlck%G94)4ua;Grd9yO2lWMjM{Z zW~l_38b!O7T|Weftp*b@tk4XI?3N2I$#{v9!iJbBASUR3Tr(eCYNd`oi=pop9wuZm z)wY#eKTk~9oeTLX-kZj@eGDYJFSwG9iVK+zl*0o1gqgG-Ly`C&GFpC)?cmA^;F2}P zRiZA?h5^!W24vXGa+1rOe3xxqWm7K^0lH~Lv34Y>u%7^qkOhOJYSadfnRibQFVr`ZR79H;1lovkmPLT_SHEqOY7U3JY2qIz zWjKXGMjBy;b>;QgoR!;midEpdv1~-P1&E=l*)U=c8qWb6l!=EG{M0JRLfsTD+FAJc zsd%-dA2wG!XjDTbf%~yGdyHTHE|#eF@LU~m5%oaUn?e4)5pw478c<=3J5sFYotNSW zv#q*Y<(Z!t*x%qDfIRa-6EECXBQD{4*c`hk?p~LL4ffih;2xk9zmVL5LQ>wIV-kLn zXOyUCUqxn%Q60ZSgwCyAX5Mwj^u*vp;!qQ`oi-#@dL%lk#>Pv8sEGC*(-eQ(Q!YPW3@YxTRH0a0j-jYRG|S(ZX$i zDMUiJkb0nS&~3Exwj&>#t<_4zqNKDtAvg`PMEAC@suXmUc4@-)GJyO%CO7LA_#gPs}{ zjUl(P)qakStC!>r+{073kVjTx$Z|sY0V+hQaB*C5$Vp z<@T{LBeiAR`M}~r8LJLwXY3i(mR2!5aoD_OmuuOpaMq~p5PgqO)hEihojq9!G6Q!t z>pE^e6zA$b30#Ll_axI{-UUr_PTg0i-Eea(f$%}#l*x+R8(_o*`i%`$dw~}zual1K zR4JDxFVj%^Z;XC79i&VsUp0uOg9A-*MsF-&4M;aMJfObI+mXnPWLR_p>38xQlF?+r z$?;uRpUPN?i@FnAhJ>&Sq{8FyTpk{gZ20|lF}g#K_MDQi7H?B&{EoWX_Zw!G<0+MhS!=zM78_;s zU=02a^?z6PN7&A(B+ftEE4s!8wV?=%9|NbGng~IeJ7o!EcY^myp4dXo2Ht)>@lhwh zw!AeS(hbUbGu|r5&{xEbzBP-#h7&y>)4-)}gF-K3C)RVeJp(k|TzS;=LAf~#e<+!w zPCw#FIM*)*_}0)@<_BmR_%#{;P+1jjqu;sb_ zJvS8Iwl0^%j>{|iG&$$}V#(O*W?Ex&6uyJWUYUcDDf?a}$whMvO^9l1xIPlI{sXul z>7tvnRTgGNp=5Zu37v!NSP0~&n6NR?!C(VVd*w)Bq+Cn1KY5gJed~Bb5qXeULebv* zQW?IN7GH`34@xXGY&y@9LAElThd{jyHk=B4!K6}((J};@aX;(W%OzYAT%>M;GV>=C zg6dP=$|PXsI_#LrJEE6e28&eSceBMLNM4uw+BSZJ=MQ!(bT&&DtDVO{FPo+ z4Rgg|7VM+J;w%x=9x38D%HCz1x{k$1I6~W9aP&2`s>W0Ru0h}!ghhy3B43GS$~PbC zZ>mNR@G_&5@@bzy^P(gdn4f2=xFYjz?9FIK)xzStR4PTaPga;0t4m;h6)<06;(7Hv9|WV< zxIRNZGVVYmtWeW~AfNCuS)j3QIg)+w-|vOSjNuBR&z>oSfEwd3X3&bUUjMocqoi=8 zei=hBTBAfQOA+P@QElvy9@t{m;zZO!B9X3>QYKgvjP^OBDf+aB0SUlzfb^#4be{mehQHr<(!!6 zLZjYj7l04R!ufl74;hIw7{w0x0K3Ca6*F%D`7sma=P3JJ!48Z+N~oM=9Q><9tx}H$ z4>@0wFy+TQK9`}ua`)1|Lq)JU9H%(jRVOLESR+dG=2`QcmD8kQqLXZsZWwkP5`+_D zPjmg^!>P6(n1d*<*D02|k4o|QN03EU$othg0J)DvfFTP+S6m+Rd3D=S=O>KhB>Z|f zA)EjfKTma(OW_m@wIJms3Sr~9KgCR+P8t1122_GQ?ReL9m`AXW|F?pw)I$4}d|Yhn z(3H1S2Dd)0ny=61sT<~VH(x1a`-odQkVwd{IBck+7@J;?h@ z9UV(dY3hQtgz<`z=j@K2ynRtNT-AHnvPs+936ZVJOjZD7Tr_#Eh`fb8qH4 zjgVIAftDFGQ+WdL7iqHCEgmu3lW*r}XKHjE zZFhm3@?Y0Fz|IkTeZXoT!fEEA54K12xM+hmm1_4zM1T5jeVQFKIQY5dA!O@!h8iaUrc>%i{jzlqXjKH z-2dOl7#d)7drJ=93r(jAwaidzUN3A&SVohR;x}Vxg%1_Ulw~G@LuldG)_-IO|7e)P z_c%JO2cShHsbJ<9V8ptA^Ky*-G%stXoEJWV#iA!Lk;EytP{HXNUXh7*hu5c=!(s4<6xDkNf(*0Wr)w3kmwXBpZDryc2 z^SlgO1H=2@x`hX$zj^?Dlq9SIi$Mc2!l!=o6?-;PRzTo5Y0AWtM{nc>D(Ix_8vgNxj;u~~KI}LQm0)k)OKD>JM!qDz;0nduu)L`cD%dSCBVfYj zaC$j@3WC-iIjf1+mP{>gUBUBm;`<^2+0hUOaus-{mI-rDh30Y2la#jXakK?gsMkXv zMy+J=w{ZlE=}qH4l|7sK>=rHeaUy9yqWaXdM%;3`2K!}MO9ZvnI{0GKjN2&qt4j(B zJg1{!Qkv<`N3lcSlmAm0THf<1ormv*RJ;gFcOJvoe}@a3yT_r%RA?YdcNkO_PEHrO zg|$uxN;^^(2vt?YCUx{CyMjxmk=b8fT$}!G?lh&Io*pwTEuNvoXyFz8p5B@I>Y=2N z;5TcJsL*gzx~7Zf1kHg_%jF_TzG&6s*_-2p@IO(1FEm7W>HOEbFvtqEjsqeOxoW%-~q4xEeIbbzqvn|2bp1} z^dzHWonuLq{ERz3RU-$_M0~`wB}T^?Fkyxq#Sv`>zs9?^RiJZdRn#(^8d9qKeHl8l zR#)egz5%IvBB(waQ`Puk8*U)7Kd&ZyGg)SFg8F$EZn;payKF62zReOmgj=L+gRCCj z+ytMxq8am7WoDZTG%(ys;fhRWM(;*Lez)+s3(N_%DAollLe~8s^3~MJ9hm&plGWzr zGxSTg?#_`g^1J%%mK>X#lj~)}0CA{cnpDW@L*3r})j7Vq0wt^2YhKBmB{l~r?}Fgq zHH8CFCT)@Z5k2)o2CwJUTBBMAfnx}!K)XIeT|N|-om&2oy>tx zE)@m_(f|Lw0D8X{*L1qvjaw$>`NJ7Ef1Imz&($NV{a<=Q9Lb%SlDGtP11E8ym^Xyg zhUoH;Z<-sy$D(&7W)?#k-*t+1BaBPGW)#fgUM8PK zm$FZYR|VrQJ7I#@vG+&GPYK|%aQnv@58n@U;8ZEK$#UQvJ>3QFFD;Ou37<`1^kWz? zz|bA7LO%3ScL3dZssf=#2a^+ z$XZTdtWr8oh92`#KUr6sj}e{7|3Yf)2S+YV|2uz5t@BRMfu@ML3uTd8gK^{72aA$~M~qk1wm@BmHBJuI$S9GozhhI3wHbHhLg-)Zt0*SQ zS6tQeA$zaiopFrA1$Z<;v?VD=*P{0G7M)*0UH_O0oy2TFTTgQ+I79z|hj%RU2-K2j zi>$8W*-plI4>TF6ceQ$M(*oz+a* zIR+3_1zY0_8q*Q)?5m!kT|qZPJlb>#@Y1HJ8n_L@tfx?Qia^yqh<#JIWzq36nuaWb zBl|nVI~aN^7YnVT3pNqJDNaFS+s)dEYPs+wC7)$wrSseLmMvHyOo!WA{;S--7q=z^ zqePbN$O3bfY5CUSE6v;t7U@t8G6jFrw~Kddes=|n3G(YDcb|-?hMq5QLl!9q0S9n~c*DeXU81QTrH*&6iM8}%_Q0uC( z@BnTK!UZaI$S@9gt3tx=j#@* zXeWEhAKy?YR&g(hBZuiQflL`LKt3o-v9Im(0N!3rPkv_K2i=uVK2mpa)PBr$lU0{f zd@4@dx-AEmYq9lEDP{U)e>~P>Q6{ENQ0moT)XxVe1D!Vlb&F_}Ih%v@FK(K%v8A%b zRjNK7jFm*+%m%5+&>mQu231$KFAF^p^tP>}DL>7ylTpPP@b@$4g=etldK{y7csi@S z$(421ODy*;YLd;9`0<3ejJurq$da0^a;Zoz3tNFu&CER|I_h>GRNTsteBNo zYjEe~Io9{ZzVUyxOHulSO}_rUg4;KZIxkt*(~q0QU(7lzY&akFQ*@S#n^;;Vcsz11 zx$Y>jIO|MnUn1bdu6ft{1S)G^33~-q_Qz@++Pal zddNVu@MLFCyJ*iCPk*O+y(55@l>osCTW76mi5zjt`oEo&{WRYcRs##3_4OzAgK{4t zfEEvvqxj8GlZrK}W1<``H4CMai23(yxpF5Fe8TUgR#rt>mt&6Hr7}DHl^-hcVLsUK z7WHI|D}9=D(SCV^DhN>V`ckWPcd%G>iaQqHNkw_@UmA7Vm9g{J4&>HYPx>ytX|bK7{!)Slp5>+GmF4 zYK=@!%2HK>P3{&`yM92GIfY}1<>q238*chxZ#RsldIg~yH1J)>D=yoaMfk8>kgyPH z$v(5!UvACYMRKc>!Z#!&ARQ|R{#NbsIlHR_XECHsdj+}@F9UOt#9BcE%1vm*sEIUT z+!>N)3nVEB0M5W=Mi34$uAHPWLNki(PS7d#@Vmh<#1^Kj$U3!vIb_4|^+O}6@9F&l S2p76_4Mtu@MY>MXH26Q$J1wRF literal 0 HcmV?d00001 From 8531c686c91f962e5ba54f9ab7c8a55d266bfdf0 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 23 Apr 2018 11:24:23 +0200 Subject: [PATCH 334/786] also export the PREFIX variable --- cmake/add_dependency.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/add_dependency.cmake b/cmake/add_dependency.cmake index eef63ea..06ce179 100644 --- a/cmake/add_dependency.cmake +++ b/cmake/add_dependency.cmake @@ -47,6 +47,6 @@ FUNCTION(add_dependency dependency_project_name required_version) SET(${dependency_project_name}_FOUND ${${dependency_project_name}_FOUND} PARENT_SCOPE) SET(${dependency_project_name}_VERSION ${${dependency_project_name}_VERSION} PARENT_SCOPE) SET(${dependency_project_name}_INCLUDE_DIRS ${${dependency_project_name}_INCLUDE_DIRS} PARENT_SCOPE) - + SET(${dependency_project_name}_PREFIX ${${dependency_project_name}_PREFIX} PARENT_SCOPE) ENDFUNCTION(add_dependency) From 71689aeeba977c4025df81afd87dcf6892590a54 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 25 Apr 2018 15:30:52 +0200 Subject: [PATCH 335/786] added CMakeLists.txt.user to gitignore (project file for Qt Creator) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index adf444b..e65f458 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ build .kdev4 *.kdev4 .vscode +CMakeLists.txt.user #files ending with ~ are emacs backups *~ #files ending with .swp might show up when using vi From cb7c654a56a6401dbd65c8d12de2d08cffd2abf8 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 7 May 2018 10:29:23 +0200 Subject: [PATCH 336/786] add enable_latest_cxx_support.cmake and added a comment to enable_cxx11_support.cmake to no longer use is. --- cmake/enable_cxx11_support.cmake | 4 +++ cmake/enable_latest_cxx_support.cmake | 39 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 cmake/enable_latest_cxx_support.cmake diff --git a/cmake/enable_cxx11_support.cmake b/cmake/enable_cxx11_support.cmake index 848fac3..b5f87dd 100644 --- a/cmake/enable_cxx11_support.cmake +++ b/cmake/enable_cxx11_support.cmake @@ -1,6 +1,10 @@ ####################################################################################################################### # enable_cxx11_support.cmake # +# NOTE: Do not use this routine! Use enable_latest_css_support.cmake instead, even if you only need C++11, as the +# compiler always uses the strictest ("oldest") specified standard if mulitple flags are given. This will create +# problems when combining multiple projects (libraries...) requiring different C++ standards. +# # Enable C++-11 support by selecting the appropriate compiler flag. The flag will be appended to all of the following # cmake variables: # - CMAKE_CXX_FLAGS diff --git a/cmake/enable_latest_cxx_support.cmake b/cmake/enable_latest_cxx_support.cmake new file mode 100644 index 0000000..0b9b6a8 --- /dev/null +++ b/cmake/enable_latest_cxx_support.cmake @@ -0,0 +1,39 @@ +####################################################################################################################### +# enable_latest_cxx_support.cmake +# +# Enable the latest C++ standard supported by the compiler. by selecting the appropriate compiler flag. The flag will +# be appended to all of the following cmake variables: +# - CMAKE_CXX_FLAGS +# - ${PROJECT_NAME}_CXX_FLAGS +# - ${PROJECT_NAME}_CMAKE_CXX_FLAGS +# +####################################################################################################################### + +####################################################################################################################### +# +# IMPORTANT NOTE: +# +# DO NOT MODIFY THIS FILE inside a project. Instead update the project-template repository and pull the change from +# there. Make sure to keep the file generic, since it will be used by other projects, too. +# +# If you have modified this file inside a project despite this warning, make sure to cherry-pick all your changes +# into the project-template repository immediately. +# +####################################################################################################################### + +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) +CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) +CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) +CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) +if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++11") + set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++11") +elseif(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++0x") + set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++0x") +else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") +endif() From db66171e7cbe106f9b614dbf80fd7bbe76d37ffb Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 7 May 2018 10:34:16 +0200 Subject: [PATCH 337/786] fixed not setting anything newer than c++11 --- cmake/enable_latest_cxx_support.cmake | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/enable_latest_cxx_support.cmake b/cmake/enable_latest_cxx_support.cmake index 0b9b6a8..21d0151 100644 --- a/cmake/enable_latest_cxx_support.cmake +++ b/cmake/enable_latest_cxx_support.cmake @@ -26,7 +26,15 @@ CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) -if(COMPILER_SUPPORTS_CXX11) +if(COMPILER_SUPPORTS_CXX17) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") + set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++17") + set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++17") +elseif(COMPILER_SUPPORTS_CXX14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++14") + set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++14") +elseif(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} -std=c++11") set(${PROJECT_NAME}_CMAKE_CXX_FLAGS "${${PROJECT_NAME}_CMAKE_CXX_FLAGS} -std=c++11") From d2a51a526510882a2a597cabdc9b9a397479f6e9 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 29 May 2018 18:18:41 +0200 Subject: [PATCH 338/786] use cppext::future_queue internally for transferring the data and make the UnidirectionalProcessArray compatible with the new implementation of readAsync() and readAny in DeviceAccess. --- .../UnidirectionalProcessArray.h | 383 +++++------------- 1 file changed, 95 insertions(+), 288 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 3663cb5..a4bae3d 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -90,11 +90,11 @@ namespace ChimeraTK { UnidirectionalProcessArray::SharedPtr receiver); TimeStamp getTimeStamp() const override { - return _currentIndex->timeStamp; + return _localBuffer._timeStamp; } ChimeraTK::VersionNumber getVersionNumber() const override { - return _currentIndex->_versionNumber; + return _localBuffer._versionNumber; } void doReadTransfer() override; @@ -162,55 +162,47 @@ namespace ChimeraTK { * same variable but different for any other process variable within the same process. The unique ID will not be * persistent accross executions of the process. */ size_t getUniqueId() const override { - return reinterpret_cast(_sharedState.get()); // use pointer address of the shared state + // use pointer address of receiver end of the variable + if(_receiver) { + return reinterpret_cast(_receiver.get()); + } + else { + return reinterpret_cast(this); + } } private: /** - * Type for the individual buffers. Each buffer stores a vector (wrapped in a Boost scoped pointer) and a time - * stamp. It extends the TransferFuture::Data type (which provides the version number) so it can be transported - * by a TransferFuture. + * Type for the individual buffers. Each buffer stores a vector, the version number and the time stamp. The type + * is swappable, i.e. std::swap will be overloaded for it. This helps to avoid unnecessary memory allocations when + * transported in a cppext::future_queue. */ - struct Buffer : public TransferFuture::Data { + struct Buffer { - /** - * Default constructor. Has to be defined explicitly because we delete our copy constructor. - */ - Buffer() - : TransferFuture::Data({}) - {} + /** The actual data contained in this buffer. */ + std::vector _value; - /** - * Delete all copy and move constructors and assignment operators. In C++11, elements of a std::vector no longer - * have to be copy-constructable if the size of the vector is fixed and specified in the constructor. - */ - Buffer(const Buffer &other) = delete; - Buffer& operator=(const Buffer &other) = delete; - Buffer(Buffer &&other) = delete; - Buffer& operator=(Buffer &&other) = delete; + /** Version number of this data */ + ChimeraTK::VersionNumber _versionNumber; - /** The actual data contained in this buffer. */ - std::vector value; + /** Time stamp associated with this buffer */ + TimeStamp _timeStamp; - /** Flag whether this buffer currently contains valid data. This flag is used only for the atomic triple buffer - * and not set for buffers on the queues (since the queue the buffer is in already tells whether it contains - * valid data or not). */ - bool _isValid{false}; + /** Swap the content of this Buffer with another */ + void swap(Buffer &other) { - /** - * Extra version number used to sort the values within the process variable (so this version is not globally - * valid). This number is needed in addition to the globally valid _versionNumber defined in - * TransferFuture::Data, because the globally valid version numebr can be controlled by the application and - * values with an older version number might be written to this process variable later. That later data with - * the older version number shall still be considered as last data e.g. in the context of readLatest(), i.e. - * whatever has been written to the process variable last should be read with readLatest(), even if it has - * the older version number. - */ - uint64_t _internalVersionNumber; + _value.swap(other._value); - /** Time stamp associated with this buffer */ - TimeStamp timeStamp; + ChimeraTK::VersionNumber tempVersion = _versionNumber; + _versionNumber = other._versionNumber; + other._versionNumber = tempVersion; + + TimeStamp tempTime = _timeStamp; + _timeStamp = other._timeStamp; + other._timeStamp = tempTime; + + } }; @@ -225,76 +217,42 @@ namespace ChimeraTK { */ bool _maySendDestructively; - /** - * Last internal version number written. See Buffer::_internalVersionNumber for more details. - */ - uint64_t _lastInternalVersionNumber{0}; - /** * The state shared between the sender and the receiver */ struct SharedState { - SharedState(size_t numberOfBuffers) - : _buffers(numberOfBuffers + 2 + 3), // two additional buffers owned by sender and receiver, 3 more additional - // buffers for the atomic triple buffer - _tripleBufferIndex(&(_buffers[3])), - _fullBufferQueue(numberOfBuffers+1), - _emptyBufferQueue(numberOfBuffers) - {} - - /** - * Buffers that hold the actual values. - */ - std::vector _buffers; - - /** - * Special atomic triple buffer to be filled when the queue runs over - * (i.e. all normal buffers are full). After construction, buffer 2 is - * owned by the sender, 3 by the shared state and 4 by the receiver. A - * buffer is considered containing valid data if its _isValid flag is - * set. The buffer's default constructor sets _isValid to false. - */ - std::atomic _tripleBufferIndex; + SharedState(size_t numberOfBuffers, size_t bufferLength) + : _queue(numberOfBuffers) + { + // fill the internal buffers of the queue + for(size_t i=0; i _fullBufferQueue; + // Create copy of shared state. Sice the future_queue itself already supports sharing and we have nothing else + // in our shared state, we do not need to store our share state as a pointer but we can "copy" it and the copies + // will stay linked. + SharedState(const SharedState &other) + : _queue(other._queue) {} /** - * Queue holding the empty buffers. Those are the buffers that have been - * returned to the sender by the receiver. + * Queue of buffers transporting the actual values */ - boost::lockfree::spsc_queue _emptyBufferQueue; - - /** - * The promise corresponding to the unfulfilled future in the _fullBufferQueue. - * This promise is basically only used by the sender. - */ - TransferFuture::PromiseType _notificationPromise; + cppext::future_queue _queue; }; - boost::shared_ptr _sharedState; + SharedState _sharedState; /** - * Index into the _buffers array that is currently owned by this instance - * and used for read and (possibly) write operations. - */ - Buffer *_currentIndex; - - /** - * Index into the _buffers array that is currently owned by this instance. - * The _tripleBufferIndex is different from the _currentIndex because it is - * only used as a fallback when a value cannot be transferred through the - * _fullBufferQueue because the _emptyBufferQueue is empty. - */ - Buffer *_tripleBufferIndex; + * Local buffer of this end (receiving or sending) of the process variable + */ + Buffer _localBuffer; /** * Pointer to the receiver associated with this sender. This field is only @@ -464,16 +422,13 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - UnidirectionalProcessArray::UnidirectionalProcessArray( - typename ProcessArray::InstanceType instanceType, - const mtca4u::RegisterPath& name, const std::string &unit, - const std::string &description, const std::vector& initialValue, - std::size_t numberOfBuffers) : - ProcessArray(instanceType, name, unit, description), _vectorSize( - initialValue.size()), _maySendDestructively(false), _sharedState( - boost::make_shared(numberOfBuffers)), _currentIndex( - &(_sharedState->_buffers[0])), _tripleBufferIndex( - &(_sharedState->_buffers[4])) + UnidirectionalProcessArray::UnidirectionalProcessArray( typename ProcessArray::InstanceType instanceType, + const mtca4u::RegisterPath& name, const std::string &unit, const std::string &description, + const std::vector& initialValue, std::size_t numberOfBuffers ) + : ProcessArray(instanceType, name, unit, description), + _vectorSize(initialValue.size()), + _maySendDestructively(false), + _sharedState(numberOfBuffers, initialValue.size()) { mtca4u::ExperimentalFeatures::enable(); // allocate and initialise buffer of the base class @@ -491,40 +446,23 @@ namespace ChimeraTK { } // We have to limit the number of buffers because we cannot allocate // more buffers than can be represented by size_t and the total number - // of buffers is the specified number plus two. These extra two buffers - // are needed because the sender and the receiver each hold one buffer - // at all times and thus only the rest remains for the queue. We use - // this definition of the number of buffers to keep it consistent with - // what ProcessScalarImpl uses. - if (numberOfBuffers > (std::numeric_limits::max() - (2+3))) { + // of buffers is the specified number plus one. This extra buffer is + // needed internally by the future_queue. + if (numberOfBuffers > (std::numeric_limits::max() - 1)) { throw std::invalid_argument("The number of buffers is too large."); } - // We have to initialize the buffers by copying in the initial vectors. - for (auto &i : _sharedState->_buffers) { - i.value = initialValue; - } - // The buffer with the index 0 is assigned to the receiver and the buffer with the index 1 is assigned to the - // sender. Indices 2-4 are assigned to the atomic triple buffer (for detailed assignment see definition of - // atomic triple buffer in the shared state). All other buffers have to be added to the empty-buffer queue. - for (std::size_t i = 2+3; i < _sharedState->_buffers.size(); ++i) { - _sharedState->_emptyBufferQueue.push(&(_sharedState->_buffers[i])); - } } /*********************************************************************************************************************/ template - UnidirectionalProcessArray::UnidirectionalProcessArray( - typename ProcessArray::InstanceType instanceType, + UnidirectionalProcessArray::UnidirectionalProcessArray( typename ProcessArray::InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener, - UnidirectionalProcessArray::SharedPtr receiver) + ProcessVariableListener::SharedPtr sendNotificationListener, UnidirectionalProcessArray::SharedPtr receiver ) : ProcessArray(instanceType, receiver->getName(), receiver->getUnit(), receiver->getDescription()), _vectorSize(receiver->_vectorSize), _maySendDestructively(maySendDestructively), _sharedState(receiver->_sharedState), - _currentIndex(&(_sharedState->_buffers[1])), - _tripleBufferIndex(&(_sharedState->_buffers[2])), _receiver(receiver), _timeStampSource(timeStampSource), _sendNotificationListener(sendNotificationListener) @@ -545,8 +483,6 @@ namespace ChimeraTK { // allocate and initialise buffer of the base class mtca4u::NDRegisterAccessor::buffer_2D.resize(1); mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; - // put future into the notification queue - _sharedState->_fullBufferQueue.push(_sharedState->_notificationPromise.get_future()); } /*********************************************************************************************************************/ @@ -554,9 +490,7 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::doReadTransfer() { - // Obtain future and wait until transfer is complete. Do not yet call postRead(), so do not call - // TransferFuture::wait(). - mtca4u::TransferElement::readAsync().getBoostFuture().wait(); + _sharedState._queue.wait(); boost::this_thread::interruption_point(); /// @todo probably redundant } @@ -565,7 +499,7 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferNonBlocking() { - bool ret = mtca4u::TransferElement::readAsync().hasNewData(); + bool ret = !(_sharedState._queue.empty()); return ret; } @@ -574,47 +508,18 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferLatest() { - // As long as there is more than one valid element on the queue, discard it. - // Due to our implementation there is always one unfulfilled future in the queue, so - // we must pop until there are two elements left in order not to flush out the newest valid value. - auto theFuture = mtca4u::TransferElement::readAsync().getBoostFuture(); - while( theFuture.wait_for(boost::chrono::duration(0)) != boost::future_status::timeout /// @todo probably redundant check - && _sharedState->_fullBufferQueue.read_available() > 2 ) { - // discard data by moving the buffer to the empty buffer queue - TransferFuture::Data *discardedBuffer = theFuture.get(); - if(discardedBuffer != _tripleBufferIndex) { // buffer is coming from fullBufferQueue - _sharedState->_fullBufferQueue.pop(); - mtca4u::TransferElement::hasActiveFuture = false; - _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); // static cast is ok, we never put something else into the queue - } - else { // buffer is coming from triple buffer - _tripleBufferIndex->_isValid = false; - } - theFuture = mtca4u::TransferElement::readAsync().getBoostFuture(); - } - // Check whether data is present in the atomic triple buffer. If it is newer, we also need to discard the last - // valid element in the queue. If it is older, we need to discard the data in the triple buffer. - // First update the triple buffer if it is not valid on our side - if(!(_tripleBufferIndex->_isValid)) { - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - } - // if it now contains a valid element, check if it is newer - if(_tripleBufferIndex->_isValid) { - uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; - uint64_t queueVersion = static_cast(theFuture.get())->_internalVersionNumber; - if(theFuture.get() == _tripleBufferIndex) { // the future is pointing us to the triple buffer (queue was empty, latest data on triple buffer) - // don't invalidate any data - } - else if(tripleBufferVersion > queueVersion) { - TransferFuture::Data *discardedBuffer = theFuture.get(); - _sharedState->_fullBufferQueue.pop(); - _sharedState->_emptyBufferQueue.push(static_cast(discardedBuffer)); - } - else { - _tripleBufferIndex->_isValid = false; - } + if (!this->isReadable()) { + throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } - return this->doReadTransferNonBlocking(); + + // flag if at least one of the pops was successfull + bool receivedData = false; + + // pop elements from the queue until it is empty + while(_sharedState._queue.pop(_localBuffer)) receivedData = true; + + // return if we got new data + return receivedData; } /*********************************************************************************************************************/ @@ -626,27 +531,10 @@ namespace ChimeraTK { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); } - // Obtain future from queue but do not pop it yet, since we need to determine first whether to use it nor not. - // The future will only be popped from the queue in postRead(). This makes sure that subsequent calls to this - // function even if the future has not yet been used still result in the correct behaviour. - assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic - TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); - - // if the future blocks, check for data in the tripple buffer - auto status = future.wait_for(boost::chrono::duration(0)); - if(status == boost::future_status::timeout) { - // if local buffer of triple buffer is invalid, swap with shared buffer first - if(!(_tripleBufferIndex->_isValid)) { - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - } - // if now the local buffer contains valid data, use that data first - if(_tripleBufferIndex->_isValid) { - future = boost::make_ready_future(static_cast(_tripleBufferIndex)).share(); - } - } - // return the future - return TransferFuture(future, this); + return TransferFuture(_sharedState._queue.template then([this] (Buffer &b) { + std::swap( _localBuffer, b ); + }, std::launch::deferred), this); } @@ -659,70 +547,13 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. - if(_currentIndex->value.size() != _vectorSize) { + if(_localBuffer._value.size() != _vectorSize) { throw std::runtime_error("Cannot run read operation because the size of the vector belonging to the current" " buffer has been modified."); } - // If local buffer of triple buffer is invalid, swap with shared buffer first - if(!(_tripleBufferIndex->_isValid)) { - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - } - - // Determine version number of data in the triple buffer - bool tripleBufferHasData = _tripleBufferIndex->_isValid; - - // Determine version number of data in the queue - assert(_sharedState->_fullBufferQueue.read_available() > 0); // guaranteed by our logic - TransferFuture::PlainFutureType future = _sharedState->_fullBufferQueue.front(); - auto status = future.wait_for(boost::chrono::duration(0)); - bool queueHasData = (status != boost::future_status::timeout); - - // If postRead() has been called, there should be new data somewhere - assert(queueHasData || tripleBufferHasData); - - // Determine whether to use the data from the queue or from the triple buffer. If in both places data is present, - // use the older data (by the version number). - bool useTripleBuffer; - if(queueHasData && !tripleBufferHasData) { - // only data in queue present - useTripleBuffer = false; - } - else if(!queueHasData && tripleBufferHasData) { - // only data in atomic triple buffer present - useTripleBuffer = true; - } - else { // queueHasData && tripleBufferHasData -> see the assert above - uint64_t tripleBufferVersion = _tripleBufferIndex->_internalVersionNumber; - uint64_t queueVersion = static_cast(future.get())->_internalVersionNumber; - if(tripleBufferVersion < queueVersion) { - useTripleBuffer = true; - } - else { - useTripleBuffer = false; - } - } - - // perform the determined action - if(!useTripleBuffer) { - // remove future from queue, since we only obtained it through front() so far - _sharedState->_fullBufferQueue.pop(); - // get buffer from future - TransferFuture::Data *nextBuffer = future.get(); - // put old buffer back on empty buffer queue and update current index - _sharedState->_emptyBufferQueue.push(_currentIndex); - _currentIndex = static_cast(nextBuffer); // static_cast should be ok, since we control the queue - } - else { - Buffer *nextBuffer = _tripleBufferIndex; - // put old buffer back into triple buffer (after invalidating int) and update current index - _currentIndex->_isValid = false; - _tripleBufferIndex = _currentIndex; - _currentIndex = nextBuffer; - } - // swap data out of the queue buffer - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _currentIndex->value ); + mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _localBuffer._value ); } @@ -790,6 +621,7 @@ namespace ChimeraTK { throw std::logic_error( "Send operation is only allowed for a sender process variable. Variable name: "+this->getName()); } + // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. @@ -798,6 +630,7 @@ namespace ChimeraTK { "Cannot run receive operation because the size of the vector belonging to the current buffer has been " "modified. Variable name: "+this->getName()); } + // A version should never be send with a version number that is equal to or // even less than the last version number used. Such an attempt indicates // that there is a problem in the logic attempting the write operation. @@ -813,53 +646,27 @@ namespace ChimeraTK { } // Before sending the value, we have to set the associated time-stamp. - _currentIndex->timeStamp = newTimeStamp; + _localBuffer._timeStamp = newTimeStamp; // set the version numbers - _currentIndex->_versionNumber = newVersionNumber; - _currentIndex->_internalVersionNumber = _lastInternalVersionNumber; - ++_lastInternalVersionNumber; - - // flag current data as valid - _currentIndex->_isValid = true; + _localBuffer._versionNumber = newVersionNumber; // set the data by copying or swapping if(shouldCopy) { - _currentIndex->value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + _localBuffer._value = mtca4u::NDRegisterAccessor::buffer_2D[0]; } else { - _currentIndex->value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); - } - // send the data to the queue by first obtaining a new empty buffer from the empty buffer queue - Buffer *nextIndex; - bool lostData = false; - if(_sharedState->_emptyBufferQueue.pop(nextIndex)) { - // Create a new promise first and push its future to the full buffer queue before fulfilling the current promise. - // This makes sure that always at least one unfulfilled future is available for the receier to wait on. - TransferFuture::PromiseType nextPromise; - _sharedState->_fullBufferQueue.push(nextPromise.get_future()); - _sharedState->_notificationPromise.set_value(_currentIndex); - _sharedState->_notificationPromise = std::move(nextPromise); - } - else { - // No empty buffer available: write to special atomic triple buffer - nextIndex = _tripleBufferIndex; - _tripleBufferIndex = _currentIndex; - _tripleBufferIndex = _sharedState->_tripleBufferIndex.exchange(_tripleBufferIndex); - // if the buffer swapped out of the shared state contained valid data, we have now lost that data - if(_tripleBufferIndex->_isValid) { - lostData = true; - // mark as invalid so we can always just swap it with the shared state - _tripleBufferIndex->_isValid = false; - } + _localBuffer._value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); } + // send the data to the queue + bool lostData = _sharedState._queue.push_overwrite(_localBuffer); + // change current index to the new empty buffer if(shouldCopy) { - nextIndex->timeStamp = newTimeStamp; - nextIndex->_versionNumber = newVersionNumber; + _localBuffer._timeStamp = newTimeStamp; + _localBuffer._versionNumber = newVersionNumber; } - _currentIndex = nextIndex; // notify send notification listener, if present if(_sendNotificationListener) { From 56d2659659fb6914db580fec20593e6001401e93 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 30 May 2018 18:34:51 +0200 Subject: [PATCH 339/786] - Fixed some bugs - Changed the default number of buffers to 3, since the tests expect this. The old implementation counted the number of buffers in a strange way. --- .../UnidirectionalProcessArray.h | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index a4bae3d..c89256a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -175,11 +175,30 @@ namespace ChimeraTK { /** * Type for the individual buffers. Each buffer stores a vector, the version number and the time stamp. The type - * is swappable, i.e. std::swap will be overloaded for it. This helps to avoid unnecessary memory allocations when - * transported in a cppext::future_queue. + * is swappable by the default implemenation of std::swap since both the move constructor and the move assignment + * operator are implemented. This helps to avoid unnecessary memory allocations when transported in a + * cppext::future_queue. */ struct Buffer { + Buffer(const std::vector &initialValue) + : _value(initialValue) {} + + Buffer(size_t size) + : _value(size) {} + + Buffer() {} + + Buffer(Buffer &&other) + : _value(std::move(other._value)), _versionNumber(other._versionNumber), _timeStamp(other._timeStamp) {} + + Buffer& operator=(Buffer &&other) { + _value = std::move(other._value); + _versionNumber = other._versionNumber; + _timeStamp = other._timeStamp; + return *this; + } + /** The actual data contained in this buffer. */ std::vector _value; @@ -189,21 +208,6 @@ namespace ChimeraTK { /** Time stamp associated with this buffer */ TimeStamp _timeStamp; - /** Swap the content of this Buffer with another */ - void swap(Buffer &other) { - - _value.swap(other._value); - - ChimeraTK::VersionNumber tempVersion = _versionNumber; - _versionNumber = other._versionNumber; - other._versionNumber = tempVersion; - - TimeStamp tempTime = _timeStamp; - _timeStamp = other._timeStamp; - other._timeStamp = tempTime; - - } - }; /** @@ -227,10 +231,9 @@ namespace ChimeraTK { { // fill the internal buffers of the queue for(size_t i=0; i std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 2, + const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 3, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr() ); @@ -413,7 +416,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", std::size_t numberOfBuffers = 2, bool maySendDestructively = false, + const std::string &description = "", std::size_t numberOfBuffers = 3, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr()); @@ -428,7 +431,8 @@ namespace ChimeraTK { : ProcessArray(instanceType, name, unit, description), _vectorSize(initialValue.size()), _maySendDestructively(false), - _sharedState(numberOfBuffers, initialValue.size()) + _sharedState(numberOfBuffers, initialValue.size()), + _localBuffer(initialValue) { mtca4u::ExperimentalFeatures::enable(); // allocate and initialise buffer of the base class @@ -463,6 +467,7 @@ namespace ChimeraTK { _vectorSize(receiver->_vectorSize), _maySendDestructively(maySendDestructively), _sharedState(receiver->_sharedState), + _localBuffer(receiver->_localBuffer._value), _receiver(receiver), _timeStampSource(timeStampSource), _sendNotificationListener(sendNotificationListener) @@ -489,8 +494,7 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::doReadTransfer() { - - _sharedState._queue.wait(); + _sharedState._queue.pop_wait(_localBuffer); boost::this_thread::interruption_point(); /// @todo probably redundant } @@ -499,8 +503,7 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferNonBlocking() { - bool ret = !(_sharedState._queue.empty()); - return ret; + return _sharedState._queue.pop(_localBuffer); } /*********************************************************************************************************************/ @@ -547,10 +550,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. - if(_localBuffer._value.size() != _vectorSize) { - throw std::runtime_error("Cannot run read operation because the size of the vector belonging to the current" - " buffer has been modified."); - } + assert(mtca4u::NDRegisterAccessor::buffer_2D[0].size() == _localBuffer._value.size()); // swap data out of the queue buffer mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _localBuffer._value ); @@ -652,6 +652,7 @@ namespace ChimeraTK { _localBuffer._versionNumber = newVersionNumber; // set the data by copying or swapping + assert(_localBuffer._value.size() == mtca4u::NDRegisterAccessor::buffer_2D[0].size() ); if(shouldCopy) { _localBuffer._value = mtca4u::NDRegisterAccessor::buffer_2D[0]; } @@ -660,7 +661,7 @@ namespace ChimeraTK { } // send the data to the queue - bool lostData = _sharedState._queue.push_overwrite(_localBuffer); + bool lostData = _sharedState._queue.push_overwrite(std::move(_localBuffer)); // change current index to the new empty buffer if(shouldCopy) { From 9ddd64af0f646f0962884632f000b23138e43382 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 30 May 2018 18:35:46 +0200 Subject: [PATCH 340/786] fixed the tests --- tests/src/stresstest.cpp | 6 +-- tests/src/testAsyncRead.cpp | 40 ++++++++++++-------- tests/src/testUnidirectionalProcessArray.cpp | 9 +++-- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/tests/src/stresstest.cpp b/tests/src/stresstest.cpp index c374725..40e961a 100644 --- a/tests/src/stresstest.cpp +++ b/tests/src/stresstest.cpp @@ -110,12 +110,12 @@ int main() { pviter->second->readLatest(); sleepTime = pviter->second->accessData(0); } - else { // mode == 3 + /*else { // mode == 3 /// @todo enable readAny in stresstest again! auto id = readAny(varList); sleepTime = varMap[id]->accessData(0); - } + }*/ ++nReceiveOps; - mode = (sleepTime % 4); + mode = (sleepTime % 3); //4); // iterate to next variable ++pviter; diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index 50563bf..84eda55 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -5,8 +5,9 @@ #include #include -#include -#include +#include +#include +#include #include "ProcessArray.h" @@ -154,7 +155,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -170,7 +172,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -186,7 +189,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -203,7 +207,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -219,7 +224,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -235,7 +241,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -251,7 +258,8 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&a1,&a2,&a3,&a4,&flag] { readAny({a1,a2,a3,a4}); flag = true; }); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -271,12 +279,13 @@ void AsyncReadTest::testReadAny() { s2 = 777; s2.write(); // no point to use a thread here - auto r = readAny({a1,a2,a3,a4}); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + auto r = group.waitAny(); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 123); - r = readAny({a1,a2,a3,a4}); + r = group.waitAny(); BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); @@ -299,28 +308,29 @@ void AsyncReadTest::testReadAny() { s1.write(); // no point to use a thread here - auto r = readAny({a1,a2,a3,a4}); + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + auto r = group.waitAny(); BOOST_CHECK(a4.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); - r = readAny({a1,a2,a3,a4}); + r = group.waitAny(); BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); - r = readAny({a1,a2,a3,a4}); + r = group.waitAny(); BOOST_CHECK(a3.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 333); BOOST_CHECK(a4 == 111); - r = readAny({a1,a2,a3,a4}); + r = group.waitAny(); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 444); BOOST_CHECK(a2 == 222); diff --git a/tests/src/testUnidirectionalProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp index ecd7641..6b93c7e 100644 --- a/tests/src/testUnidirectionalProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -239,7 +239,7 @@ namespace ChimeraTK { void UnidirectionalProcessArrayTest::testSynchronization() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 3, true); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; // If we send three values consecutively, they all should be received because the queue length is two and there is @@ -251,16 +251,19 @@ namespace ChimeraTK { sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 2); sender->writeDestructively(); BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER); } BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 1); } BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { BOOST_CHECK(*i == SOME_NUMBER + 2); @@ -346,7 +349,7 @@ namespace ChimeraTK { void UnidirectionalProcessArrayTest::testVersionNumbers() { typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true, + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 3, true, TimeStampSource::SharedPtr()); typename ProcessArray::SharedPtr sender = senderReceiver.first; typename ProcessArray::SharedPtr receiver = senderReceiver.second; @@ -409,7 +412,7 @@ namespace ChimeraTK { template void UnidirectionalProcessArrayTest::testBlockingRead() { - auto senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); + auto senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 3, true); auto sender = senderReceiver.first; auto receiver = senderReceiver.second; From 60526f393e24610246f68626e0b7b22cd1424f4b Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 13:33:13 +0200 Subject: [PATCH 341/786] fixed inverted return value --- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index c89256a..bbee961 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -661,7 +661,7 @@ namespace ChimeraTK { } // send the data to the queue - bool lostData = _sharedState._queue.push_overwrite(std::move(_localBuffer)); + bool dataNotLost = _sharedState._queue.push_overwrite(std::move(_localBuffer)); // change current index to the new empty buffer if(shouldCopy) { @@ -674,7 +674,7 @@ namespace ChimeraTK { _sendNotificationListener->notify(_receiver); } - return lostData; + return !dataNotLost; } /*********************************************************************************************************************/ From f1c0cee4d12fa9324a040d908ca4d27a83aaf659 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 13:33:27 +0200 Subject: [PATCH 342/786] we have to reuse the ReadAnyGroup and should not create new ones --- tests/src/testAsyncRead.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index 84eda55..f061064 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -151,11 +151,13 @@ void AsyncReadTest::testReadAny() { s3 = 120; s4 = 345; + // create ReadAnyGroup + ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); + // variable Test1 { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -172,7 +174,6 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -189,7 +190,6 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -207,7 +207,6 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -224,7 +223,6 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -241,7 +239,6 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -258,7 +255,6 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); // check that it doesn't return too soon @@ -279,7 +275,6 @@ void AsyncReadTest::testReadAny() { s2 = 777; s2.write(); // no point to use a thread here - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); auto r = group.waitAny(); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 666); @@ -308,7 +303,6 @@ void AsyncReadTest::testReadAny() { s1.write(); // no point to use a thread here - ChimeraTK::ReadAnyGroup group({a1,a2,a3,a4}); auto r = group.waitAny(); BOOST_CHECK(a4.getId() == r); BOOST_CHECK(a1 == 666); From a111c4028c4f2ee60c94bb802c05f732a227a052 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 13:33:41 +0200 Subject: [PATCH 343/786] fixed default number of buffers --- .../ControlSystemAdapter/BidirectionalProcessArray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index d4bab78..04c88f8 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -73,7 +73,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", - T initialValue = T(), std::size_t numberOfBuffers = 2, + T initialValue = T(), std::size_t numberOfBuffers = 3, TimeStampSource::SharedPtr timeStampSource1 = TimeStampSource::SharedPtr(), TimeStampSource::SharedPtr timeStampSource2 = @@ -132,7 +132,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", std::size_t numberOfBuffers = 2, + const std::string &description = "", std::size_t numberOfBuffers = 3, TimeStampSource::SharedPtr timeStampSource1 = TimeStampSource::SharedPtr(), TimeStampSource::SharedPtr timeStampSource2 = From cf1eeec9b76b2bacf8bfa0ed5552fbbc211ac9fa Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 14:42:01 +0200 Subject: [PATCH 344/786] Do not directly take the version number and time stamp from the _localBuffer member variable, since it gets modified in doReadTransfer-like functions. Doing so would impose a thread safety violation in some contexts. --- .../UnidirectionalProcessArray.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index bbee961..f1364b7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -90,11 +90,11 @@ namespace ChimeraTK { UnidirectionalProcessArray::SharedPtr receiver); TimeStamp getTimeStamp() const override { - return _localBuffer._timeStamp; + return _timeStamp; } ChimeraTK::VersionNumber getVersionNumber() const override { - return _localBuffer._versionNumber; + return _versionNumber; } void doReadTransfer() override; @@ -257,6 +257,16 @@ namespace ChimeraTK { */ Buffer _localBuffer; + /** + * Local copy of the version number belonging to the NDRegisterAccessor::buffer_2D + */ + VersionNumber _versionNumber; + + /** + * Local copy of the version number belonging to the NDRegisterAccessor::buffer_2D + */ + TimeStamp _timeStamp; + /** * Pointer to the receiver associated with this sender. This field is only * used if this process variable represents a sender. @@ -552,8 +562,10 @@ namespace ChimeraTK { // trouble when it suddenly experiences a vector of the wrong size. assert(mtca4u::NDRegisterAccessor::buffer_2D[0].size() == _localBuffer._value.size()); - // swap data out of the queue buffer + // swap data out of the local buffer into the user buffer mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _localBuffer._value ); + _versionNumber = _localBuffer._versionNumber; + _timeStamp = _localBuffer._timeStamp; } From f8ecefd4118ce546f7cae3370c152ed34dfcf108 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 14:43:06 +0200 Subject: [PATCH 345/786] call preRead and postRead of the receiver - this was a bug already present before switching to future_queue! --- .../BidirectionalProcessArray.h | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 04c88f8..3edcfcd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -200,6 +200,8 @@ namespace ChimeraTK { return _versionNumber; } + void doPreRead() override; + void doReadTransfer() override; bool doReadTransferNonBlocking() override; @@ -210,8 +212,12 @@ namespace ChimeraTK { void doPostRead() override; + void doPreWrite() override; + bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber={}) override; + void doPostWrite() override; + /** * Sends the current value to the receiver. Returns true if an * empty buffer was available and false if no empty buffer was @@ -363,6 +369,13 @@ namespace ChimeraTK { mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->accessChannel(0); } +/*********************************************************************************************************************/ + + template + void BidirectionalProcessArray::doPreRead() { + _receiver->preRead(); + } + /*********************************************************************************************************************/ template @@ -419,6 +432,12 @@ namespace ChimeraTK { } } +/*********************************************************************************************************************/ + + template + void BidirectionalProcessArray::doPreWrite() { + } + /*********************************************************************************************************************/ template @@ -452,6 +471,12 @@ namespace ChimeraTK { return lostData; } +/*********************************************************************************************************************/ + + template + void BidirectionalProcessArray::doPostWrite() { + } + /*********************************************************************************************************************/ template From fea5b032dfac2337a0ff769a905727f430e17470 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 15:32:33 +0200 Subject: [PATCH 346/786] changed two exceptions from std::runtime_error into std::logic_error, since they fit the definition of a logic error --- .../UnidirectionalProcessArray.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index f1364b7..54bd478 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -638,7 +638,7 @@ namespace ChimeraTK { // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. if(mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { - throw std::runtime_error( + throw std::logic_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been " "modified. Variable name: "+this->getName()); } @@ -646,8 +646,8 @@ namespace ChimeraTK { // A version should never be send with a version number that is equal to or // even less than the last version number used. Such an attempt indicates // that there is a problem in the logic attempting the write operation. - if (newVersionNumber <= getVersionNumber()) { - throw std::runtime_error( + if (newVersionNumber < _versionNumber) { + throw std::logic_error( "The version number passed to write is less than or equal to the last version number used."); } @@ -657,11 +657,11 @@ namespace ChimeraTK { _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); } - // Before sending the value, we have to set the associated time-stamp. + // Set time stamp and version number _localBuffer._timeStamp = newTimeStamp; - - // set the version numbers _localBuffer._versionNumber = newVersionNumber; + _timeStamp = newTimeStamp; + _versionNumber = newVersionNumber; // set the data by copying or swapping assert(_localBuffer._value.size() == mtca4u::NDRegisterAccessor::buffer_2D[0].size() ); @@ -674,12 +674,12 @@ namespace ChimeraTK { // send the data to the queue bool dataNotLost = _sharedState._queue.push_overwrite(std::move(_localBuffer)); - +/* // change current index to the new empty buffer if(shouldCopy) { _localBuffer._timeStamp = newTimeStamp; _localBuffer._versionNumber = newVersionNumber; - } + } */ // notify send notification listener, if present if(_sendNotificationListener) { From 69661955ef40de92e950a101c7e1deda753461ab Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 15:33:58 +0200 Subject: [PATCH 347/786] Fixed the test. Writing older version numbers than previously seen should not be allowed. It is unclear why this test did not fail in the past. --- tests/src/testUnidirectionalProcessArray.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/src/testUnidirectionalProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp index 6b93c7e..c5e392e 100644 --- a/tests/src/testUnidirectionalProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -375,12 +375,16 @@ namespace ChimeraTK { BOOST_CHECK(receiver->readNonBlocking()); BOOST_CHECK(receiver->getVersionNumber() > versionNumber); BOOST_CHECK(receiver->accessChannel(0)[0] == 3); - // Even when sending again an update with an older version number, the update should be seen by the receiver. + // Even when sending again an update with an older version number, we should receive an exception sender->accessChannel(0)[0] = 4; - sender->writeDestructively(versionNumber); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(versionNumber == receiver->getVersionNumber()); - BOOST_CHECK(receiver->accessChannel(0)[0] == 4); + try { + sender->writeDestructively(versionNumber); + BOOST_ERROR("Exception expected."); + } + catch(std::logic_error&) {} + BOOST_CHECK(receiver->readNonBlocking() == false); + BOOST_CHECK(receiver->getVersionNumber() > versionNumber); + BOOST_CHECK(receiver->accessChannel(0)[0] == 3); // When we send non-destructively, the version number on the sender and the // receiver should match after sending and receiving. versionNumber = receiver->getVersionNumber(); From f108279818d55701a654459759ca78103f862934 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 1 Jun 2018 16:07:09 +0200 Subject: [PATCH 348/786] deleted some commented code --- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 54bd478..0398c0f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -674,12 +674,6 @@ namespace ChimeraTK { // send the data to the queue bool dataNotLost = _sharedState._queue.push_overwrite(std::move(_localBuffer)); -/* - // change current index to the new empty buffer - if(shouldCopy) { - _localBuffer._timeStamp = newTimeStamp; - _localBuffer._versionNumber = newVersionNumber; - } */ // notify send notification listener, if present if(_sendNotificationListener) { From 2359503013b078b08907e4fdb00268f1a7d21c0b Mon Sep 17 00:00:00 2001 From: Jens Georg Date: Tue, 5 Jun 2018 17:24:23 +0200 Subject: [PATCH 349/786] Make ApplicationBase::getName const There is no need for it to be non-const --- include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h index 4074c7c..cc5eac4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h +++ b/include/ChimeraTK/ControlSystemAdapter/ApplicationBase.h @@ -63,7 +63,7 @@ namespace ChimeraTK { static ApplicationBase& getInstance(); /** Return the name of the application */ - const std::string& getName() { + const std::string& getName() const { return applicationName; } From cbbed3115bea057387a6f77135edcf662abc7a15 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 8 Jun 2018 11:30:36 +0200 Subject: [PATCH 350/786] removed no-longer existing header file --- tests/src/stresstest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/stresstest.cpp b/tests/src/stresstest.cpp index 40e961a..04001a0 100644 --- a/tests/src/stresstest.cpp +++ b/tests/src/stresstest.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "ProcessArray.h" using namespace ChimeraTK; From b746ca5f4f4643b889bfed94143ea0760c146c12 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 8 Jun 2018 14:49:35 +0200 Subject: [PATCH 351/786] - allow specifying AccessModeFlags like wait_for_new_data. This still needs a real implementation of the case when this flag is not specified (specifying it is the default and compatible with the previous behaviour) - implement TransferElement::interrupt() --- .../BidirectionalProcessArray.h | 95 +++++++--------- .../ControlSystemAdapter/ProcessArray.h | 23 ++-- .../UnidirectionalProcessArray.h | 107 ++++++++++-------- 3 files changed, 116 insertions(+), 109 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 3edcfcd..a6aeb8f 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -69,19 +69,15 @@ namespace ChimeraTK { * ChimeraTK::ExperimentalFeatures in DeviceAccess). */ template - std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( - std::size_t size, const mtca4u::RegisterPath & name = "", - const std::string &unit = "", const std::string &description = "", + std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( std::size_t size, + const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 3, - TimeStampSource::SharedPtr timeStampSource1 = - TimeStampSource::SharedPtr(), - TimeStampSource::SharedPtr timeStampSource2 = - TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener1 = - ProcessVariableListener::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener2 = - ProcessVariableListener::SharedPtr()); + TimeStampSource::SharedPtr timeStampSource1 = TimeStampSource::SharedPtr(), + TimeStampSource::SharedPtr timeStampSource2 = TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener1 = ProcessVariableListener::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener2 = ProcessVariableListener::SharedPtr(), + const AccessModeFlags &flags={AccessMode::wait_for_new_data} ); /** * Creates a bidirectional synchronized process array. A bidirectional @@ -128,19 +124,15 @@ namespace ChimeraTK { * ChimeraTK::ExperimentalFeatures in DeviceAccess). */ template - std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( - const std::vector& initialValue, - const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", std::size_t numberOfBuffers = 3, - TimeStampSource::SharedPtr timeStampSource1 = - TimeStampSource::SharedPtr(), - TimeStampSource::SharedPtr timeStampSource2 = - TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener1 = - ProcessVariableListener::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener2 = - ProcessVariableListener::SharedPtr()); + std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( const std::vector& initialValue, + const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + std::size_t numberOfBuffers = 3, + TimeStampSource::SharedPtr timeStampSource1 = TimeStampSource::SharedPtr(), + TimeStampSource::SharedPtr timeStampSource2 = TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener1 = ProcessVariableListener::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener2 = ProcessVariableListener::SharedPtr(), + const AccessModeFlags &flags={AccessMode::wait_for_new_data} ); /** * Implementation of the process array that transports data in both @@ -169,13 +161,13 @@ namespace ChimeraTK { const std::vector&, const mtca4u::RegisterPath &, const std::string &, const std::string &, std::size_t, TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, - ProcessVariableListener::SharedPtr, ProcessVariableListener::SharedPtr); + ProcessVariableListener::SharedPtr, ProcessVariableListener::SharedPtr, const AccessModeFlags &flags); friend std::pair::SharedPtr, typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray<>( std::size_t, const mtca4u::RegisterPath &, const std::string &, const std::string &, T, std::size_t, TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, ProcessVariableListener::SharedPtr, - ProcessVariableListener::SharedPtr); + ProcessVariableListener::SharedPtr, const AccessModeFlags &flags); /** * Creates a bidirectional process array that uses the passed process arrays @@ -190,7 +182,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr sender, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, - TimeStamp initialTimeStamp, VersionNumber initialVersionNumber); + TimeStamp initialTimeStamp, VersionNumber initialVersionNumber, const AccessModeFlags &flags); TimeStamp getTimeStamp() const override { return _timeStamp; @@ -338,8 +330,9 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr sender, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, - TimeStamp initialTimeStamp, VersionNumber initialVersionNumber) - : ProcessArray(ProcessArray::SENDER_RECEIVER, name, unit, description), + TimeStamp initialTimeStamp, VersionNumber initialVersionNumber, + const AccessModeFlags &flags) + : ProcessArray(ProcessArray::SENDER_RECEIVER, name, unit, description, flags), _allowPersistentDataStorage(allowPersistentDataStorage), _receiver(receiver), _sender(boost::dynamic_pointer_cast>(sender)), @@ -515,18 +508,17 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( - std::size_t size, const mtca4u::RegisterPath & name, - const std::string &unit, const std::string &description, T initialValue, + typename std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( std::size_t size, + const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, std::size_t numberOfBuffers, TimeStampSource::SharedPtr timeStampSource1, - TimeStampSource::SharedPtr timeStampSource2, - ProcessVariableListener::SharedPtr sendNotificationListener1, - ProcessVariableListener::SharedPtr sendNotificationListener2) { + TimeStampSource::SharedPtr timeStampSource2, ProcessVariableListener::SharedPtr sendNotificationListener1, + ProcessVariableListener::SharedPtr sendNotificationListener2, const AccessModeFlags &flags ) + { auto senderReceiver1 = createSynchronizedProcessArray(size, name, unit, - description, initialValue, numberOfBuffers, true); + description, initialValue, numberOfBuffers, true, {}, {}, flags); auto senderReceiver2 = createSynchronizedProcessArray(size, name, unit, - description, initialValue, numberOfBuffers, true); + description, initialValue, numberOfBuffers, true, {}, {}, flags); // We create a default-constructed time-stamp and version number because we // want to be sure that initially both sides have the same time stamp and // version number. @@ -541,12 +533,12 @@ namespace ChimeraTK { boost::make_shared >(uniqueId, name, unit, description, true, senderReceiver2.second, senderReceiver1.first, timeStampSource1, sendNotificationListener1, timeStamp, - versionNumber); + versionNumber, flags); typename boost::shared_ptr > pv2 = boost::make_shared >(uniqueId, name, unit, description, false, senderReceiver1.second, senderReceiver2.first, timeStampSource2, sendNotificationListener2, timeStamp, - versionNumber); + versionNumber, flags); pv1->_partner = pv2; pv2->_partner = pv1; return std::pair::SharedPtr, @@ -556,18 +548,17 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray( - const std::vector& initialValue, const mtca4u::RegisterPath & name, - const std::string &unit, const std::string &description, + typename std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( const std::vector& initialValue, + const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, std::size_t numberOfBuffers, TimeStampSource::SharedPtr timeStampSource1, - TimeStampSource::SharedPtr timeStampSource2, - ProcessVariableListener::SharedPtr sendNotificationListener1, - ProcessVariableListener::SharedPtr sendNotificationListener2) { + TimeStampSource::SharedPtr timeStampSource2, ProcessVariableListener::SharedPtr sendNotificationListener1, + ProcessVariableListener::SharedPtr sendNotificationListener2, const AccessModeFlags &flags ) + { auto senderReceiver1 = createSynchronizedProcessArray(initialValue, name, - unit, description, numberOfBuffers, true, timeStampSource1); + unit, description, numberOfBuffers, true, timeStampSource1, {}, flags); auto senderReceiver2 = createSynchronizedProcessArray(initialValue, name, - unit, description, numberOfBuffers, true, timeStampSource2); + unit, description, numberOfBuffers, true, timeStampSource2, {}, flags); // We create a default-constructed time-stamp and version number because we // want to be sure that initially both sides have the same time stamp and // version number. @@ -582,12 +573,12 @@ namespace ChimeraTK { boost::make_shared >(uniqueId, name, unit, description, true, senderReceiver2.second, senderReceiver1.first, timeStampSource1, sendNotificationListener1, timeStamp, - versionNumber); + versionNumber, flags); typename boost::shared_ptr > pv2 = boost::make_shared >(uniqueId, name, unit, description, false, senderReceiver1.second, senderReceiver2.first, timeStampSource2, sendNotificationListener2, timeStamp, - versionNumber); + versionNumber, flags); pv1->_partner = pv2; pv2->_partner = pv1; return std::pair::SharedPtr, diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index b0b6971..fd6c40a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -72,7 +72,7 @@ namespace ChimeraTK { * Creates a process array of the specified type. */ ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, - const std::string &unit, const std::string &description); + const std::string &unit, const std::string &description, const AccessModeFlags &flags); virtual ~ProcessArray(); @@ -138,13 +138,20 @@ namespace ChimeraTK { // You can't replace anything here. Just do nothing. } - private: + AccessModeFlags getAccessModeFlags() const override { + return _flags; + } + + protected: /** * Type this instance is representing. */ InstanceType _instanceType; + /// AccessModeFlags of this ProcessArray + AccessModeFlags _flags; + }; /*********************************************************************************************************************/ @@ -152,12 +159,12 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - ProcessArray::ProcessArray(InstanceType instanceType, - const mtca4u::RegisterPath& name, const std::string &unit, - const std::string &description) : - mtca4u::NDRegisterAccessor(name, unit, description), _instanceType( - instanceType) { - } + ProcessArray::ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, + const std::string &description, const AccessModeFlags &flags) + : mtca4u::NDRegisterAccessor(name, unit, description), + _instanceType(instanceType), + _flags(flags) + {} /*********************************************************************************************************************/ diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 0398c0f..62aca8e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -65,7 +65,7 @@ namespace ChimeraTK { typename ProcessArray::InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, const std::string &description, const std::vector& initialValue, - std::size_t numberOfBuffers); + std::size_t numberOfBuffers, const AccessModeFlags &flags); /** * Creates a process array that acts as a sender. A sender is intended @@ -87,7 +87,7 @@ namespace ChimeraTK { typename ProcessArray::InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, - UnidirectionalProcessArray::SharedPtr receiver); + UnidirectionalProcessArray::SharedPtr receiver, const AccessModeFlags &flags); TimeStamp getTimeStamp() const override { return _timeStamp; @@ -171,6 +171,15 @@ namespace ChimeraTK { } } + void interrupt() override { + try { + throw boost::thread_interrupted(); + } + catch(...) { + _sharedState._queue.push_exception(std::current_exception()); + } + } + private: /** @@ -375,11 +384,13 @@ namespace ChimeraTK { * The specified initial value is used for all the elements of the array. */ template - std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 3, - bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr() ); + std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createSynchronizedProcessArray(std::size_t size, + const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + T initialValue = T(), std::size_t numberOfBuffers = 3, bool maySendDestructively = false, + TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), + ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr(), + const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /** * Creates a synchronized process array. A synchronized process array works @@ -424,11 +435,13 @@ namespace ChimeraTK { * values provided by this vector. */ template - std::pair::SharedPtr, typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const mtca4u::RegisterPath & name = "", const std::string &unit = "", - const std::string &description = "", std::size_t numberOfBuffers = 3, bool maySendDestructively = false, + std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createSynchronizedProcessArray( const std::vector& initialValue, + const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + std::size_t numberOfBuffers = 3, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), - ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr()); + ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr(), + const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /*********************************************************************************************************************/ /*** Implementations of member functions below this line *************************************************************/ @@ -437,8 +450,8 @@ namespace ChimeraTK { template UnidirectionalProcessArray::UnidirectionalProcessArray( typename ProcessArray::InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, const std::string &description, - const std::vector& initialValue, std::size_t numberOfBuffers ) - : ProcessArray(instanceType, name, unit, description), + const std::vector& initialValue, std::size_t numberOfBuffers, const AccessModeFlags &flags ) + : ProcessArray(instanceType, name, unit, description, flags), _vectorSize(initialValue.size()), _maySendDestructively(false), _sharedState(numberOfBuffers, initialValue.size()), @@ -472,8 +485,9 @@ namespace ChimeraTK { template UnidirectionalProcessArray::UnidirectionalProcessArray( typename ProcessArray::InstanceType instanceType, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener, UnidirectionalProcessArray::SharedPtr receiver ) - : ProcessArray(instanceType, receiver->getName(), receiver->getUnit(), receiver->getDescription()), + ProcessVariableListener::SharedPtr sendNotificationListener, UnidirectionalProcessArray::SharedPtr receiver, + const AccessModeFlags &flags ) + : ProcessArray(instanceType, receiver->getName(), receiver->getUnit(), receiver->getDescription(), flags), _vectorSize(receiver->_vectorSize), _maySendDestructively(maySendDestructively), _sharedState(receiver->_sharedState), @@ -505,8 +519,7 @@ namespace ChimeraTK { template void UnidirectionalProcessArray::doReadTransfer() { _sharedState._queue.pop_wait(_localBuffer); - boost::this_thread::interruption_point(); /// @todo probably redundant - + /// @todo if wait_for_new_data is not set, make identical to doReadTransferLatest() } /*********************************************************************************************************************/ @@ -514,6 +527,7 @@ namespace ChimeraTK { template bool UnidirectionalProcessArray::doReadTransferNonBlocking() { return _sharedState._queue.pop(_localBuffer); + /// @todo if wait_for_new_data is not set, make identical to doReadTransferLatest() } /*********************************************************************************************************************/ @@ -548,6 +562,7 @@ namespace ChimeraTK { return TransferFuture(_sharedState._queue.template then([this] (Buffer &b) { std::swap( _localBuffer, b ); }, std::launch::deferred), this); + /// @todo if wait_for_new_data is not set, make identical to doReadTransferLatest() (but asynchronous) } @@ -688,43 +703,37 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - std::size_t size, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, - std::size_t numberOfBuffers, bool maySendDestructively, - TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener) { - typename boost::shared_ptr > receiver = - boost::make_shared >( - ProcessArray::RECEIVER, name, unit, description, - std::vector(size, initialValue), numberOfBuffers); - typename UnidirectionalProcessArray::SharedPtr sender = - boost::make_shared >( - ProcessArray::SENDER, maySendDestructively, timeStampSource, - sendNotificationListener, receiver); - return std::pair::SharedPtr, - typename ProcessArray::SharedPtr>(sender, receiver); + typename std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createSynchronizedProcessArray(std::size_t size, + const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, + std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener, const AccessModeFlags &flags) + { + auto receiver = boost::make_shared>( ProcessArray::RECEIVER, name, unit, + description, std::vector(size, initialValue), + numberOfBuffers, flags); + auto sender = boost::make_shared >( ProcessArray::SENDER, maySendDestructively, + timeStampSource, sendNotificationListener, + receiver, flags); + return {sender, receiver}; } /*********************************************************************************************************************/ template - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> createSynchronizedProcessArray( - const std::vector& initialValue, const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, - std::size_t numberOfBuffers, bool maySendDestructively, - TimeStampSource::SharedPtr timeStampSource, - ProcessVariableListener::SharedPtr sendNotificationListener) { - typename boost::shared_ptr > receiver = - boost::make_shared >( - ProcessArray::RECEIVER, name, unit, description, initialValue, - numberOfBuffers); - typename UnidirectionalProcessArray::SharedPtr sender = - boost::make_shared >( - ProcessArray::SENDER, maySendDestructively, timeStampSource, - sendNotificationListener, receiver); - return std::pair::SharedPtr, - typename ProcessArray::SharedPtr>(sender, receiver); + typename std::pair< typename ProcessArray::SharedPtr, + typename ProcessArray::SharedPtr > createSynchronizedProcessArray(const std::vector& initialValue, + const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, + std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, + ProcessVariableListener::SharedPtr sendNotificationListener, const AccessModeFlags &flags) + { + auto receiver = boost::make_shared>( ProcessArray::RECEIVER, name, unit, + description, initialValue, numberOfBuffers, + flags); + auto sender = boost::make_shared>( ProcessArray::SENDER, maySendDestructively, + timeStampSource, sendNotificationListener, + receiver, flags); + return {sender, receiver}; } } // namespace ChimeraTK From b6bd6278c95f272f24aa9b072adcec83b09483b1 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 11 Jun 2018 18:48:10 +0200 Subject: [PATCH 352/786] allow specifying the AccessModeFlags in the PV manager --- .../ControlSystemAdapter/DevicePVManager.h | 20 +++++++++++-------- .../ControlSystemAdapter/PVManager.h | 14 +++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index 426a431..b32d68e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -82,7 +82,8 @@ namespace ChimeraTK { SynchronizationDirection synchronizationDirection, const mtca4u::RegisterPath& processVariableName, std::size_t size, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", - T initialValue = T(), bool maySendDestructively = false, std::size_t numberOfBuffers = 2); + T initialValue = T(), bool maySendDestructively = false, std::size_t numberOfBuffers = 3, + const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /** * Creates a new process array and registers it with the PV manager. @@ -113,7 +114,8 @@ namespace ChimeraTK { SynchronizationDirection synchronizationDirection, const mtca4u::RegisterPath& processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", - bool maySendDestructively = false, std::size_t numberOfBuffers = 2); + bool maySendDestructively = false, std::size_t numberOfBuffers = 3, + const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /** * Returns a reference to a process array that has been created earlier @@ -226,16 +228,17 @@ namespace ChimeraTK { SynchronizationDirection synchronizationDirection, const mtca4u::RegisterPath& processVariableName, std::size_t size, const std::string& unit, const std::string& description, T initialValue, - bool maySendDestructively, std::size_t numberOfBuffers) { + bool maySendDestructively, std::size_t numberOfBuffers, const AccessModeFlags &flags) + { switch (synchronizationDirection) { case controlSystemToDevice: return _pvManager->createProcessArrayControlSystemToDevice( processVariableName, std::vector(size, initialValue), unit, description, - maySendDestructively, numberOfBuffers).second; + maySendDestructively, numberOfBuffers, flags).second; case deviceToControlSystem: return _pvManager->createProcessArrayDeviceToControlSystem( processVariableName, std::vector(size, initialValue), unit, description, - maySendDestructively, numberOfBuffers).second; + maySendDestructively, numberOfBuffers, flags).second; case bidirectional: if (maySendDestructively) { throw std::logic_error( @@ -254,16 +257,17 @@ namespace ChimeraTK { SynchronizationDirection synchronizationDirection, const mtca4u::RegisterPath& processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, - bool maySendDestructively, std::size_t numberOfBuffers) { + bool maySendDestructively, std::size_t numberOfBuffers, const AccessModeFlags &flags) + { switch (synchronizationDirection) { case controlSystemToDevice: return _pvManager->createProcessArrayControlSystemToDevice( processVariableName, initialValue, unit, description, maySendDestructively, - numberOfBuffers).second; + numberOfBuffers, {}, {}, flags).second; case deviceToControlSystem: return _pvManager->createProcessArrayDeviceToControlSystem( processVariableName, initialValue, unit, description, maySendDestructively, - numberOfBuffers).second; + numberOfBuffers, {}, {}, flags).second; case bidirectional: if (maySendDestructively) { throw std::logic_error( diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 0c8b87a..858b7ed 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -121,7 +121,8 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", - bool maySendDestructively = false, std::size_t numberOfBuffers = 2); + bool maySendDestructively = false, std::size_t numberOfBuffers = 3, + const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /** * Creates a new process array for transferring data from the control system @@ -154,7 +155,8 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", - bool maySendDestructively = false, std::size_t numberOfBuffers = 2); + bool maySendDestructively = false, std::size_t numberOfBuffers = 3, + const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /** * Returns a reference to a process array that has been created earlier @@ -502,7 +504,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, - std::size_t numberOfBuffers) { + std::size_t numberOfBuffers, const AccessModeFlags &flags) { if (_processVariables.find(processVariableName) != _processVariables.end()) { throw std::invalid_argument( @@ -520,7 +522,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, unit, description, numberOfBuffers, maySendDestructively, timeStampSource, - sendNotificationListener); + sendNotificationListener, flags); _processVariables.insert( std::make_pair(processVariableName, @@ -539,7 +541,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, - std::size_t numberOfBuffers) { + std::size_t numberOfBuffers, const AccessModeFlags &flags) { if (_processVariables.find(processVariableName) != _processVariables.end()) { throw std::invalid_argument( @@ -558,7 +560,7 @@ namespace ChimeraTK { typename ProcessArray::SharedPtr> processVariables = createSynchronizedProcessArray(initialValue, processVariableName, unit, description, numberOfBuffers, maySendDestructively, timeStampSource, - sendNotificationListener); + sendNotificationListener, flags); _processVariables.insert( std::make_pair(processVariableName, From 0d49d71aa1e5ef991579adbe44b089fcbc9f9a3b Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 19 Jun 2018 16:07:20 +0200 Subject: [PATCH 353/786] converted test into AUTO_TEST_CASEs --- tests/src/testUnidirectionalProcessArray.cpp | 979 +++++++++---------- 1 file changed, 454 insertions(+), 525 deletions(-) diff --git a/tests/src/testUnidirectionalProcessArray.cpp b/tests/src/testUnidirectionalProcessArray.cpp index ecd7641..a05f8e8 100644 --- a/tests/src/testUnidirectionalProcessArray.cpp +++ b/tests/src/testUnidirectionalProcessArray.cpp @@ -1,571 +1,500 @@ +// Define a name for the test module. +#define BOOST_TEST_MODULE UnidirectionalProcessArrayTest +// Only after defining the name include the unit test header. +#include +using namespace boost::unit_test_framework; + #include #include #include -#include - #include "ProcessArray.h" #include "CountingProcessVariableListener.h" #include "CountingTimeStampSource.h" -using namespace boost::unit_test_framework; +typedef boost::mpl::list test_types; + +using namespace ChimeraTK; + +static size_t const N_ELEMENTS = 12; + +// Just some number to add so the content is not equal to the index. We +// declare and define this constant outside of the ProcessArrayTest class, so +// that it can be used with references. +static size_t const SOME_NUMBER = 42; + +BOOST_AUTO_TEST_CASE_TEMPLATE( testConstructors, T, test_types ) { + + std::vector referenceVector; + referenceVector.push_back(0); + referenceVector.push_back(1); + referenceVector.push_back(2); + referenceVector.push_back(3); + + // Now we repeat the tests but for a sender / receiver pair. We do not test + // the notification listener and time-stamp source because there is no + // simple test for them and they are tested in a different method. + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + BOOST_CHECK(sender->getName() == "/"); + for (typename std::vector::iterator i = sender->accessChannel(0).begin(); + i != sender->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == 0); + } + BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); + BOOST_CHECK(receiver->getName() == "/"); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == 0); + } + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); + BOOST_CHECK(!sender->isReadable()); + BOOST_CHECK(sender->isWriteable()); + BOOST_CHECK(receiver->isReadable()); + BOOST_CHECK(!receiver->isWriteable()); + senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "test", "", "", + SOME_NUMBER, 5); + sender = senderReceiver.first; + receiver = senderReceiver.second; + BOOST_CHECK(sender->getName() == "/test"); + for (typename std::vector::iterator i = sender->accessChannel(0).begin(); + i != sender->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } + BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); + BOOST_CHECK(receiver->getName() == "/test"); + for (typename std::vector::const_iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } + BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); + senderReceiver = createSynchronizedProcessArray(referenceVector, "test", "", "", + 5, false); + sender = senderReceiver.first; + receiver = senderReceiver.second; + BOOST_CHECK(sender->getName() == "/test"); + BOOST_CHECK(sender->accessChannel(0).size() == 4); + BOOST_CHECK( + std::equal(sender->accessChannel(0).begin(), sender->accessChannel(0).end(), + referenceVector.begin())); + BOOST_CHECK(receiver->getName() == "/test"); + BOOST_CHECK(receiver->accessChannel(0).size() == 4); + BOOST_CHECK( + std::equal(receiver->accessChannel(0).begin(), receiver->accessChannel(0).end(), + referenceVector.begin())); +} -namespace ChimeraTK { - - /** - * The test class for the ProcessArray. - * It is templated to be tested with all data types. - */ - template - class UnidirectionalProcessArrayTest { - public: - static void testConstructors(); - static void testGet(); - static void testSet(); - static void testSwap(); - static void testSendNotification(); - static void testTimeStampSource(); - static void testSynchronization(); - static void testVersionNumbers(); - static void testBlockingRead(); - static void testReadLatest(); - - private: - static size_t const N_ELEMENTS = 12; - - }; - - // Just some number to add so the content is not equal to the index. We - // declare and define this constant outside of the ProcessArrayTest class, so - // that it can be used with references. - static size_t const SOME_NUMBER = 42; - - /** - * The boost test suite which executes the ProcessArrayTest. - */ - template - class UnidirectionalProcessArrayTestSuite: public test_suite { - public: - UnidirectionalProcessArrayTestSuite() : - test_suite("UnidirectionalProcessArray test suite") { - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testConstructors)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testGet)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSet)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSwap)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSendNotification)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testTimeStampSource)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testSynchronization)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testVersionNumbers)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testBlockingRead)); - add(BOOST_TEST_CASE(&UnidirectionalProcessArrayTest::testReadLatest)); - } - }; - - template - void UnidirectionalProcessArrayTest::testConstructors() { - - std::vector referenceVector; - referenceVector.push_back(0); - referenceVector.push_back(1); - referenceVector.push_back(2); - referenceVector.push_back(3); - - // Now we repeat the tests but for a sender / receiver pair. We do not test - // the notification listener and time-stamp source because there is no - // simple test for them and they are tested in a different method. - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == "/"); - for (typename std::vector::iterator i = sender->accessChannel(0).begin(); - i != sender->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == 0); - } - BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); - BOOST_CHECK(receiver->getName() == "/"); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == 0); - } - BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); - BOOST_CHECK(!sender->isReadable()); - BOOST_CHECK(sender->isWriteable()); - BOOST_CHECK(receiver->isReadable()); - BOOST_CHECK(!receiver->isWriteable()); - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "test", "", "", - SOME_NUMBER, 5); - sender = senderReceiver.first; - receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == "/test"); - for (typename std::vector::iterator i = sender->accessChannel(0).begin(); - i != sender->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - BOOST_CHECK(sender->accessChannel(0).size() == N_ELEMENTS); - BOOST_CHECK(receiver->getName() == "/test"); - for (typename std::vector::const_iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - BOOST_CHECK(receiver->accessChannel(0).size() == N_ELEMENTS); - senderReceiver = createSynchronizedProcessArray(referenceVector, "test", "", "", - 5, false); - sender = senderReceiver.first; - receiver = senderReceiver.second; - BOOST_CHECK(sender->getName() == "/test"); - BOOST_CHECK(sender->accessChannel(0).size() == 4); - BOOST_CHECK( - std::equal(sender->accessChannel(0).begin(), sender->accessChannel(0).end(), - referenceVector.begin())); - BOOST_CHECK(receiver->getName() == "/test"); - BOOST_CHECK(receiver->accessChannel(0).size() == 4); - BOOST_CHECK( - std::equal(receiver->accessChannel(0).begin(), receiver->accessChannel(0).end(), - referenceVector.begin())); - } - - template - void UnidirectionalProcessArrayTest::testGet() { - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - sender->accessChannel(0); - receiver->accessChannel(0); - typename std::vector & v = sender->accessChannel(0); - typename std::vector const & cv = sender->accessChannel(0); - for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - for (typename std::vector::const_iterator i = cv.begin(); i != cv.end(); - ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } +BOOST_AUTO_TEST_CASE_TEMPLATE( testGet, T, test_types ) { + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", SOME_NUMBER); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + sender->accessChannel(0); + receiver->accessChannel(0); + typename std::vector & v = sender->accessChannel(0); + typename std::vector const & cv = sender->accessChannel(0); + for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } + for (typename std::vector::const_iterator i = cv.begin(); i != cv.end(); + ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE( testSet, T, test_types ) { + typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> + senderReceiver = createSynchronizedProcessArray(N_ELEMENTS); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + + // Test the assignment of a vector. + std::vector v(N_ELEMENTS, SOME_NUMBER + 1); + sender->accessChannel(0) = v; + receiver->accessChannel(0) = v; + for(typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 1); + } + for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 1); } - template - void UnidirectionalProcessArrayTest::testSet() { - typename std::pair::SharedPtr, typename ProcessArray::SharedPtr> - senderReceiver = createSynchronizedProcessArray(N_ELEMENTS); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; +} - // Test the assignment of a vector. - std::vector v(N_ELEMENTS, SOME_NUMBER + 1); - sender->accessChannel(0) = v; - receiver->accessChannel(0) = v; - for(typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 1); - } - for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 1); - } +BOOST_AUTO_TEST_CASE_TEMPLATE( testSwap, T, test_types ) { + + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + // Test swapping with a vector + typename std::vector v(N_ELEMENTS, SOME_NUMBER); + sender->accessChannel(0).swap(v); + for(typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } + for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { + BOOST_CHECK(*i == 0); } + sender->accessChannel(0).swap(v); + receiver->accessChannel(0).swap(v); + for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); + } + for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { + BOOST_CHECK(*i == 0); + } +} - template - void UnidirectionalProcessArrayTest::testSwap() { +BOOST_AUTO_TEST_CASE_TEMPLATE( testSendNotification, T, test_types ) { + boost::shared_ptr sendNotificationListener( + boost::make_shared()); + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, + TimeStampSource::SharedPtr(), sendNotificationListener); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + BOOST_CHECK(sendNotificationListener->count == 0); + sender->write(); + BOOST_CHECK(sendNotificationListener->count == 1); + BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); + sender->write(); + BOOST_CHECK(sendNotificationListener->count == 2); + BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); +} - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; +BOOST_AUTO_TEST_CASE_TEMPLATE( testTimeStampSource, T, test_types ) { + TimeStampSource::SharedPtr timeStampSource( + boost::make_shared()); + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, + timeStampSource); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + sender->write(); + receiver->readNonBlocking(); + BOOST_CHECK(receiver->getTimeStamp().index0 == 0); + sender->write(); + receiver->readNonBlocking(); + BOOST_CHECK(receiver->getTimeStamp().index0 == 1); + sender->write(); + receiver->readNonBlocking(); + BOOST_CHECK(receiver->getTimeStamp().index0 == 2); +} - // Test swapping with a vector - typename std::vector v(N_ELEMENTS, SOME_NUMBER); - sender->accessChannel(0).swap(v); - for(typename std::vector::iterator i = sender->accessChannel(0).begin(); i != sender->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { - BOOST_CHECK(*i == 0); - } - sender->accessChannel(0).swap(v); - receiver->accessChannel(0).swap(v); - for(typename std::vector::iterator i = receiver->accessChannel(0).begin(); i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - for (typename std::vector::iterator i = v.begin(); i != v.end(); ++i) { - BOOST_CHECK(*i == 0); - } +BOOST_AUTO_TEST_CASE_TEMPLATE( testSynchronization, T, test_types ) { + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + // If we send three values consecutively, they all should be received because the queue length is two and there is + // the additional atomic triple buffer holding a third value + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER); + sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 1); + sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 2); + sender->writeDestructively(); + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER); } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 1); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 2); + } + // We have received all values, so no more values should be available. + BOOST_CHECK(!receiver->readNonBlocking()); + + // Now we try to send four values consecutively. This should result in the last but one value being dropped. The + // latest value should be preserved, since it is in the atomic triple buffer + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 3); + sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 4); + sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); + sender->writeDestructively(); + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 6); + sender->writeDestructively(); + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 3); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 4); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 6); + } + // We have received all values, so no more values should be available. + BOOST_CHECK(!receiver->readNonBlocking()); - template - void UnidirectionalProcessArrayTest::testSendNotification() { - boost::shared_ptr sendNotificationListener( - boost::make_shared()); - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, - TimeStampSource::SharedPtr(), sendNotificationListener); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - BOOST_CHECK(sendNotificationListener->count == 0); - sender->write(); - BOOST_CHECK(sendNotificationListener->count == 1); - BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); - sender->write(); - BOOST_CHECK(sendNotificationListener->count == 2); - BOOST_CHECK(sendNotificationListener->lastProcessVariable == receiver); - } - - template - void UnidirectionalProcessArrayTest::testTimeStampSource() { - TimeStampSource::SharedPtr timeStampSource( - boost::make_shared()); - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false, - timeStampSource); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - sender->write(); - receiver->readNonBlocking(); - BOOST_CHECK(receiver->getTimeStamp().index0 == 0); - sender->write(); - receiver->readNonBlocking(); - BOOST_CHECK(receiver->getTimeStamp().index0 == 1); - sender->write(); - receiver->readNonBlocking(); - BOOST_CHECK(receiver->getTimeStamp().index0 == 2); - } - - template - void UnidirectionalProcessArrayTest::testSynchronization() { - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - // If we send three values consecutively, they all should be received because the queue length is two and there is - // the additional atomic triple buffer holding a third value - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER); - sender->writeDestructively(); - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 1); - sender->writeDestructively(); - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 2); - sender->writeDestructively(); - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 1); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 2); - } - // We have received all values, so no more values should be available. - BOOST_CHECK(!receiver->readNonBlocking()); - - // Now we try to send four values consecutively. This should result in the last but one value being dropped. The - // latest value should be preserved, since it is in the atomic triple buffer - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 3); - sender->writeDestructively(); - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 4); - sender->writeDestructively(); - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); + // Same as before but this time with more values + for(int i=0; i<10; ++i) { + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + i); sender->writeDestructively(); - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 6); - sender->writeDestructively(); - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 3); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 4); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK_EQUAL(*i, SOME_NUMBER + 6); - } - // We have received all values, so no more values should be available. - BOOST_CHECK(!receiver->readNonBlocking()); - - // Same as before but this time with more values - for(int i=0; i<10; ++i) { - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + i); - sender->writeDestructively(); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 0); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 1); - } - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 9); - } - // We have received all values, so no more values should be available. - BOOST_CHECK(!receiver->readNonBlocking()); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 0); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 1); + } + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 9); + } + // We have received all values, so no more values should be available. + BOOST_CHECK(!receiver->readNonBlocking()); + + // When we send non-destructively, the value should also be preserved on the + // sender side. + sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); + for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); + i != receiver->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 5); + } + for (typename std::vector::iterator i = sender->accessChannel(0).begin(); + i != sender->accessChannel(0).end(); ++i) { + BOOST_CHECK(*i == SOME_NUMBER + 5); + } - // When we send non-destructively, the value should also be preserved on the - // sender side. - sender->accessChannel(0).assign(N_ELEMENTS, SOME_NUMBER + 5); - sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - for (typename std::vector::iterator i = receiver->accessChannel(0).begin(); - i != receiver->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 5); - } - for (typename std::vector::iterator i = sender->accessChannel(0).begin(); - i != sender->accessChannel(0).end(); ++i) { - BOOST_CHECK(*i == SOME_NUMBER + 5); - } + // Calling writeDestructively() on a sender that has not the corresponding + // flag set should result in an exception. + senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false); + sender = senderReceiver.first; + receiver = senderReceiver.second; + BOOST_CHECK_THROW(sender->writeDestructively(), std::runtime_error); +} - // Calling writeDestructively() on a sender that has not the corresponding - // flag set should result in an exception. - senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, false); - sender = senderReceiver.first; - receiver = senderReceiver.second; - BOOST_CHECK_THROW(sender->writeDestructively(), std::runtime_error); - } - - template - void UnidirectionalProcessArrayTest::testVersionNumbers() { - typename std::pair::SharedPtr, - typename ProcessArray::SharedPtr> senderReceiver = - createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true, - TimeStampSource::SharedPtr()); - typename ProcessArray::SharedPtr sender = senderReceiver.first; - typename ProcessArray::SharedPtr receiver = senderReceiver.second; - // After sending destructively and receiving a value, the version number on - // the receiver should be greater. - VersionNumber initialVersionNumber = receiver->getVersionNumber(); - sender->accessChannel(0)[0] = 1; - sender->writeDestructively(); - BOOST_CHECK(receiver->readNonBlocking()); - VersionNumber versionNumber = receiver->getVersionNumber(); - BOOST_CHECK(versionNumber > initialVersionNumber); - BOOST_CHECK(receiver->accessChannel(0)[0] == 1); - // When we send again specifying the same version number, there should still be the update of the receiver. - sender->accessChannel(0)[0] = 2; - sender->writeDestructively(versionNumber); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(versionNumber == receiver->getVersionNumber()); - BOOST_CHECK(receiver->accessChannel(0)[0] == 2); - // When we explicitly use a greater version number, the receiver should be - // updated again. - sender->accessChannel(0)[0] = 3; - sender->writeDestructively(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(receiver->getVersionNumber() > versionNumber); - BOOST_CHECK(receiver->accessChannel(0)[0] == 3); - // Even when sending again an update with an older version number, the update should be seen by the receiver. - sender->accessChannel(0)[0] = 4; - sender->writeDestructively(versionNumber); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(versionNumber == receiver->getVersionNumber()); - BOOST_CHECK(receiver->accessChannel(0)[0] == 4); - // When we send non-destructively, the version number on the sender and the - // receiver should match after sending and receiving. - versionNumber = receiver->getVersionNumber(); - sender->accessChannel(0)[0] = 5; +BOOST_AUTO_TEST_CASE_TEMPLATE( testVersionNumbers, T, test_types ) { + typename std::pair::SharedPtr, + typename ProcessArray::SharedPtr> senderReceiver = + createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true, + TimeStampSource::SharedPtr()); + typename ProcessArray::SharedPtr sender = senderReceiver.first; + typename ProcessArray::SharedPtr receiver = senderReceiver.second; + // After sending destructively and receiving a value, the version number on + // the receiver should be greater. + VersionNumber initialVersionNumber = receiver->getVersionNumber(); + sender->accessChannel(0)[0] = 1; + sender->writeDestructively(); + BOOST_CHECK(receiver->readNonBlocking()); + VersionNumber versionNumber = receiver->getVersionNumber(); + BOOST_CHECK(versionNumber > initialVersionNumber); + BOOST_CHECK(receiver->accessChannel(0)[0] == 1); + // When we send again specifying the same version number, there should still be the update of the receiver. + sender->accessChannel(0)[0] = 2; + sender->writeDestructively(versionNumber); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(versionNumber == receiver->getVersionNumber()); + BOOST_CHECK(receiver->accessChannel(0)[0] == 2); + // When we explicitly use a greater version number, the receiver should be + // updated again. + sender->accessChannel(0)[0] = 3; + sender->writeDestructively(); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(receiver->getVersionNumber() > versionNumber); + BOOST_CHECK(receiver->accessChannel(0)[0] == 3); + // Even when sending again an update with an older version number, the update should be seen by the receiver. + sender->accessChannel(0)[0] = 4; + sender->writeDestructively(versionNumber); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(versionNumber == receiver->getVersionNumber()); + BOOST_CHECK(receiver->accessChannel(0)[0] == 4); + // When we send non-destructively, the version number on the sender and the + // receiver should match after sending and receiving. + versionNumber = receiver->getVersionNumber(); + sender->accessChannel(0)[0] = 5; + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK(receiver->getVersionNumber() > versionNumber); + BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); + BOOST_CHECK(receiver->accessChannel(0)[0] == 5); + + // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element + // into the queue + for(int i=0; i<10; ++i) { + sender->accessData(0) = 33+i; sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK(receiver->getVersionNumber() > versionNumber); - BOOST_CHECK(receiver->getVersionNumber() == sender->getVersionNumber()); - BOOST_CHECK(receiver->accessChannel(0)[0] == 5); - - // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element - // into the queue - for(int i=0; i<10; ++i) { - sender->accessData(0) = 33+i; - sender->write(); - } - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 33); - BOOST_CHECK(receiver->readNonBlocking()); // after this line the buffer should be empty - BOOST_CHECK_EQUAL(receiver->accessData(0), 34); - sender->accessData(0) = 12; - sender->write(); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 42); - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 12); - BOOST_CHECK(!(receiver->readNonBlocking())); } + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 33); + BOOST_CHECK(receiver->readNonBlocking()); // after this line the buffer should be empty + BOOST_CHECK_EQUAL(receiver->accessData(0), 34); + sender->accessData(0) = 12; + sender->write(); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 12); + BOOST_CHECK(!(receiver->readNonBlocking())); +} - template - void UnidirectionalProcessArrayTest::testBlockingRead() { - auto senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); - auto sender = senderReceiver.first; - auto receiver = senderReceiver.second; - - // blocking read should just return the value if it is already there - sender->accessData(0) = 42; +BOOST_AUTO_TEST_CASE_TEMPLATE( testBlockingRead, T, test_types ) { + auto senderReceiver = createSynchronizedProcessArray(N_ELEMENTS, "", "", "", 0, 2, true); + auto sender = senderReceiver.first; + auto receiver = senderReceiver.second; + + // blocking read should just return the value if it is already there + sender->accessData(0) = 42; + sender->write(); + receiver->read(); + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + + // start blocking read first in the background and send then the data + { + std::thread backgroundTask( [&receiver] { receiver->read(); } ); + usleep(200000); + sender->accessData(0) = 43; sender->write(); - receiver->read(); - BOOST_CHECK_EQUAL(receiver->accessData(0), 42); - - // start blocking read first in the background and send then the data - { - std::thread backgroundTask( [&receiver] { receiver->read(); } ); - usleep(200000); - sender->accessData(0) = 43; - sender->write(); - backgroundTask.join(); - BOOST_CHECK_EQUAL(receiver->accessData(0), 43); - } + backgroundTask.join(); + BOOST_CHECK_EQUAL(receiver->accessData(0), 43); + } - // same with multiple transfers (not more than the queue length to avoid race conditions) - { - std::thread backgroundTask( [&receiver] { - for(int i=0; i<2; ++i) { - receiver->read(); - BOOST_CHECK_EQUAL(static_cast(receiver->accessData(0)), 2 + i); - } - }); + // same with multiple transfers (not more than the queue length to avoid race conditions) + { + std::thread backgroundTask( [&receiver] { for(int i=0; i<2; ++i) { - usleep(10000); - sender->accessData(0) = 2+i; - sender->write(); + receiver->read(); + BOOST_CHECK_EQUAL(static_cast(receiver->accessData(0)), 2 + i); } - backgroundTask.join(); + }); + for(int i=0; i<2; ++i) { + usleep(10000); + sender->accessData(0) = 2+i; + sender->write(); } + backgroundTask.join(); } +} - template - void UnidirectionalProcessArrayTest::testReadLatest() { - auto senderReceiver = createSynchronizedProcessArray(1); - auto sender = senderReceiver.first; - auto receiver = senderReceiver.second; - - // readLatest with only one element in the queue should read that element - sender->accessData(0) = 42; - sender->write(); - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 42); - - // readLatest with no element in the queue will return false and not change the value - BOOST_CHECK(!receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 42); - - // readLatest with two elements (queue is full) should return the second element - sender->accessData(0) = 66; +BOOST_AUTO_TEST_CASE_TEMPLATE( testReadLatest, T, test_types ) { + auto senderReceiver = createSynchronizedProcessArray(1); + auto sender = senderReceiver.first; + auto receiver = senderReceiver.second; + + // readLatest with only one element in the queue should read that element + sender->accessData(0) = 42; + sender->write(); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + + // readLatest with no element in the queue will return false and not change the value + BOOST_CHECK(!receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 42); + + // readLatest with two elements (queue is full) should return the second element + sender->accessData(0) = 66; + sender->write(); + sender->accessData(0) = 77; + sender->write(); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 77); + BOOST_CHECK(!receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 77); + + // readLatest with three elements (queue is full and atomic buffer is filled) should return the third element + sender->accessData(0) = 33; + sender->write(); + sender->accessData(0) = 44; + sender->write(); + sender->accessData(0) = 55; + sender->write(); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 55); + + // readLatest with 20 elements (certain queue overflow) should still return the last element + for(int i=0; i<20; ++i) { + sender->accessData(0) = i; sender->write(); - sender->accessData(0) = 77; - sender->write(); - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 77); - BOOST_CHECK(!receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 77); + } + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 19); + BOOST_CHECK(!(receiver->readNonBlocking())); - // readLatest with three elements (queue is full and atomic buffer is filled) should return the third element - sender->accessData(0) = 33; + // redo last step to make sure no buffers are lost when having a queue overflow + for(int i=0; i<20; ++i) { + sender->accessData(0) = 100+i; sender->write(); - sender->accessData(0) = 44; - sender->write(); - sender->accessData(0) = 55; + } + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 119); + BOOST_CHECK(!(receiver->readNonBlocking())); + + // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element + // into the queue - which should then be read by readLatest + for(int i=0; i<10; ++i) { + sender->accessData(0) = 33+i; sender->write(); - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 55); - - // readLatest with 20 elements (certain queue overflow) should still return the last element - for(int i=0; i<20; ++i) { - sender->accessData(0) = i; - sender->write(); - } - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 19); - BOOST_CHECK(!(receiver->readNonBlocking())); - - // redo last step to make sure no buffers are lost when having a queue overflow - for(int i=0; i<20; ++i) { - sender->accessData(0) = 100+i; - sender->write(); - } - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 119); - BOOST_CHECK(!(receiver->readNonBlocking())); - - // provoke buffer overrun, read until the queue is empty (but triple buffer still has value) and put a new element - // into the queue - which should then be read by readLatest - for(int i=0; i<10; ++i) { - sender->accessData(0) = 33+i; - sender->write(); - } - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 33); - BOOST_CHECK(receiver->readNonBlocking()); // after this line the queue should be empty - BOOST_CHECK_EQUAL(receiver->accessData(0), 34); - sender->accessData(0) = 12; + } + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 33); + BOOST_CHECK(receiver->readNonBlocking()); // after this line the queue should be empty + BOOST_CHECK_EQUAL(receiver->accessData(0), 34); + sender->accessData(0) = 12; + sender->write(); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 12); + BOOST_CHECK(!(receiver->readLatest())); + + // provoke that the future obtained in the very beginning of doReadTransferLatest() is pointing to the buffer on + // the triple buffer + for(int i=0; i<10; ++i) { + sender->accessData(0) = 11+i; sender->write(); - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 12); - BOOST_CHECK(!(receiver->readLatest())); - - // provoke that the future obtained in the very beginning of doReadTransferLatest() is pointing to the buffer on - // the triple buffer - for(int i=0; i<10; ++i) { - sender->accessData(0) = 11+i; + } + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 11); + BOOST_CHECK(receiver->readNonBlocking()); // after this line the queue should be empty + BOOST_CHECK_EQUAL(receiver->accessData(0), 12); + BOOST_CHECK(receiver->readLatest()); + BOOST_CHECK_EQUAL(receiver->accessData(0), 20); + BOOST_CHECK(!(receiver->readLatest())); + for(int i=0; i<100; ++i) { // check that all buffers are still properly working + for(int k=0; k<3; ++k) { + sender->accessData(0) = 10*i + k; sender->write(); } - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 11); - BOOST_CHECK(receiver->readNonBlocking()); // after this line the queue should be empty - BOOST_CHECK_EQUAL(receiver->accessData(0), 12); - BOOST_CHECK(receiver->readLatest()); - BOOST_CHECK_EQUAL(receiver->accessData(0), 20); - BOOST_CHECK(!(receiver->readLatest())); - for(int i=0; i<100; ++i) { // check that all buffers are still properly working - for(int k=0; k<3; ++k) { - sender->accessData(0) = 10*i + k; - sender->write(); - } - for(int k=0; k<3; ++k) { - BOOST_CHECK(receiver->readNonBlocking()); - BOOST_CHECK_EQUAL(receiver->accessData(0), T(10*i + k)); - } + for(int k=0; k<3; ++k) { + BOOST_CHECK(receiver->readNonBlocking()); + BOOST_CHECK_EQUAL(receiver->accessData(0), T(10*i + k)); } - - } -} //namespace ChimeraTK - -test_suite* -init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { - framework::master_test_suite().p_name.value = "UnidirectionalProcessArray test suite"; - - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - framework::master_test_suite().add( - new ChimeraTK::UnidirectionalProcessArrayTestSuite); - return NULL; } From 9b2f2425c7afa10e9d533d2b514672c4b5cba07e Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 21 Jun 2018 17:20:13 +0200 Subject: [PATCH 354/786] in interrupt(), use push_overwrite_exception() to make sure the exception ends up in the queue even if the queue is full. --- .../ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 62aca8e..87b20bd 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -176,7 +176,7 @@ namespace ChimeraTK { throw boost::thread_interrupted(); } catch(...) { - _sharedState._queue.push_exception(std::current_exception()); + _sharedState._queue.push_overwrite_exception(std::current_exception()); } } From d8103f9cc73aad86736515d716b7f66f4d6dacf3 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 3 Jul 2018 13:54:48 +0200 Subject: [PATCH 355/786] stop using the deprecated namespace mtca4u and use ChimeraTK instead --- .../BidirectionalProcessArray.h | 32 ++++++------ .../ControlSystemPVManager.h | 22 ++++---- .../ControlSystemSynchronizationUtility.h | 6 +-- .../ControlSystemAdapter/DevicePVManager.h | 34 ++++++------ .../DeviceSynchronizationUtility.h | 6 +-- .../ControlSystemAdapter/PVManager.h | 28 +++++----- .../PersistentDataStorage.h | 12 ++--- .../ControlSystemAdapter/ProcessArray.h | 22 ++++---- .../ControlSystemAdapter/ProcessVariable.h | 4 +- .../ControlSystemAdapter/TimeStamp.h | 4 +- .../TypeChangingDecorator.h | 50 +++++++++--------- .../UnidirectionalProcessArray.h | 52 +++++++++---------- src/ControlSystemPVManager.cc | 2 +- src/ControlSystemSynchronizationUtility.cc | 4 +- src/DevicePVManager.cc | 2 +- src/DeviceSynchronizationUtility.cc | 4 +- src/PVManager.cc | 2 +- src/TypeChangingDecorator.cc | 6 +-- tests/src/stresstest.cpp | 4 +- tests/src/testAsyncRead.cpp | 26 +++++----- tests/src/testTypeChangingDecorator.cpp | 24 ++++----- 21 files changed, 173 insertions(+), 173 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index a6aeb8f..1b9d817 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include "ProcessArray.h" #include "ProcessVariableListener.h" @@ -71,7 +71,7 @@ namespace ChimeraTK { template std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( std::size_t size, - const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + const ChimeraTK::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 3, TimeStampSource::SharedPtr timeStampSource1 = TimeStampSource::SharedPtr(), TimeStampSource::SharedPtr timeStampSource2 = TimeStampSource::SharedPtr(), @@ -126,7 +126,7 @@ namespace ChimeraTK { template std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( const std::vector& initialValue, - const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + const ChimeraTK::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", std::size_t numberOfBuffers = 3, TimeStampSource::SharedPtr timeStampSource1 = TimeStampSource::SharedPtr(), TimeStampSource::SharedPtr timeStampSource2 = TimeStampSource::SharedPtr(), @@ -158,13 +158,13 @@ namespace ChimeraTK { friends so that they can access the private _partner field. */ friend std::pair::SharedPtr, typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray<>( - const std::vector&, const mtca4u::RegisterPath &, + const std::vector&, const ChimeraTK::RegisterPath &, const std::string &, const std::string &, std::size_t, TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, ProcessVariableListener::SharedPtr, ProcessVariableListener::SharedPtr, const AccessModeFlags &flags); friend std::pair::SharedPtr, typename ProcessArray::SharedPtr> createBidirectionalSynchronizedProcessArray<>( - std::size_t, const mtca4u::RegisterPath &, const std::string &, + std::size_t, const ChimeraTK::RegisterPath &, const std::string &, const std::string &, T, std::size_t, TimeStampSource::SharedPtr, TimeStampSource::SharedPtr, ProcessVariableListener::SharedPtr, ProcessVariableListener::SharedPtr, const AccessModeFlags &flags); @@ -176,7 +176,7 @@ namespace ChimeraTK { * createBidirectionalSynchronizedProcessArray functions. */ BidirectionalProcessArray(std::size_t uniqueId, - const mtca4u::RegisterPath& name, const std::string &unit, + const ChimeraTK::RegisterPath& name, const std::string &unit, const std::string &description, bool allowPersistentDataStorage, typename ProcessArray::SharedPtr receiver, typename ProcessArray::SharedPtr sender, @@ -200,7 +200,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; - mtca4u::TransferFuture doReadTransferAsync() override; + ChimeraTK::TransferFuture doReadTransferAsync() override; void doPostRead() override; @@ -324,7 +324,7 @@ namespace ChimeraTK { template BidirectionalProcessArray::BidirectionalProcessArray(std::size_t uniqueId, - const mtca4u::RegisterPath &name, const std::string &unit, + const ChimeraTK::RegisterPath &name, const std::string &unit, const std::string &description, bool allowPersistentDataStorage, typename ProcessArray::SharedPtr receiver, typename ProcessArray::SharedPtr sender, @@ -358,8 +358,8 @@ namespace ChimeraTK { // Allocate and initialize the buffer of the base class we copy the value // from the receiver because the calling code should already have take care // of initializing that value. - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->accessChannel(0); + ChimeraTK::NDRegisterAccessor::buffer_2D.resize(1); + ChimeraTK::NDRegisterAccessor::buffer_2D[0] = receiver->accessChannel(0); } /*********************************************************************************************************************/ @@ -393,7 +393,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - mtca4u::TransferFuture BidirectionalProcessArray::doReadTransferAsync() { + ChimeraTK::TransferFuture BidirectionalProcessArray::doReadTransferAsync() { return {_receiver->readAsync(), this}; } @@ -494,10 +494,10 @@ namespace ChimeraTK { } _persistentDataStorage = storage; _persistentDataStorageID = _persistentDataStorage->registerVariable( - mtca4u::TransferElement::getName(), - mtca4u::NDRegisterAccessor::getNumberOfSamples()); + ChimeraTK::TransferElement::getName(), + ChimeraTK::NDRegisterAccessor::getNumberOfSamples()); if (sendInitialValue) { - mtca4u::NDRegisterAccessor::buffer_2D[0] = + ChimeraTK::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); doWriteTransfer(); } @@ -510,7 +510,7 @@ namespace ChimeraTK { template typename std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( std::size_t size, - const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, + const ChimeraTK::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, std::size_t numberOfBuffers, TimeStampSource::SharedPtr timeStampSource1, TimeStampSource::SharedPtr timeStampSource2, ProcessVariableListener::SharedPtr sendNotificationListener1, ProcessVariableListener::SharedPtr sendNotificationListener2, const AccessModeFlags &flags ) @@ -550,7 +550,7 @@ namespace ChimeraTK { template typename std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createBidirectionalSynchronizedProcessArray( const std::vector& initialValue, - const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, + const ChimeraTK::RegisterPath & name, const std::string &unit, const std::string &description, std::size_t numberOfBuffers, TimeStampSource::SharedPtr timeStampSource1, TimeStampSource::SharedPtr timeStampSource2, ProcessVariableListener::SharedPtr sendNotificationListener1, ProcessVariableListener::SharedPtr sendNotificationListener2, const AccessModeFlags &flags ) diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h index 06b916f..c094211 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemPVManager.h @@ -55,9 +55,9 @@ namespace ChimeraTK { /** * Returns a reference to a process array that has been created earlier * using the - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t, const AccessModeFlags&)} * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t, const AccessModeFlags&)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type @@ -65,14 +65,14 @@ namespace ChimeraTK { */ template typename ProcessArray::SharedPtr getProcessArray( - const mtca4u::RegisterPath& processVariableName) const; + const ChimeraTK::RegisterPath& processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t, const AccessModeFlags&)}, * or - * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link DevicePVManager::createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t, const AccessModeFlags&)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. */ @@ -80,14 +80,14 @@ namespace ChimeraTK { * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid * non-working links: * - * The {@link getProcessArray(const mtca4u::RegisterPath&)} methods should be preferred + * The {@link getProcessArray(const ChimeraTK::RegisterPath&)} methods should be preferred */ - /** The \c getProcessScalar(const mtca4u::RegisterPath&) and - * \c getProcessArray(const mtca4u::RegisterPath&) methods should be preferred + /** The \c getProcessScalar(const ChimeraTK::RegisterPath&) and + * \c getProcessArray(const ChimeraTK::RegisterPath&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( - const mtca4u::RegisterPath& processVariableName) const; + const ChimeraTK::RegisterPath& processVariableName) const; /** * Returns a vector containing all process variables that are registered @@ -111,7 +111,7 @@ namespace ChimeraTK { * notification. */ ProcessVariable::SharedPtr nextNotification(); - + /** * Enable the persistent data storage system provided by the ControlSystemAdapter. This function requires an * existing instance of an ApplicationBase class. @@ -139,7 +139,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr ControlSystemPVManager::getProcessArray( - const mtca4u::RegisterPath& processVariableName) const { + const ChimeraTK::RegisterPath& processVariableName) const { auto pv = _pvManager->getProcessArray(processVariableName).first; if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); return pv; diff --git a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h index 9a54345..22dd7d1 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h @@ -37,14 +37,14 @@ namespace ChimeraTK { /** * @deprecated Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead. */ - void addReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName, + void addReceiveNotificationListener(ChimeraTK::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener); /** * Removes a receive notification listener that has previously been * registered with - * {@link #addReceiveNotificationListener(mtca4u::RegisterPath const &, ProcessVariableListener::SharedPtr)}. + * {@link #addReceiveNotificationListener(ChimeraTK::RegisterPath const &, ProcessVariableListener::SharedPtr)}. * If no listener is registered for the specified process variable, * this method does nothing. */ @@ -53,7 +53,7 @@ namespace ChimeraTK { /** * @deprecated Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead. */ - void removeReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName); + void removeReceiveNotificationListener(ChimeraTK::RegisterPath const & processVariableName); /** diff --git a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h index b32d68e..24aeef4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/DevicePVManager.h @@ -80,8 +80,8 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, - const mtca4u::RegisterPath& processVariableName, std::size_t size, - const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + const ChimeraTK::RegisterPath& processVariableName, std::size_t size, + const std::string& unit = ChimeraTK::TransferElement::unitNotSet, const std::string& description = "", T initialValue = T(), bool maySendDestructively = false, std::size_t numberOfBuffers = 3, const AccessModeFlags &flags={AccessMode::wait_for_new_data}); @@ -112,31 +112,31 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr createProcessArray( SynchronizationDirection synchronizationDirection, - const mtca4u::RegisterPath& processVariableName, const std::vector& initialValue, - const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + const ChimeraTK::RegisterPath& processVariableName, const std::vector& initialValue, + const std::string& unit = ChimeraTK::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 3, const AccessModeFlags &flags={AccessMode::wait_for_new_data}); /** * Returns a reference to a process array that has been created earlier * using the - * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t, const AccessModeFlags&)} * or - * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t, const AccessModeFlags&)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. Throws a bad_cast exception if * there is a process scalar or array with the specified name but its type * does not match. */ template typename ProcessArray::SharedPtr getProcessArray( - const mtca4u::RegisterPath& processVariableName) const; + const ChimeraTK::RegisterPath& processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier * using the - * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t)}, + * {@link createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, std::size_t, const std::string&, const std::string&, T, bool, std::size_t, const AccessModeFlags&)}, * or - * {@link createProcessArray(SynchronizationDirection, const mtca4u::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t)} + * {@link createProcessArray(SynchronizationDirection, const ChimeraTK::RegisterPath&, const std::vector&, const std::string&, const std::string&, bool, std::size_t, const AccessModeFlags&)} * method. Returns a pointer to null if there is no process * scalar or array with the specified name. */ @@ -144,15 +144,15 @@ namespace ChimeraTK { * FIXME: It these should be links, but Doxygen can't resolve then, so we leave it normal to avoid * non-working links: * - * The {@link getProcessScalar(const mtca4u::RegisterPath&)} and - * {@link getProcessArray(const mtca4u::RegisterPath&)} methods should be preferred + * The {@link getProcessScalar(const ChimeraTK::RegisterPath&)} and + * {@link getProcessArray(const ChimeraTK::RegisterPath&)} methods should be preferred */ - /** The \c getProcessScalar(const mtca4u::RegisterPath&) and - * \c getProcessArray(const mtca4u::RegisterPath&) methods should be preferred + /** The \c getProcessScalar(const ChimeraTK::RegisterPath&) and + * \c getProcessArray(const ChimeraTK::RegisterPath&) methods should be preferred * if the type of the process variable is known at compile time. */ ProcessVariable::SharedPtr getProcessVariable( - const mtca4u::RegisterPath& processVariableName) const; + const ChimeraTK::RegisterPath& processVariableName) const; /** * Returns a vector containing all process variables that are registered @@ -226,7 +226,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, - const mtca4u::RegisterPath& processVariableName, std::size_t size, + const ChimeraTK::RegisterPath& processVariableName, std::size_t size, const std::string& unit, const std::string& description, T initialValue, bool maySendDestructively, std::size_t numberOfBuffers, const AccessModeFlags &flags) { @@ -255,7 +255,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::createProcessArray( SynchronizationDirection synchronizationDirection, - const mtca4u::RegisterPath& processVariableName, const std::vector& initialValue, + const ChimeraTK::RegisterPath& processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers, const AccessModeFlags &flags) { @@ -283,7 +283,7 @@ namespace ChimeraTK { template typename ProcessArray::SharedPtr DevicePVManager::getProcessArray( - const mtca4u::RegisterPath& processVariableName) const { + const ChimeraTK::RegisterPath& processVariableName) const { return _pvManager->getProcessArray(processVariableName).second; } diff --git a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h index d3b4fa8..cfc2a03 100644 --- a/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h +++ b/include/ChimeraTK/ControlSystemAdapter/DeviceSynchronizationUtility.h @@ -37,13 +37,13 @@ namespace ChimeraTK { /** * @deprecated Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead. */ - void addReceiveNotificationListener(mtca4u::RegisterPath const & processVariableName, + void addReceiveNotificationListener(ChimeraTK::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener); /** * Removes a receive notification listener that has previously been * registered with - * {@link #addReceiveNotificationListener(mtca4u::RegisterPath const &, ProcessVariableListener::SharedPtr)}. + * {@link #addReceiveNotificationListener(ChimeraTK::RegisterPath const &, ProcessVariableListener::SharedPtr)}. * If no listener is registered for the specified process variable name, * this method does nothing. */ @@ -53,7 +53,7 @@ namespace ChimeraTK { * @deprecated Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead. */ void removeReceiveNotificationListener( - mtca4u::RegisterPath const & processVariableName); + ChimeraTK::RegisterPath const & processVariableName); /** * Runs receive on all process variables that have data pending. This method diff --git a/include/ChimeraTK/ControlSystemAdapter/PVManager.h b/include/ChimeraTK/ControlSystemAdapter/PVManager.h index 858b7ed..b7a54c8 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PVManager.h +++ b/include/ChimeraTK/ControlSystemAdapter/PVManager.h @@ -11,7 +11,7 @@ #include #include "ProcessArray.h" -#include +#include #include "PVManagerDecl.h" @@ -59,7 +59,7 @@ namespace ChimeraTK { * (e.g. an iterator). * @todo FIXME: This was an unordered_map of strings, but it was changed to register path. The register path does not have a hash, thus we fall back to std::map. Write a std::hash if you cant to improve the performance. */ - typedef std::map ProcessVariableMap; + typedef std::map ProcessVariableMap; /** * Creates a new process array for transferring data between the device @@ -85,9 +85,9 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createBidirectionalProcessArray( - mtca4u::RegisterPath const & processVariableName, + ChimeraTK::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit = - mtca4u::TransferElement::unitNotSet, + ChimeraTK::TransferElement::unitNotSet, const std::string& description = "", std::size_t numberOfBuffers = 2); /** @@ -119,8 +119,8 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayDeviceToControlSystem( - mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, - const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + ChimeraTK::RegisterPath const & processVariableName, const std::vector& initialValue, + const std::string& unit = ChimeraTK::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 3, const AccessModeFlags &flags={AccessMode::wait_for_new_data}); @@ -153,8 +153,8 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> createProcessArrayControlSystemToDevice( - mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, - const std::string& unit = mtca4u::TransferElement::unitNotSet, const std::string& description = "", + ChimeraTK::RegisterPath const & processVariableName, const std::vector& initialValue, + const std::string& unit = ChimeraTK::TransferElement::unitNotSet, const std::string& description = "", bool maySendDestructively = false, std::size_t numberOfBuffers = 3, const AccessModeFlags &flags={AccessMode::wait_for_new_data}); @@ -174,7 +174,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> getProcessArray( - mtca4u::RegisterPath const & processVariableName) const; + ChimeraTK::RegisterPath const & processVariableName) const; /** * Returns a reference to a process scalar or array that has been created earlier @@ -188,7 +188,7 @@ namespace ChimeraTK { * device library. */ std::pair getProcessVariable( - mtca4u::RegisterPath const & processVariableName) const; + ChimeraTK::RegisterPath const & processVariableName) const; /** * Returns the map containing all process variables, using the names as @@ -457,7 +457,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createBidirectionalProcessArray( - mtca4u::RegisterPath const & processVariableName, + ChimeraTK::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, std::size_t numberOfBuffers) { if (_processVariables.find(processVariableName) @@ -502,7 +502,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayDeviceToControlSystem( - mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, + ChimeraTK::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers, const AccessModeFlags &flags) { if (_processVariables.find(processVariableName) @@ -539,7 +539,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::createProcessArrayControlSystemToDevice( - mtca4u::RegisterPath const & processVariableName, const std::vector& initialValue, + ChimeraTK::RegisterPath const & processVariableName, const std::vector& initialValue, const std::string& unit, const std::string& description, bool maySendDestructively, std::size_t numberOfBuffers, const AccessModeFlags &flags) { if (_processVariables.find(processVariableName) @@ -577,7 +577,7 @@ namespace ChimeraTK { template std::pair::SharedPtr, typename ProcessArray::SharedPtr> PVManager::getProcessArray( - mtca4u::RegisterPath const & processVariableName) const { + ChimeraTK::RegisterPath const & processVariableName) const { ProcessVariableSharedPtrPair processVariable = getProcessVariable( processVariableName); typename ProcessArray::SharedPtr csPV = boost::dynamic_pointer_cast< diff --git a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h index 7b45920..26e15a7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h +++ b/include/ChimeraTK/ControlSystemAdapter/PersistentDataStorage.h @@ -11,8 +11,8 @@ #include #include -#include -#include +#include +#include namespace xmlpp { class Element; @@ -52,7 +52,7 @@ namespace ChimeraTK { * called from within UnidirectionalProcessArray::setPersistentDataStorage() etc. and true when called from * within readFromFile(). */ template - size_t registerVariable(mtca4u::RegisterPath const &name, size_t nElements, bool fromFile=false); + size_t registerVariable(ChimeraTK::RegisterPath const &name, size_t nElements, bool fromFile=false); /** Retrieve the current value for the variable with the given ID */ template @@ -86,7 +86,7 @@ namespace ChimeraTK { std::string _filename; /** Vector of variable names. The index is the ID of the variable. */ - std::vector _variableNames; + std::vector _variableNames; /** Vector of flags whether the variable was registers from the application. The index is the ID of the variable. * This flag is used to clean up variables only coming from the file and are no longer present in the @@ -101,7 +101,7 @@ namespace ChimeraTK { using DataMap = std::map >; /** boost::fusion::map of the data type to the DataMap holding the values for the type */ - mtca4u::TemplateUserTypeMap _dataMap; + ChimeraTK::TemplateUserTypeMap _dataMap; /** */ boost::thread writerThread; @@ -133,7 +133,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - size_t PersistentDataStorage::registerVariable(mtca4u::RegisterPath const &name, size_t nElements, bool fromFile) { + size_t PersistentDataStorage::registerVariable(ChimeraTK::RegisterPath const &name, size_t nElements, bool fromFile) { // check if already existing auto position = std::find(_variableNames.begin(), _variableNames.end(), name); diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index fd6c40a..dfc5248 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -14,8 +14,8 @@ #include #include -#include -#include +#include +#include #include "ProcessVariableListener.h" #include "TimeStampSource.h" @@ -32,7 +32,7 @@ namespace ChimeraTK { * This class is not thread-safe and should only be used from a single thread. */ template - class ProcessArray : public mtca4u::NDRegisterAccessor { + class ProcessArray : public ChimeraTK::NDRegisterAccessor { public: @@ -71,7 +71,7 @@ namespace ChimeraTK { /** * Creates a process array of the specified type. */ - ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, + ProcessArray(InstanceType instanceType, const ChimeraTK::RegisterPath& name, const std::string &unit, const std::string &description, const AccessModeFlags &flags); virtual ~ProcessArray(); @@ -122,19 +122,19 @@ namespace ChimeraTK { return true; } - bool mayReplaceOther(const boost::shared_ptr&) const override { + bool mayReplaceOther(const boost::shared_ptr&) const override { return false; // never true as we shall return false if instance is the same } - std::vector > getHardwareAccessingElements() override { - return { boost::enable_shared_from_this::shared_from_this() }; + std::vector > getHardwareAccessingElements() override { + return { boost::enable_shared_from_this::shared_from_this() }; } - std::list> getInternalElements() override { + std::list> getInternalElements() override { return {}; } - void replaceTransferElement(boost::shared_ptr) override { + void replaceTransferElement(boost::shared_ptr) override { // You can't replace anything here. Just do nothing. } @@ -159,9 +159,9 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - ProcessArray::ProcessArray(InstanceType instanceType, const mtca4u::RegisterPath& name, const std::string &unit, + ProcessArray::ProcessArray(InstanceType instanceType, const ChimeraTK::RegisterPath& name, const std::string &unit, const std::string &description, const AccessModeFlags &flags) - : mtca4u::NDRegisterAccessor(name, unit, description), + : ChimeraTK::NDRegisterAccessor(name, unit, description), _instanceType(instanceType), _flags(flags) {} diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h index 9180eab..79f2a5e 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessVariable.h @@ -1,10 +1,10 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H -#include +#include namespace ChimeraTK{ - typedef mtca4u::TransferElement ProcessVariable; + typedef ChimeraTK::TransferElement ProcessVariable; } #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_PROCESS_VARIABLE_H diff --git a/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h b/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h index 8c85bbd..ce7fb21 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h +++ b/include/ChimeraTK/ControlSystemAdapter/TimeStamp.h @@ -1,10 +1,10 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H -#include +#include namespace ChimeraTK { - using mtca4u::TimeStamp; + using ChimeraTK::TimeStamp; } // namespace ChimeraTK #endif // CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TIME_STAMP_H diff --git a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h index 4d0354d..957d9b4 100644 --- a/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h +++ b/include/ChimeraTK/ControlSystemAdapter/TypeChangingDecorator.h @@ -1,10 +1,10 @@ #ifndef CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H #define CHIMERA_TK_CONTROL_SYSTEM_ADAPTER_TYPE_CHANGING_DECORATOR_H -#include -#include +#include +#include #include -#include +#include namespace ChimeraTK { /** There are three types of TypeChanging decorators which do different data conversions @@ -30,11 +30,11 @@ namespace ChimeraTK { * @param decoratorType The type of decorator you want (see description of DecoratorType) */ template - boost::shared_ptr> getDecorator(const boost::shared_ptr &transferElement, + boost::shared_ptr> getDecorator(const boost::shared_ptr &transferElement, DecoratorType decoratorType = DecoratorType::range_checking); template - boost::shared_ptr> getDecorator(mtca4u::TransferElementAbstractor &transferElement, + boost::shared_ptr> getDecorator(ChimeraTK::TransferElementAbstractor &transferElement, DecoratorType decoratorType = DecoratorType::range_checking) { return getDecorator(transferElement.getHighLevelImplElement(), decoratorType); } @@ -60,11 +60,11 @@ namespace ChimeraTK { * */ template - class TypeChangingDecorator : public mtca4u::NDRegisterAccessorDecorator, public DecoratorTypeHolder { + class TypeChangingDecorator : public ChimeraTK::NDRegisterAccessorDecorator, public DecoratorTypeHolder { public: - TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const &target) noexcept; + TypeChangingDecorator(boost::shared_ptr< ChimeraTK::NDRegisterAccessor< IMPL_T> > const &target) noexcept; virtual void convertAndCopyFromImpl() = 0; virtual void convertAndCopyToImpl() = 0; @@ -87,7 +87,7 @@ namespace ChimeraTK { _target->postWrite(); } - bool mayReplaceOther(const boost::shared_ptr &other) const override { + bool mayReplaceOther(const boost::shared_ptr &other) const override { auto casted = boost::dynamic_pointer_cast const>(other); if(!casted) return false; return _target->mayReplaceOther(casted->_target); @@ -95,7 +95,7 @@ namespace ChimeraTK { protected: - using mtca4u::NDRegisterAccessorDecorator::_target; + using ChimeraTK::NDRegisterAccessorDecorator::_target; }; /** This class is intended as a base class. @@ -191,7 +191,7 @@ namespace ChimeraTK { } }; - using mtca4u::NDRegisterAccessorDecorator::_target; + using ChimeraTK::NDRegisterAccessorDecorator::_target; }; @@ -211,7 +211,7 @@ namespace ChimeraTK { protected: - using mtca4u::NDRegisterAccessorDecorator::_target; + using ChimeraTK::NDRegisterAccessorDecorator::_target; }; @@ -220,8 +220,8 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< mtca4u::NDRegisterAccessor< IMPL_T> > const &target) noexcept - : mtca4u::NDRegisterAccessorDecorator(target) + TypeChangingDecorator::TypeChangingDecorator(boost::shared_ptr< ChimeraTK::NDRegisterAccessor< IMPL_T> > const &target) noexcept + : ChimeraTK::NDRegisterAccessorDecorator(target) {} /*********************************************************************************************************************/ @@ -287,7 +287,7 @@ namespace ChimeraTK { } } - using mtca4u::NDRegisterAccessorDecorator::_target; + using ChimeraTK::NDRegisterAccessorDecorator::_target; }; @@ -312,17 +312,17 @@ namespace ChimeraTK { * We need it to loop up if a decorator has already been created for the transfer element, * and return this if so. Multiple decorators for the same transfer element don't work. */ - std::map< boost::shared_ptr, - boost::shared_ptr > & getGlobalDecoratorMap(); + std::map< boost::shared_ptr, + boost::shared_ptr > & getGlobalDecoratorMap(); template class DecoratorFactory { public: - DecoratorFactory(boost::shared_ptr< mtca4u::TransferElement > theImpl_, DecoratorType wantedDecoratorType_) + DecoratorFactory(boost::shared_ptr< ChimeraTK::TransferElement > theImpl_, DecoratorType wantedDecoratorType_) : theImpl(theImpl_), wantedDecoratorType(wantedDecoratorType_){} - boost::shared_ptr< mtca4u::TransferElement > theImpl; + boost::shared_ptr< ChimeraTK::TransferElement > theImpl; DecoratorType wantedDecoratorType; - mutable boost::shared_ptr > createdDecorator; + mutable boost::shared_ptr > createdDecorator; template void operator()(PAIR&) const { @@ -331,12 +331,12 @@ namespace ChimeraTK { if (wantedDecoratorType == DecoratorType::range_checking){ createdDecorator.reset( new TypeChangingRangeCheckingDecorator< UserType, TargetImplType> ( - boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); + boost::dynamic_pointer_cast< ChimeraTK::NDRegisterAccessor >( theImpl ) ) ); }else if( wantedDecoratorType == DecoratorType::C_style_conversion){ createdDecorator.reset( new TypeChangingDirectCastDecorator< UserType, TargetImplType> ( - boost::dynamic_pointer_cast< mtca4u::NDRegisterAccessor >( theImpl ) ) ); + boost::dynamic_pointer_cast< ChimeraTK::NDRegisterAccessor >( theImpl ) ) ); }else{ - throw mtca4u::NotImplementedException("TypeChangingDecorator with range limitation is not implemented yet."); + throw ChimeraTK::NotImplementedException("TypeChangingDecorator with range limitation is not implemented yet."); } } }; @@ -344,7 +344,7 @@ namespace ChimeraTK { // the factory function. You don't have to care about the IMPL_Type when requesting a decorator. Implementation, declared at the top of this file template - boost::shared_ptr> getDecorator(const boost::shared_ptr &transferElement, + boost::shared_ptr> getDecorator(const boost::shared_ptr &transferElement, DecoratorType decoratorType) { // check if there already is a decorator for the transfer element @@ -352,7 +352,7 @@ namespace ChimeraTK { if (decoratorMapEntry != getGlobalDecoratorMap().end()) { // There already is a decorator for this transfer element // The decorator has to have a matching type, otherwise we can only throw - auto castedType = boost::dynamic_pointer_cast>(decoratorMapEntry->second); + auto castedType = boost::dynamic_pointer_cast>(decoratorMapEntry->second); if (castedType) { // User type matches, but the decorator type also has to match auto decoTypeHolder = boost::dynamic_pointer_cast(decoratorMapEntry->second); /// @todo eliminate this cast and change map to hold DecoratorTypeHolder assert(decoTypeHolder); @@ -374,7 +374,7 @@ namespace ChimeraTK { } DecoratorFactory< UserType > factory(transferElement, decoratorType ); - boost::fusion::for_each(mtca4u::userTypeMap(), factory); + boost::fusion::for_each(ChimeraTK::userTypeMap(), factory); getGlobalDecoratorMap()[transferElement] = factory.createdDecorator; return factory.createdDecorator; } diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 87b20bd..10c2931 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -15,7 +15,7 @@ #include #include -#include +#include #include "ProcessArray.h" #include "ProcessVariableListener.h" @@ -63,7 +63,7 @@ namespace ChimeraTK { */ UnidirectionalProcessArray( typename ProcessArray::InstanceType instanceType, - const mtca4u::RegisterPath& name, const std::string &unit, + const ChimeraTK::RegisterPath& name, const std::string &unit, const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, const AccessModeFlags &flags); @@ -103,7 +103,7 @@ namespace ChimeraTK { bool doReadTransferLatest() override; - mtca4u::TransferFuture doReadTransferAsync() override; + ChimeraTK::TransferFuture doReadTransferAsync() override; void doPostRead() override; @@ -386,7 +386,7 @@ namespace ChimeraTK { template std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createSynchronizedProcessArray(std::size_t size, - const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + const ChimeraTK::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", T initialValue = T(), std::size_t numberOfBuffers = 3, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr(), @@ -437,7 +437,7 @@ namespace ChimeraTK { template std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createSynchronizedProcessArray( const std::vector& initialValue, - const mtca4u::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", + const ChimeraTK::RegisterPath & name = "", const std::string &unit = "", const std::string &description = "", std::size_t numberOfBuffers = 3, bool maySendDestructively = false, TimeStampSource::SharedPtr timeStampSource = TimeStampSource::SharedPtr(), ProcessVariableListener::SharedPtr sendNotificationListener = ProcessVariableListener::SharedPtr(), @@ -449,7 +449,7 @@ namespace ChimeraTK { template UnidirectionalProcessArray::UnidirectionalProcessArray( typename ProcessArray::InstanceType instanceType, - const mtca4u::RegisterPath& name, const std::string &unit, const std::string &description, + const ChimeraTK::RegisterPath& name, const std::string &unit, const std::string &description, const std::vector& initialValue, std::size_t numberOfBuffers, const AccessModeFlags &flags ) : ProcessArray(instanceType, name, unit, description, flags), _vectorSize(initialValue.size()), @@ -457,10 +457,10 @@ namespace ChimeraTK { _sharedState(numberOfBuffers, initialValue.size()), _localBuffer(initialValue) { - mtca4u::ExperimentalFeatures::enable(); + ChimeraTK::ExperimentalFeatures::enable(); // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = initialValue; + ChimeraTK::NDRegisterAccessor::buffer_2D.resize(1); + ChimeraTK::NDRegisterAccessor::buffer_2D[0] = initialValue; // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (!this->isReadable()) { @@ -496,7 +496,7 @@ namespace ChimeraTK { _timeStampSource(timeStampSource), _sendNotificationListener(sendNotificationListener) { - mtca4u::ExperimentalFeatures::enable(); + ChimeraTK::ExperimentalFeatures::enable(); // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (!this->isWriteable()) { @@ -510,8 +510,8 @@ namespace ChimeraTK { "The pointer to the receiver must point to an instance that is actually a receiver."); } // allocate and initialise buffer of the base class - mtca4u::NDRegisterAccessor::buffer_2D.resize(1); - mtca4u::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; + ChimeraTK::NDRegisterAccessor::buffer_2D.resize(1); + ChimeraTK::NDRegisterAccessor::buffer_2D[0] = receiver->buffer_2D[0]; } /*********************************************************************************************************************/ @@ -552,7 +552,7 @@ namespace ChimeraTK { /*********************************************************************************************************************/ template - mtca4u::TransferFuture UnidirectionalProcessArray::doReadTransferAsync() { + ChimeraTK::TransferFuture UnidirectionalProcessArray::doReadTransferAsync() { if (!this->isReadable()) { throw std::logic_error("Receive operation is only allowed for a receiver process variable."); @@ -575,10 +575,10 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the sender might get into // trouble when it suddenly experiences a vector of the wrong size. - assert(mtca4u::NDRegisterAccessor::buffer_2D[0].size() == _localBuffer._value.size()); + assert(ChimeraTK::NDRegisterAccessor::buffer_2D[0].size() == _localBuffer._value.size()); // swap data out of the local buffer into the user buffer - mtca4u::NDRegisterAccessor::buffer_2D[0].swap( _localBuffer._value ); + ChimeraTK::NDRegisterAccessor::buffer_2D[0].swap( _localBuffer._value ); _versionNumber = _localBuffer._versionNumber; _timeStamp = _localBuffer._timeStamp; @@ -624,12 +624,12 @@ namespace ChimeraTK { bool sendInitialValue = false; if(!_persistentDataStorage) sendInitialValue = true; _persistentDataStorage = storage; - _persistentDataStorageID = _persistentDataStorage->registerVariable(mtca4u::TransferElement::getName(), - mtca4u::NDRegisterAccessor::getNumberOfSamples()); + _persistentDataStorageID = _persistentDataStorage->registerVariable(ChimeraTK::TransferElement::getName(), + ChimeraTK::NDRegisterAccessor::getNumberOfSamples()); if(sendInitialValue) { if( _persistentDataStorage->retrieveValue(_persistentDataStorageID).size() == - mtca4u::NDRegisterAccessor::buffer_2D[0].size() ) { - mtca4u::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); + ChimeraTK::NDRegisterAccessor::buffer_2D[0].size() ) { + ChimeraTK::NDRegisterAccessor::buffer_2D[0] = _persistentDataStorage->retrieveValue(_persistentDataStorageID); doWriteTransfer(); } } @@ -652,7 +652,7 @@ namespace ChimeraTK { // We have to check that the vector that we currently own still has the // right size. Otherwise, the code using the receiver might get into // trouble when it suddenly experiences a vector of the wrong size. - if(mtca4u::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { + if(ChimeraTK::NDRegisterAccessor::buffer_2D[0].size() != _vectorSize) { throw std::logic_error( "Cannot run receive operation because the size of the vector belonging to the current buffer has been " "modified. Variable name: "+this->getName()); @@ -669,7 +669,7 @@ namespace ChimeraTK { // First update the persistent data storage, if any was associated. This cannot be done after sending, since the // value might no longer be available within this instance. if(_persistentDataStorage) { - _persistentDataStorage->updateValue(_persistentDataStorageID, mtca4u::NDRegisterAccessor::buffer_2D[0]); + _persistentDataStorage->updateValue(_persistentDataStorageID, ChimeraTK::NDRegisterAccessor::buffer_2D[0]); } // Set time stamp and version number @@ -679,12 +679,12 @@ namespace ChimeraTK { _versionNumber = newVersionNumber; // set the data by copying or swapping - assert(_localBuffer._value.size() == mtca4u::NDRegisterAccessor::buffer_2D[0].size() ); + assert(_localBuffer._value.size() == ChimeraTK::NDRegisterAccessor::buffer_2D[0].size() ); if(shouldCopy) { - _localBuffer._value = mtca4u::NDRegisterAccessor::buffer_2D[0]; + _localBuffer._value = ChimeraTK::NDRegisterAccessor::buffer_2D[0]; } else { - _localBuffer._value.swap( mtca4u::NDRegisterAccessor::buffer_2D[0] ); + _localBuffer._value.swap( ChimeraTK::NDRegisterAccessor::buffer_2D[0] ); } // send the data to the queue @@ -705,7 +705,7 @@ namespace ChimeraTK { template typename std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createSynchronizedProcessArray(std::size_t size, - const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, + const ChimeraTK::RegisterPath & name, const std::string &unit, const std::string &description, T initialValue, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, const AccessModeFlags &flags) { @@ -723,7 +723,7 @@ namespace ChimeraTK { template typename std::pair< typename ProcessArray::SharedPtr, typename ProcessArray::SharedPtr > createSynchronizedProcessArray(const std::vector& initialValue, - const mtca4u::RegisterPath & name, const std::string &unit, const std::string &description, + const ChimeraTK::RegisterPath & name, const std::string &unit, const std::string &description, std::size_t numberOfBuffers, bool maySendDestructively, TimeStampSource::SharedPtr timeStampSource, ProcessVariableListener::SharedPtr sendNotificationListener, const AccessModeFlags &flags) { diff --git a/src/ControlSystemPVManager.cc b/src/ControlSystemPVManager.cc index a9e1f4e..e8c5d7c 100644 --- a/src/ControlSystemPVManager.cc +++ b/src/ControlSystemPVManager.cc @@ -8,7 +8,7 @@ namespace ChimeraTK { } ProcessVariable::SharedPtr ControlSystemPVManager::getProcessVariable( - const mtca4u::RegisterPath & processVariableName) const { + const ChimeraTK::RegisterPath & processVariableName) const { auto pv = _pvManager->getProcessVariable(processVariableName).first; if(_persistentDataStorage && pv->isWriteable()) pv->setPersistentDataStorage(_persistentDataStorage); return pv; diff --git a/src/ControlSystemSynchronizationUtility.cc b/src/ControlSystemSynchronizationUtility.cc index cdbc67b..d257b6c 100644 --- a/src/ControlSystemSynchronizationUtility.cc +++ b/src/ControlSystemSynchronizationUtility.cc @@ -14,7 +14,7 @@ namespace ChimeraTK { } void ControlSystemSynchronizationUtility::addReceiveNotificationListener( - mtca4u::RegisterPath const & processVariableName, + ChimeraTK::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { std::cout << "Use of deprecated function: Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); @@ -29,7 +29,7 @@ namespace ChimeraTK { } void ControlSystemSynchronizationUtility::removeReceiveNotificationListener( - mtca4u::RegisterPath const & processVariableName) { + ChimeraTK::RegisterPath const & processVariableName) { std::cout << "Use of deprecated function: Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); // althougt it's just one line: call the new interface so the old test covers both diff --git a/src/DevicePVManager.cc b/src/DevicePVManager.cc index 388215a..db5101d 100644 --- a/src/DevicePVManager.cc +++ b/src/DevicePVManager.cc @@ -9,7 +9,7 @@ namespace ChimeraTK { } ProcessVariable::SharedPtr DevicePVManager::getProcessVariable( - const mtca4u::RegisterPath & processVariableName) const { + const ChimeraTK::RegisterPath & processVariableName) const { return _pvManager->getProcessVariable(processVariableName).second; } diff --git a/src/DeviceSynchronizationUtility.cc b/src/DeviceSynchronizationUtility.cc index 89b5bcc..4262cb7 100644 --- a/src/DeviceSynchronizationUtility.cc +++ b/src/DeviceSynchronizationUtility.cc @@ -12,7 +12,7 @@ namespace ChimeraTK { } void DeviceSynchronizationUtility::addReceiveNotificationListener( - mtca4u::RegisterPath const & processVariableName, + ChimeraTK::RegisterPath const & processVariableName, ProcessVariableListener::SharedPtr receiveNotificationListener) { std::cout << "Use of deprecated function: Use addReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable, ProcessVariableListener::SharedPtr receiveNotificationListener) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); @@ -28,7 +28,7 @@ namespace ChimeraTK { } void DeviceSynchronizationUtility::removeReceiveNotificationListener( - mtca4u::RegisterPath const & processVariableName) { + ChimeraTK::RegisterPath const & processVariableName) { std::cout << "Use of deprecated function: Use removeReceiveNotificationListener(ProcessVariable::SharedPtr const & processVariable) instead of the version with processVariableName!" << std::endl; auto pv = _pvManager->getProcessVariable(processVariableName); // althougt it's just one line: call the new interface so the old test covers both diff --git a/src/PVManager.cc b/src/PVManager.cc index 392e4fe..9d817be 100644 --- a/src/PVManager.cc +++ b/src/PVManager.cc @@ -25,7 +25,7 @@ namespace ChimeraTK { } std::pair PVManager::getProcessVariable( - mtca4u::RegisterPath const & processVariableName) const { + ChimeraTK::RegisterPath const & processVariableName) const { ProcessVariableMap::const_iterator i = _processVariables.find( processVariableName); if (i != _processVariables.end()) { diff --git a/src/TypeChangingDecorator.cc b/src/TypeChangingDecorator.cc index 5219a7c..7405eb1 100644 --- a/src/TypeChangingDecorator.cc +++ b/src/TypeChangingDecorator.cc @@ -3,10 +3,10 @@ namespace ChimeraTK{ // The global instance of the map -std::map< boost::shared_ptr, - boost::shared_ptr > globalDecoratorMap; +std::map< boost::shared_ptr, + boost::shared_ptr > globalDecoratorMap; -std::map< boost::shared_ptr, boost::shared_ptr > & +std::map< boost::shared_ptr, boost::shared_ptr > & getGlobalDecoratorMap(){ return globalDecoratorMap; } diff --git a/tests/src/stresstest.cpp b/tests/src/stresstest.cpp index 04001a0..8258920 100644 --- a/tests/src/stresstest.cpp +++ b/tests/src/stresstest.cpp @@ -86,8 +86,8 @@ int main() { auto pviter = pvars.begin(); // fill list of app receivers for readAny() - std::list> varList; - std::map< mtca4u::TransferElementID, boost::shared_ptr> > varMap; + std::list> varList; + std::map< ChimeraTK::TransferElementID, boost::shared_ptr> > varMap; for(auto &pvar : pvars) { varList.emplace_back(*(pvar.second)); varMap[pvar.second->getId()] = pvar.second; diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index f061064..bc82adb 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -14,7 +14,7 @@ using namespace boost::unit_test_framework; using namespace ChimeraTK; -using namespace mtca4u; +using namespace ChimeraTK; /**********************************************************************************************************************/ class AsyncReadTest { @@ -65,8 +65,8 @@ void AsyncReadTest::testAsyncRead() { auto receiver = senderReceiver.second; // obtain register accessor with integral type - auto accessor = mtca4u::ScalarRegisterAccessor(receiver); - auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); + auto accessor = ChimeraTK::ScalarRegisterAccessor(receiver); + auto senderAccessor = ChimeraTK::ScalarRegisterAccessor(sender); // simple reading through readAsync without actual need TransferFuture future; @@ -127,17 +127,17 @@ void AsyncReadTest::testReadAny() { // obtain accessors for process variables auto senderReceiver1 = createSynchronizedProcessArray(1, "Test1"); - auto a1 = mtca4u::ScalarRegisterAccessor(senderReceiver1.second); - auto s1 = mtca4u::ScalarRegisterAccessor(senderReceiver1.first); + auto a1 = ChimeraTK::ScalarRegisterAccessor(senderReceiver1.second); + auto s1 = ChimeraTK::ScalarRegisterAccessor(senderReceiver1.first); auto senderReceiver2 = createSynchronizedProcessArray(1, "Test2"); - auto a2 = mtca4u::ScalarRegisterAccessor(senderReceiver2.second); - auto s2 = mtca4u::ScalarRegisterAccessor(senderReceiver2.first); + auto a2 = ChimeraTK::ScalarRegisterAccessor(senderReceiver2.second); + auto s2 = ChimeraTK::ScalarRegisterAccessor(senderReceiver2.first); auto senderReceiver3 = createSynchronizedProcessArray(1, "Test3"); - auto a3 = mtca4u::ScalarRegisterAccessor(senderReceiver3.second); - auto s3 = mtca4u::ScalarRegisterAccessor(senderReceiver3.first); + auto a3 = ChimeraTK::ScalarRegisterAccessor(senderReceiver3.second); + auto s3 = ChimeraTK::ScalarRegisterAccessor(senderReceiver3.first); auto senderReceiver4 = createSynchronizedProcessArray(1, "Test4"); - auto a4 = mtca4u::ScalarRegisterAccessor(senderReceiver4.second); - auto s4 = mtca4u::ScalarRegisterAccessor(senderReceiver4.first); + auto a4 = ChimeraTK::ScalarRegisterAccessor(senderReceiver4.second); + auto s4 = ChimeraTK::ScalarRegisterAccessor(senderReceiver4.first); // initialise the buffers of the accessors a1 = 1; @@ -350,8 +350,8 @@ void AsyncReadTest::testMixedRead() { auto receiver = senderReceiver.second; // obtain register accessor with integral type - auto accessor = mtca4u::ScalarRegisterAccessor(receiver); - auto senderAccessor = mtca4u::ScalarRegisterAccessor(sender); + auto accessor = ChimeraTK::ScalarRegisterAccessor(receiver); + auto senderAccessor = ChimeraTK::ScalarRegisterAccessor(sender); TransferFuture future; diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index 02b14e4..b41ea20 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -6,9 +6,9 @@ using namespace boost::unit_test_framework; #include "TypeChangingDecorator.h" using namespace ChimeraTK; -#include -#include -#include +#include +#include +#include //function which can be called many times but only call enable() the first time void enableExperimental(){ @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_SUITE( TypeChangingDecoratorTestSuite ) // the startReadValue and expectedWriteValue are from the register in the dummy device, which is 32 bit fixed point singed with 16 fractional bits, thus we talk to it as double from the test template class DECORATOR_TYPE = TypeChangingRangeCheckingDecorator> void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue, double expectedWriteValue){ - mtca4u::Device d; + ChimeraTK::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); auto anotherScalarAccessor = d.getScalarRegisterAccessor("/SOME/SCALAR"); @@ -174,7 +174,7 @@ template class DECORATOR_TYPE> void loopTest(){ // Test loops for numeric data types. One type is enough because it's template code // We don't have to test the string specialisations, arrays of strings are not allowed - mtca4u::Device d; + ChimeraTK::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto twoD = d.getTwoDRegisterAccessor("/SOME/TWO_D"); auto anotherAccessor = d.getTwoDRegisterAccessor("/SOME/TWO_D"); @@ -239,7 +239,7 @@ try{ \ BOOST_AUTO_TEST_CASE( testRangeChecks ){ // Just a few tests where the type changing decorator with conversion should throw // where the direct cast decorator changes the interpretation - mtca4u::Device d; + ChimeraTK::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto myInt = d.getScalarRegisterAccessor("/SOME/INT"); auto myIntDummy = d.getScalarRegisterAccessor("/SOME/INT"); // the second accessor for the test @@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE( testRangeChecks ){ } BOOST_AUTO_TEST_CASE( testTransferGroup ){ - mtca4u::Device d; + ChimeraTK::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto partial0 = d.getScalarRegisterAccessor("/SOME/ARRAY"); auto partial1 = d.getScalarRegisterAccessor("/SOME/ARRAY",1); @@ -285,8 +285,8 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ wholeArray[1]=12346; wholeArray.write(); - mtca4u::ScalarRegisterAccessor decorated0(getDecorator(partial0)); - mtca4u::ScalarRegisterAccessor decorated1(getDecorator(partial1)); + ChimeraTK::ScalarRegisterAccessor decorated0(getDecorator(partial0)); + ChimeraTK::ScalarRegisterAccessor decorated1(getDecorator(partial1)); TransferGroup group; group.addAccessor(decorated0); @@ -306,10 +306,10 @@ BOOST_AUTO_TEST_CASE( testTransferGroup ){ } BOOST_AUTO_TEST_CASE( testFactory ){ - mtca4u::Device d; + ChimeraTK::Device d; d.open("sdm://./dummy=decoratorTest.map"); auto scalar = d.getScalarRegisterAccessor("/SOME/SCALAR"); - mtca4u::TransferElementAbstractor & transferElement = scalar; + ChimeraTK::TransferElementAbstractor & transferElement = scalar; auto decoratedScalar = getDecorator(transferElement); BOOST_CHECK( decoratedScalar ); @@ -335,7 +335,7 @@ BOOST_AUTO_TEST_CASE( testFactory ){ // fixme: at the moment we are throwing if a limiting decorator is requested auto scalar3 = d.getScalarRegisterAccessor("/SOME/SCALAR"); - CHECK_THROW_PRINT( getDecorator(scalar3, DecoratorType::limiting), mtca4u::NotImplementedException ); + CHECK_THROW_PRINT( getDecorator(scalar3, DecoratorType::limiting), ChimeraTK::NotImplementedException ); } BOOST_AUTO_TEST_SUITE_END() From 426e895c5da533ad37cc89a35219537145fd64f7 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 4 Jul 2018 15:36:36 +0200 Subject: [PATCH 356/786] necessary update for change in DeviceAccess --- tests/src/testAsyncRead.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index bc82adb..8818bd1 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -158,7 +158,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -174,7 +174,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -190,7 +190,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -207,7 +207,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -223,7 +223,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -239,7 +239,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -255,7 +255,7 @@ void AsyncReadTest::testReadAny() { { // launch the readAny in a background thread std::atomic flag{false}; - std::thread thread([&group,&flag] { group.waitAny(); flag = true; }); + std::thread thread([&group,&flag] { group.readAny(); flag = true; }); // check that it doesn't return too soon usleep(100000); @@ -275,12 +275,12 @@ void AsyncReadTest::testReadAny() { s2 = 777; s2.write(); // no point to use a thread here - auto r = group.waitAny(); + auto r = group.readAny(); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 123); - r = group.waitAny(); + r = group.readAny(); BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); @@ -303,28 +303,28 @@ void AsyncReadTest::testReadAny() { s1.write(); // no point to use a thread here - auto r = group.waitAny(); + auto r = group.readAny(); BOOST_CHECK(a4.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 777); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); - r = group.waitAny(); + r = group.readAny(); BOOST_CHECK(a2.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 122); BOOST_CHECK(a4 == 111); - r = group.waitAny(); + r = group.readAny(); BOOST_CHECK(a3.getId() == r); BOOST_CHECK(a1 == 666); BOOST_CHECK(a2 == 222); BOOST_CHECK(a3 == 333); BOOST_CHECK(a4 == 111); - r = group.waitAny(); + r = group.readAny(); BOOST_CHECK(a1.getId() == r); BOOST_CHECK(a1 == 444); BOOST_CHECK(a2 == 222); From 75aeeb9328ac125fe69e8fb3913129869e47a7bd Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 11 Jul 2018 11:31:49 +0200 Subject: [PATCH 357/786] added performance test --- tests/src/testPerformance.cpp | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/src/testPerformance.cpp diff --git a/tests/src/testPerformance.cpp b/tests/src/testPerformance.cpp new file mode 100644 index 0000000..a73e08c --- /dev/null +++ b/tests/src/testPerformance.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include "ProcessArray.h" + +using namespace ChimeraTK; + +int main() { + + constexpr size_t nVars = 1000; + constexpr size_t nSendsPerVar = 1000; + + // create process variable pairs + std::vector< std::pair< boost::shared_ptr>, boost::shared_ptr> > > pvars; + for(size_t k=0; k(1, name)); + } + + // create sender thread + boost::thread sender( [pvars] { + for(size_t i=0; iaccessData(0) = k*nSendsPerVar + i; + while(true) { + bool dataLost = pv.first->write(); + if(!dataLost) break; + } + } + } + } ); // end of sender thread definition via lambda + + auto start = std::chrono::steady_clock::now(); + + size_t error_count = 0; + for(size_t i=0; iread(); + if( pv.second->accessData(0) < k*nSendsPerVar + i || pv.second->accessData(0) > k*nSendsPerVar + i + 1 ) ++error_count; + } + } + + if(error_count > 0) { + std::cout << "*** Test FAILED. " << error_count << " errors found." << std::endl; + return 1; + } + + auto end = std::chrono::steady_clock::now(); + + auto nTransfers = nVars * nSendsPerVar; + + std::chrono::duration diff = end-start; + std::cout << "Time for " << nTransfers << " transfers: " << diff.count() << " s\n"; + std::cout << "Average time per transfer: " << diff.count()/(double)nTransfers * 1e6 << " us\n"; + + + return 0; + +} From 72055fa1d50367b63a14e8ce02a67352edd57b31 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 13 Jul 2018 10:40:58 +0200 Subject: [PATCH 358/786] removed unneeded include files --- .../ControlSystemAdapter/UnidirectionalProcessArray.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 10c2931..8a45ea7 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -8,12 +8,7 @@ #include #include -#include #include -#include -#include -#include -#include #include From d0e6b3bf4ae6b868fcc820a4ffc31d46b608a542 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Fri, 13 Jul 2018 10:54:27 +0200 Subject: [PATCH 359/786] removed more no-longer-required includes --- .../ControlSystemAdapter/BidirectionalProcessArray.h | 5 ----- include/ChimeraTK/ControlSystemAdapter/ProcessArray.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h index 1b9d817..6299031 100644 --- a/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/BidirectionalProcessArray.h @@ -7,12 +7,7 @@ #include #include -#include #include -#include -#include -#include -#include #include diff --git a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h index dfc5248..5d4e876 100644 --- a/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/ProcessArray.h @@ -7,12 +7,7 @@ #include #include -#include #include -#include -#include -#include -#include #include #include From 81f26ac8d83e977d7a26ea8e1ef590e2e5e1c92f Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Mon, 16 Jul 2018 15:39:26 +0200 Subject: [PATCH 360/786] performance test is now using arrays instead of scalars --- tests/src/testPerformance.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/src/testPerformance.cpp b/tests/src/testPerformance.cpp index a73e08c..f928fa8 100644 --- a/tests/src/testPerformance.cpp +++ b/tests/src/testPerformance.cpp @@ -7,22 +7,27 @@ using namespace ChimeraTK; int main() { - constexpr size_t nVars = 1000; - constexpr size_t nSendsPerVar = 1000; + constexpr size_t dataSize = 16384; + constexpr size_t nVars = 200; + constexpr size_t nSendsPerVar = 2000; // create process variable pairs std::vector< std::pair< boost::shared_ptr>, boost::shared_ptr> > > pvars; for(size_t k=0; k(1, name)); + pvars.push_back(createSynchronizedProcessArray(dataSize, name)); } // create sender thread - boost::thread sender( [pvars] { + size_t sum_sender = 0; + boost::thread sender( [pvars, &sum_sender] { for(size_t i=0; iaccessData(0) = k*nSendsPerVar + i; + for(size_t j=0; jaccessData(j) = k*nSendsPerVar + i + j; + sum_sender += k*nSendsPerVar + i + j; + } while(true) { bool dataLost = pv.first->write(); if(!dataLost) break; @@ -33,18 +38,19 @@ int main() { auto start = std::chrono::steady_clock::now(); - size_t error_count = 0; + size_t sum = 0; for(size_t i=0; iread(); - if( pv.second->accessData(0) < k*nSendsPerVar + i || pv.second->accessData(0) > k*nSendsPerVar + i + 1 ) ++error_count; + for(size_t j=0; jaccessData(j); } } - - if(error_count > 0) { - std::cout << "*** Test FAILED. " << error_count << " errors found." << std::endl; - return 1; + std::cout << "SUM = " << sum << std::endl; + sender.join(); + bool failed = false; + if(sum_sender != sum) { + std::cout << "ERROR sender sum = " << sum_sender << std::endl; + failed = true; } auto end = std::chrono::steady_clock::now(); @@ -56,6 +62,6 @@ int main() { std::cout << "Average time per transfer: " << diff.count()/(double)nTransfers * 1e6 << " us\n"; - return 0; + return failed; } From 9fb6b9ae5cb0345ea2005776f8136845dc57af12 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 17 Jul 2018 12:55:43 +0200 Subject: [PATCH 361/786] performance test only checks the data result if no data was overwritten in the process. --- tests/src/testPerformance.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/src/testPerformance.cpp b/tests/src/testPerformance.cpp index f928fa8..cccb31b 100644 --- a/tests/src/testPerformance.cpp +++ b/tests/src/testPerformance.cpp @@ -20,7 +20,8 @@ int main() { // create sender thread size_t sum_sender = 0; - boost::thread sender( [pvars, &sum_sender] { + bool dataWasLost = false; + boost::thread sender( [pvars, &sum_sender, &dataWasLost] { for(size_t i=0; iwrite(); if(!dataLost) break; + dataWasLost = true; } } } @@ -50,7 +52,13 @@ int main() { bool failed = false; if(sum_sender != sum) { std::cout << "ERROR sender sum = " << sum_sender << std::endl; - failed = true; + if(dataWasLost) { + std::cout << "-> data was overwritten on sender side, sums are expected not to match." << std::endl; + } + else { + std::cout << "-> no data was overwritten on sender side, sums are expected to match. Failing the test!" << std::endl; + failed = true; + } } auto end = std::chrono::steady_clock::now(); @@ -61,7 +69,6 @@ int main() { std::cout << "Time for " << nTransfers << " transfers: " << diff.count() << " s\n"; std::cout << "Average time per transfer: " << diff.count()/(double)nTransfers * 1e6 << " us\n"; - return failed; } From 45d2f3d5d5ee2ad21ba1ea45170d388572294301 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Tue, 17 Jul 2018 15:22:57 +0200 Subject: [PATCH 362/786] let the performance test run shorter, to avoid running into timeouts in Debug builds on Jenkins --- tests/src/testPerformance.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/testPerformance.cpp b/tests/src/testPerformance.cpp index cccb31b..8b933f7 100644 --- a/tests/src/testPerformance.cpp +++ b/tests/src/testPerformance.cpp @@ -8,8 +8,8 @@ using namespace ChimeraTK; int main() { constexpr size_t dataSize = 16384; - constexpr size_t nVars = 200; - constexpr size_t nSendsPerVar = 2000; + constexpr size_t nVars = 20; + constexpr size_t nSendsPerVar = 200; // create process variable pairs std::vector< std::pair< boost::shared_ptr>, boost::shared_ptr> > > pvars; From 92c490e280c8bcf139939e11a90b9d60cd769da3 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 18 Jul 2018 09:31:23 +0200 Subject: [PATCH 363/786] - increase minor version - update dependency versions --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 342f2fe..f50a8f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(ChimeraTK-ControlSystemAdapter) include(cmake/add_dependency.cmake) set(${PROJECT_NAME}_MAJOR_VERSION 00) -set(${PROJECT_NAME}_MINOR_VERSION 13) +set(${PROJECT_NAME}_MINOR_VERSION 14) set(${PROJECT_NAME}_PATCH_VERSION 00) include(cmake/set_version_numbers.cmake) @@ -43,7 +43,7 @@ ENDIF(Boost_UNIT_TEST_FRAMEWORK_FOUND) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # DeviceAccess is needed due to the common interface classes -add_dependency(mtca4u-deviceaccess 00.29 REQUIRED) +add_dependency(mtca4u-deviceaccess 00.33 REQUIRED) # libXML++ is used for parsing/writing XML files FIND_PACKAGE(PkgConfig REQUIRED) @@ -106,7 +106,7 @@ ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) #Install the library install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin - LIBRARY DESTINATION lib + LIBRARY DESTINATION lib ) #all include files go into include From b484c44d4a1eab74a35bf023e66d365ec9b0ba6e Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 18 Jul 2018 16:15:29 +0200 Subject: [PATCH 364/786] updated HowTo file and formatted it properly with markdown --- HowTo.project-template | 38 ----------------------- HowTo_project-template.md | 63 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 38 deletions(-) delete mode 100644 HowTo.project-template create mode 100644 HowTo_project-template.md diff --git a/HowTo.project-template b/HowTo.project-template deleted file mode 100644 index 6d0d6d8..0000000 --- a/HowTo.project-template +++ /dev/null @@ -1,38 +0,0 @@ -This project uses the ChimeraTK project-template. - -This file descibes how to corretly use the template project. - -Concept: -The project template is merged via git. The idea is to have the whole project-template repository merged into the project which uses it, incl. all the history. Like this updates and improvements in the template scripts can easily be propagated to all client projects by simply merging the head of project template. - -Instructions: - -A) Preparing your project to use the project template - -1. Add the project template repository as a git remote to your project. This only is done once for each local repository. -$ git remote add project-template https://github.com/ChimeraTK/project-template.git - -We intentionally use the read-only https version here. Never write to the project template from one of the client projects! - -2. Update the remote on you hard disk so git knows what is in project-template -$ git remote update - -3. Merge the project template master into your project -$ git merge project-template/master - -Not you can use the files which are provided in the cmake directory in your CMakeLists.txt - -B) Getting updates of project template - -Just repeat steps 2. and 3. of section A. If you are on a different computer with a different checkout directory, you might have to repeat step 1 if the remote is not there (check with 'git remote show'). - -C) Updating/Improving the project template. - -Never write to the project template from one of the client projects! -If you have improvements or additions to the project template, check out the project-template repository separately, make the modifications and commit/push them. - -Be careful not to break functionality for other project which are using the template! - -After the project template is updated you can merge the changes into your project as described in section B). - -Note: For testing you might want to add your local repository of project-template as a remote to your project, so you don't have to push untested changes. Be careful that everything is pushed to github when you are done in order not to lose consistency of the repositories. diff --git a/HowTo_project-template.md b/HowTo_project-template.md new file mode 100644 index 0000000..b721c87 --- /dev/null +++ b/HowTo_project-template.md @@ -0,0 +1,63 @@ +This file descibes the intended way to use the template project. + +## Concept: +The project template is merged via git. The idea is to have the whole +project-template repository merged into the project which uses it, incl. +all the history. Like this updates and improvements in the template +scripts can easily be propagated to all client projects by simply +merging the head of project template. + +## Instructions: + +### Preparing your project to use the project template + - Add the project template repository as a git remote to your project. + This only is done once for each local repository. + ``` + $ git remote add project-template https://github.com/ChimeraTK/project-template.git + ``` + We intentionally use the read-only https version here. Never write + to the project template from one of the client projects! + + - Update the remote on you hard disk so git knows what is in + project-template + ``` + $ git remote update + ``` + + - Merge the project template master into your project + ``` + $ git merge project-template/master + ``` + + Note:you can access the files provided in the cmake directory from your + CMakeLists.txt after appending this location to your CMAKE_MODULE_PATH + variable + +### Getting updates to the project template + ``` + $ git remote update + $ git merge project-template/master + + ``` + If you are on a different computer with a different checkout directory which does not have the remote added + (check with 'git remote show'), you may have to repeat steps from the + section: Preparing your project to use the project template. + + +### Updating/Improving the project template. + + - Never write to the project template from one of the client projects! If + you have improvements or additions to the project template, check out + the project-template repository separately, make the modifications and + commit/push them. + + - Be careful not to break functionality for other project which are using + the template! + + - After the project template is updated you can merge the changes into + your project as described in the section above. (Getting updates of project template) + + - Note: For testing you might want to add your local repository of + project-template as a remote to your project, so you don't have to push + untested changes. Be careful that everything is pushed to github when + you are done in order not to lose consistency of the repositories. From c2573a5451421d81bd5cef66b0dfc9b9461545cd Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Wed, 18 Jul 2018 16:30:19 +0200 Subject: [PATCH 365/786] Removed HowTo_project-template.md --- HowTo_project-template.md | 63 --------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 HowTo_project-template.md diff --git a/HowTo_project-template.md b/HowTo_project-template.md deleted file mode 100644 index b721c87..0000000 --- a/HowTo_project-template.md +++ /dev/null @@ -1,63 +0,0 @@ -This file descibes the intended way to use the template project. - -## Concept: -The project template is merged via git. The idea is to have the whole -project-template repository merged into the project which uses it, incl. -all the history. Like this updates and improvements in the template -scripts can easily be propagated to all client projects by simply -merging the head of project template. - -## Instructions: - -### Preparing your project to use the project template - - Add the project template repository as a git remote to your project. - This only is done once for each local repository. - ``` - $ git remote add project-template https://github.com/ChimeraTK/project-template.git - ``` - We intentionally use the read-only https version here. Never write - to the project template from one of the client projects! - - - Update the remote on you hard disk so git knows what is in - project-template - ``` - $ git remote update - ``` - - - Merge the project template master into your project - ``` - $ git merge project-template/master - ``` - - Note:you can access the files provided in the cmake directory from your - CMakeLists.txt after appending this location to your CMAKE_MODULE_PATH - variable - -### Getting updates to the project template - ``` - $ git remote update - $ git merge project-template/master - - ``` - If you are on a different computer with a different checkout directory which does not have the remote added - (check with 'git remote show'), you may have to repeat steps from the - section: Preparing your project to use the project template. - - -### Updating/Improving the project template. - - - Never write to the project template from one of the client projects! If - you have improvements or additions to the project template, check out - the project-template repository separately, make the modifications and - commit/push them. - - - Be careful not to break functionality for other project which are using - the template! - - - After the project template is updated you can merge the changes into - your project as described in the section above. (Getting updates of project template) - - - Note: For testing you might want to add your local repository of - project-template as a remote to your project, so you don't have to push - untested changes. Be careful that everything is pushed to github when - you are done in order not to lose consistency of the repositories. From 4d9febd279df818c4dd9358c945b772b81e965d9 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer Date: Thu, 19 Jul 2018 16:32:14 +0200 Subject: [PATCH 366/786] do not enable experimental features where no longer needed --- .../UnidirectionalProcessArray.h | 2 -- tests/src/testAsyncRead.cpp | 3 --- tests/src/testTypeChangingDecorator.cpp | 16 +++------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h index 8a45ea7..b11f14a 100644 --- a/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h +++ b/include/ChimeraTK/ControlSystemAdapter/UnidirectionalProcessArray.h @@ -452,7 +452,6 @@ namespace ChimeraTK { _sharedState(numberOfBuffers, initialValue.size()), _localBuffer(initialValue) { - ChimeraTK::ExperimentalFeatures::enable(); // allocate and initialise buffer of the base class ChimeraTK::NDRegisterAccessor::buffer_2D.resize(1); ChimeraTK::NDRegisterAccessor::buffer_2D[0] = initialValue; @@ -491,7 +490,6 @@ namespace ChimeraTK { _timeStampSource(timeStampSource), _sendNotificationListener(sendNotificationListener) { - ChimeraTK::ExperimentalFeatures::enable(); // It would be better to do the validation before initializing, but this // would mean that we would have to initialize twice. if (!this->isWriteable()) { diff --git a/tests/src/testAsyncRead.cpp b/tests/src/testAsyncRead.cpp index 8818bd1..6122cfa 100644 --- a/tests/src/testAsyncRead.cpp +++ b/tests/src/testAsyncRead.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "ProcessArray.h" @@ -47,8 +46,6 @@ class AsyncReadTestSuite : public test_suite { test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/ [] ) { - ChimeraTK::ExperimentalFeatures::enable(); - framework::master_test_suite().p_name.value = "Async read test suite"; framework::master_test_suite().add(new AsyncReadTestSuite); diff --git a/tests/src/testTypeChangingDecorator.cpp b/tests/src/testTypeChangingDecorator.cpp index b41ea20..7269f86 100644 --- a/tests/src/testTypeChangingDecorator.cpp +++ b/tests/src/testTypeChangingDecorator.cpp @@ -10,15 +10,6 @@ using namespace ChimeraTK; #include #include -//function which can be called many times but only call enable() the first time -void enableExperimental(){ - static bool isEnabled(false); - if (!isEnabled){ - ChimeraTK::ExperimentalFeatures::enable();// still experimental. - isEnabled = true; - } -} - // always test close for floating point values. Don't rely on exact binary representations bool test_close(double a, double b, double tolerance = 0.0001){ if (std::fabs(a - b) < tolerance ){ @@ -105,19 +96,18 @@ void testDecorator(double startReadValue, T expectedReadValue, T startWriteValue BOOST_CHECK( test_close(anotherScalarAccessor, expectedWriteValue+1) ); //test asynchronouy reading. Check that the modification is arriving in the decorator's buffer - enableExperimental(); - + // just to check that the test is not producing false positives by accident assert( fabs(startReadValue+2 - (expectedWriteValue+1)) > 0.001 ); anotherScalarAccessor = startReadValue+2; anotherScalarAccessor.write(); - + auto future = decoratedScalar.readAsync(); // nothing must change on the user buffer yet BOOST_CHECK( test_close(decoratedScalar.accessData(0), startWriteValue+1) ); future.wait(); // this calls the post-reads correctly BOOST_CHECK( test_close(decoratedScalar.accessData(0), expectedReadValue+2) ); - + // FIXME: We cannot test that the decorator is relaying doReadTransfer, doReadTransferLatest and // do readTransferLatest correctly with the dummy backend because they all point to the same // implementation. Thus we intentionally do not call them to indicate them uncovered. From 08760225be0b1639a7cc4158816fe753d32ae042 Mon Sep 17 00:00:00 2001 From: Martin Killenberg Date: Wed, 8 Aug 2018 09:37:25 +0200 Subject: [PATCH 367/786] added FindFFTW to project template. Needed by several middle layer servers --- cmake/Modules/FindFFTW.cmake | 159 +++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 cmake/Modules/FindFFTW.cmake diff --git a/cmake/Modules/FindFFTW.cmake b/cmake/Modules/FindFFTW.cmake new file mode 100644 index 0000000..8a88a21 --- /dev/null +++ b/cmake/Modules/FindFFTW.cmake @@ -0,0 +1,159 @@ +# - Find the FFTW library +# +# Usage: +# find_package(FFTW [REQUIRED] [QUIET] ) +# +# It sets the following variables: +# FFTW_FOUND ... true if fftw is found on the system +# FFTW_LIBRARIES ... full path to fftw library +# FFTW_INCLUDES ... fftw include directory +# +# The following variables will be checked by the function +# FFTW_USE_STATIC_LIBS ... if true, only static libraries are found +# FFTW_ROOT ... if set, the libraries are exclusively searched +# under this path +# FFTW_LIBRARY ... fftw library to use +# FFTW_INCLUDE_DIR ... fftw include directory +# + +#If environment variable FFTWDIR is specified, it has same effect as FFTW_ROOT +if( NOT FFTW_ROOT AND ENV{FFTWDIR} ) + set( FFTW_ROOT $ENV{FFTWDIR} ) +endif() + +# Check if we can use PkgConfig +find_package(PkgConfig) + +#Determine from PKG +if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT ) + pkg_check_modules( PKG_FFTW QUIET "fftw3" ) +endif() + +#Check whether to search static or dynamic libs +set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} ) + +if( ${FFTW_USE_STATIC_LIBS} ) + set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ) +else() + set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} ) +endif() + +if( FFTW_ROOT ) + + #find libs + find_library( + FFTW_LIB + NAMES "fftw3" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + + find_library( + FFTW_THREADS_LIB + NAMES "fftw3_threads" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + + find_library( + FFTWF_LIB + NAMES "fftw3f" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + + find_library( + FFTWF_THREADS_LIB + NAMES "fftw3f_threads" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + + find_library( + FFTWL_LIB + NAMES "fftw3l" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + + find_library( + FFTWL_THREADS_LIB + NAMES "fftw3l_threads" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + + #find includes + find_path( + FFTW_INCLUDES + NAMES "fftw3.h" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "include" + NO_DEFAULT_PATH + ) + +else() + + find_library( + FFTW_LIB + NAMES "fftw3" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + + find_library( + FFTW_THREADS_LIB + NAMES "fftw3_threads" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + + find_library( + FFTWF_LIB + NAMES "fftw3f" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + + find_library( + FFTWF_THREADS_LIB + NAMES "fftw3f_threads" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + + find_library( + FFTWL_LIB + NAMES "fftw3l" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + + find_library( + FFTWL_THREADS_LIB + NAMES "fftw3l_threads" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + + find_path( + FFTW_INCLUDES + NAMES "fftw3.h" + PATHS ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} + ) + +endif( FFTW_ROOT ) + +set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB}) + +if(FFTWL_LIB) + set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTWL_LIB}) +endif() + +set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFTW DEFAULT_MSG + FFTW_INCLUDES FFTW_LIBRARIES) + +mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB FFTW_THREADS_LIB FFTWF_THREADS_LIB FFTWL_THREADS_LIB) From 44a3fde5eee342053b59eac83d27e4649f31b97f Mon Sep 17 00:00:00 2001 From: Jens Georg Date: Thu, 30 Aug 2018 09:42:59 +0200 Subject: [PATCH 368/786] Add easier steps to use the template Not adding the repository as a remote simply makes it impossible to push to it. Also this makes it mandatory to always fetch the changes from the remote anyway since it is not locally mirrored. --- HowTo_project-template.md | 47 ++++++++++----------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/HowTo_project-template.md b/HowTo_project-template.md index b721c87..307c988 100644 --- a/HowTo_project-template.md +++ b/HowTo_project-template.md @@ -1,50 +1,26 @@ This file descibes the intended way to use the template project. -## Concept: +## Concept The project template is merged via git. The idea is to have the whole project-template repository merged into the project which uses it, incl. all the history. Like this updates and improvements in the template scripts can easily be propagated to all client projects by simply merging the head of project template. -## Instructions: +## Instructions -### Preparing your project to use the project template - - Add the project template repository as a git remote to your project. - This only is done once for each local repository. - ``` - $ git remote add project-template https://github.com/ChimeraTK/project-template.git - ``` - We intentionally use the read-only https version here. Never write - to the project template from one of the client projects! - - - Update the remote on you hard disk so git knows what is in - project-template - ``` - $ git remote update - ``` - - - Merge the project template master into your project - ``` - $ git merge project-template/master - ``` - - Note:you can access the files provided in the cmake directory from your - CMakeLists.txt after appending this location to your CMAKE_MODULE_PATH - variable - -### Getting updates to the project template +### Initial import and getting of the project template +In your local git repository, issue: ``` - $ git remote update - $ git merge project-template/master + $ git fetch https://github.com/ChimeraTK/project-template.git + $ git merge FETCH_HEAD +``` - ``` - If you are on a different computer with a different checkout directory which does not have the remote added - (check with 'git remote show'), you may have to repeat steps from the - section: Preparing your project to use the project template. - +Note: you can access the files provided in the cmake directory from your +CMakeLists.txt after appending this location to your `CMAKE_MODULE_PATH` +variable -### Updating/Improving the project template. +### Updating/Improving the project template - Never write to the project template from one of the client projects! If you have improvements or additions to the project template, check out @@ -61,3 +37,4 @@ merging the head of project template. project-template as a remote to your project, so you don't have to push untested changes. Be careful that everything is pushed to github when you are done in order not to lose consistency of the repositories. + From 5e46c305dbf6ffe269a0f2efc0de89ae8ea9a0bc Mon Sep 17 00:00:00 2001 From: Jens Georg Date: Thu, 30 Aug 2018 11:16:57 +0200 Subject: [PATCH 369/786] Add Doxyfile.in --- cmake/Doxyfile.in | 2472 ++++++++++++++++++++++ cmake/enable_doxygen_documentation.cmake | 3 +- 2 files changed, 2474 insertions(+), 1 deletion(-) create mode 100644 cmake/Doxyfile.in diff --git a/cmake/Doxyfile.in b/cmake/Doxyfile.in new file mode 100644 index 0000000..6055abf --- /dev/null +++ b/cmake/Doxyfile.in @@ -0,0 +1,2472 @@ +# Doxyfile 1.8.11 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @PROJECT_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/doc/DESY_logo.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# 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 +# U+3044. +# The default value is: NO. + +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. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# 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 +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# 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. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# 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. +# +# 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. + +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. +# 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. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# 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 +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +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. +# 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. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: 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. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# 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. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# 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 +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: 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. +# The default value is: YES. + +WARN_IF_DOC_ERROR = 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. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src \ + @CMAKE_CURRENT_SOURCE_DIR@/include \ + @CMAKE_CURRENT_SOURCE_DIR@/Modules/src \ + @CMAKE_CURRENT_SOURCE_DIR@/Modules/include \ + @CMAKE_CURRENT_BINARY_DIR@/main.dox + +# 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: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# 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 +# 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, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# 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 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. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# 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. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +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. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: 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. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what 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. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# 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. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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 +# 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. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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 = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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: http://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 +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name 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. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# 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. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +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. +# +# 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 +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +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). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +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://qt-project.org/doc/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. + +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://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +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://qt-project.org/doc/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://qt-project.org/doc/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://qt-project.org/doc/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. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. 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. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = 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. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# 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 +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using 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 +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# 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. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# 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 +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /