From c72297020bf20b1711d50656a2ed90b4a81d584a Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Thu, 10 Jul 2014 13:17:26 -0400 Subject: [PATCH] update documentation --- documentation/RELEASE_NOTES.md | 33 + documentation/TODO.md | 43 +- documentation/examples.zip | Bin 9082 -> 102821 bytes documentation/pvDataCPP.html | 607 ++- documentation/pvDataCPP_20140708.html | 5380 +++++++++++++++++++++++++ src/factory/Convert.cpp | 1 - src/pv/convert.h | 6 +- 7 files changed, 5652 insertions(+), 418 deletions(-) create mode 100644 documentation/pvDataCPP_20140708.html diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 0e0311c..8c03979 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -4,6 +4,8 @@ Release release/3.1 IN DEVELOPMENT The main changes since release 3.0.2 are: * array semantics now enforce Copy On Write. +* String no longer defined. +* toString replaced by stream I/O * union is new type. * copy is new. * monitorPlugin is new. @@ -16,6 +18,36 @@ In order to limit memory usage the storage for raw data is managed via a new sha This allows multiple instances of array data to use the shared raw data. COW is implemented via shared_vectors of const data, i. e. data that can not be modified. + +String no longer defined +--------- + +This is replaced by std::string. + + +toString replaced by stream I/O +--------- + +pvData.h and pvIntrospect no longer defines toString +Instead they have stream support. +pvIntrospect uses method dump and pvData uses dumpValue. +For example: + + PVDoublePtr pvValue; + String buffer; + pvValue->toString(&buffer); + cout << buffer << endl; + buffer.clear(); + pvValue->getField()->toString(&buffer); + cout << buffer << evdl; + +is replaced by + + PVDoublePtr pvValue; + cout << pvValue=>dumpValue(cout) << endl + cout << pvValue->getField()->dump(cout) << endl; + + union is a new basic type. ------------ @@ -41,6 +73,7 @@ monitorPlugin ------------- This is for is for use by code that implements pvAccess monitors. +This is prototype and is subject to debate. Release 3.0.2 ========== diff --git a/documentation/TODO.md b/documentation/TODO.md index 268a561..d5d93d8 100644 --- a/documentation/TODO.md +++ b/documentation/TODO.md @@ -1,33 +1,10 @@ TODO =========== -toString, dumpValue, printer, and streams +printer ------------ -The way to print introspection and data instances is not consistent. -This needs work. -Some items that need work are: - -* Introspection has no support for streams. Only for toString. -* data has problems. toString is implemented by calling Convert::getString. -It look like this was intended to use printer but that did nor properly indent fields of structures. - -The current implementation is: - - void Convert::getString(StringBuilder buf,PVField const *pvField,int /*indentLevel*/) - { - // TODO indextLevel ignored - std::ostringstream strm; - strm << pvField->dumpValue(strm) << std::endl; - // PrinterPlain p; - // p.setStream(strm); - // p.print(*pvField); - strm.str().swap(*buf); - } - -Thus it just uses dumpValue. -What should it do? -If printer is used it must do proper indentation. +pv/printer.h is not used. doxygen ------- @@ -35,6 +12,15 @@ doxygen There is a lot of public code that does not have doxygen tags. +postMonitor: PVUnion, PVUnionArray, and PVStructureArray +-------- + +PVUnion, PVUnionArray, and PVStructureArray all have elements +that are treated like a top level field. +The implementation should be changed so that it implements PostHandler. +Thus when an element is modified it will call postPut for itself. + + monitorPlugin ------------- @@ -43,12 +29,7 @@ A debate is on-going about what semantics should be. PVFieldConstPtr etc ------------------- -In pvDataCPP.html look at the monoitorPlugin example. -It has a discussion of a possible need for shared pointers that can not be used to modify data. -This in addition to PVFieldPtr should PVFieldConstPtr exist. +In addition to PVFieldPtr should PVFieldConstPtr exist. But this means that all methods defined in pvDataCPP must be checked. This is a BIG job. -Release 3.0.2 -========== -This was the starting point for RELEASE_NOTES diff --git a/documentation/examples.zip b/documentation/examples.zip index 76893639fe6cd0a681d5cdbcf1d2e3c1b1e6e62e..1f27dd599e79fd60f7e505a290db81cddc9bb217 100644 GIT binary patch delta 97940 zcmaHSb984-lx>`jJGSi(I<{@QW1F4icY_W(w#`n*wmY_M+t%yvn_2VL%&hnBT~(*n zsdILnyU+cz_OnD`J`9qQEF=^>*guay`(Z2+F>KsWt7`uDj#xhoxc~6`{q-x+!p8jd zo238sD?|P@)w3HA|Je8jNaufSEj?5+_`i-m=wR&sN)-DS`9CLucq2G0OhqP92Rl0l zduDYtI55b6P=Eb{iWmPtgcffC!wRW3&>EitlMPA0ei+XK%K^JE`0v4geX0KsV6uOJ z@fDeDt?b=A89li;3^~~UFZ{nnnlw3>ug6qYCmPqtfb9OfvEe((=kuztskz!@B?6!8XtH98k*sOdnMDnzyS~GX{RrprTZ@4*?dB_(|knew9CUK_>jA2m{XTUmF4E z@oxiQ;9npZ|Mu{|QH}&g0A}X-&Cb!*%!S$5>OV>Ur^WxWf+6%DoqAL_{%>h`|1<4> zGi#dOp=$GQqxt`4TiLrhJGeNSnYhY-v$Fq}=zr>|{@+BG z=_2{4swV+R7{eCfWC<6a7%^fvtgIMM19A)FM4aepb%cG9ta5;Ej-S#x@V!y$N=HH- zyn_sf=QN;K2?=)s$2sVJBz26GXBKfWWnp?^XHism2vxDeb(ae-<_Ah1bWJ4}^IzW4d(Ge~A3!M4q zO)=GV#$H>Z4n>#*bjwx8Z{<(WA>+3f!1gq?Vn=8FmcYo90J#J)PE(1)`;_W~z93Fn ztI1|o3-7+c#`6_FCyGAj>2k58@tLF{UPQX!j)6+g4UAux_Zrs`G-23JEav zKWmyS#?Q+VKlEMNSRy`An;hz+7e{A@k^%tYYc0+rnl!P;z{w6b%juHQ`JjW?sbjMHbM(Hj(^a{_@-h;GnDH*2;{$y_(wKBT8LWxI z4nH^1A^CPMy5{ahs=2rB`t4b0jEJt>r%5glMYyfExNiXW@wva*C$l`1IDBE?jh~At z(V4GR=g?qP58b5yjx^SG&9 z+{|u_9af@VRc<%6_4V&*lDu!Ifxr?gMcbuu4kFi@d=UC`p|6Uk-upkbZ@A42ROV(% z%%(?p67_e&T@hCS+))QDv}E>Z?Un6c+0MXu+w&>44OI4=M&A9_uw%@mv-HU)02d~w zCrAZDP~qBz$1`Pfm1%|E`7Nm)D`)5Qj(gG~wmJ{DVs`Cjg>CV|-sOR#**=4A*Ev(; z%kYIOH~q*MRdH*}@KG&n#vb&|{^IKtI%}$tL49c)9cMbB05xXTg}c`(^jCc;iXlORJ)>(`3 z`sCz|y#isHMrfb8D53g^5^K(b(Q;%i8-%qAzY&8Y9bs~tefse!CM$EWtvc?rzC#DwD|ZsMDHcYGJgco)VSJeLP0zR5CsG^`9)gQp5_kBxNAsIPn$-RAGKBKdS-R@BRF8``JOOOKr& zzTG4;);x9N)ei0ZHB*kX0}6!U&PiO5c;YO9-&-f7A$4{bejU>pI(v1jnmjb0p8Gk< zxsu(-D}ZhaEGqdOB1Dbh8MFb|%fAT+V zd)25Uyfb^h)cz%RP0v8jd6y~9DQqUPu=OOsh~s5x|8jFE59nX7c)&{HEH z{)u@B+s^BmR@TKTzAJn>QqP1TrcQC_L9ILdN15x5Dao6^^T3~WYbk+aIz$h&KJL5`HD zFrRhv3A~_5L3v5rm7lsjzY=kl>nG@&-X|os(EOnW#wl1eZ(t z;DYMXc8DppzrHbroaVmso~Z{#p*43UF{-vc^04pGBM@?OG>S2?Of9_k(4&Uy6;tSVbbJcsNzLnj+A`3FO4mwlS<^Xg9Y!d?*yKcb!~*Zs?`j0#9)-X+NH+ta+N8TI;2%NY#0;B^5LpbH zA<#a*{2JjOzkpLi2Pgg^Z4{A1)fZVyps>bCEOJWkwvlD2=7_r7mekZ?-z}F z$qufF>KN}=4DVUtSB%gk-ZcQKBcD2@jr4vS|RGO|uNt z9?J!|*}dXZN(?=Fs*+@J;XK;+5J}%0je`8cT#Guy0IH~($ql~>FpXdJ@gS9Ka6B2g zUqMqR{=w5|*2cFEe!+2Q%l(G~e!(he%cF;wLZWbRUs;!7*%LaI2=kL6koH5`A`GC| z6JnHb^RdB@3dz+*550s$hdycJ@Z$rPG?=95`8R&SXsE2L-{~fm@I<5|#-Z?JM5K&w zp>lFDek@l)^5Y#+>$al2LjGwGCZ5(H_%*O$qMu6zNnxRUvC+)v2b; zy1Xwo>9MI09Pv|`KKpnn(&Z&P^jarVRnI`9%4RgCYhM&YRV@YO)N&PTAI2*=mlvu= zffmpe(G60=v=T2PHj?fZ_Vexfs>wD+uNo$e4+%lrN0JJqwWPphw_8NaosWnkVdUx2 zQ%$W|;T+6KFcyaqkEU`Bo&LZ>Zj=->YY-kW#J}0SoJd=rkr8AHP3;oPu(#_eF%CfO zB&Z+Vw+)u1z$L+1LgrB+wVcv%8pT_8<;rQH2ad(?r|Ojyp0tk_jz9yl`N;Sn%un$x zwH6iEDc*^`d)!>a0eNCHTfcE~-BM~OX|X6Bq{mQVQDuiPYD8|&Hk9kGf~Rk@fzNV{ zVnLLa`@&Vu*AoedQ5wFo#@9=#=K#lV*wC^aExKNg9PLAKrvz-2G_OU6(ZwYCfNI6J z&sL{kEjZ1Q8ty0g7b0+9I2l(>$~Vw`<7_IsmR{4?!`@Yd8-`z~NIIzAEvZN=Rm@j7 zf1?lx2ysBH64a|@N{6m%Tz`>j$Dpk5qBy7EHJt5N|FdnK?1J>$!cbEA%KE7W^y**O{J6R3ju~Xg(c* zeD?!7Cs>K5U6kJdoaQSA-hA#_GE7Yd)7KgiNjeB~=9-^+osF9^xv)-U)_7iQ1ob!ZtyJB(a-P8p#l3dJ!MI2$H) zuCa`+-IL#*@goIqwOc1b0{j-$v0bh-UovSMjzk zJLvPqV?7d&UgEbwzRd=(WIc^0Mq9JtAO$AVJhc4__!AU9%W^?;eK#Ply{W*_P_1Dmbq*q`@s%4h8rK_(ma`PjK`luw$A@dLN!tbc}`RDi%M3mDd(&{s7cs(D#%+~Sw zKguyN(@{La?`Ki=YU?^5QFmuE9$Gs(Ww1QIk9PHat_NIe>Osue0OG-de5;CB`p;wV znW=uLy?2LrMImLd@v&*2SFD0BUOY{3k1Uv@=9O0^=xn zBUSHR>V~rTsx|wp7&Az;MRVsr=y?Si4<@f`N^Y2k5)(q_9MJfir%!C#
layeTwBW)s(;I#!@0wWNkdxLc(Y;dW4uDhJO7TC>VEPxD-avmau)`-){Mzff9 zh7xn(2p@Rv%RXE-2w$+LkdBoAwKVKjfw_GD(vi7)n5I?g(s-J)Giv(+t z{_eM(tVt{qE_CDjKa|J|TZD#K3=oShKMg@;EVRf;5Dk}clPOvOOkL*sXn5$hMm0=9 zCxhE!DD#&CyZ|<}Kuy{iwOg4BL?_M5HPryZO@+a(&E1x~0+La23hz>6?Nd_Cd({dh z(UqO&5*0XXjsGjEM=4z84pzdK0H|G@qn~Ky#7=^aDfe3}L_&oV^uEC&N=x0nP@)|?&b4k;Xuu&rBIuGmZc5egA3$@$1 zo7ME*%C6ZoZqV4%Q=!d|-F=0l%#T~N3o%UF_wB8%aB*J^7KvBr( zcbm@G-~2`RL*XTDX5pXpl7I7WjhRA!I{%60T3{V6%GRuF=xF`Bp@Kc0-M1UOWI1kD zjt5Y6g_aI$Nc$SEcn9rKY>DuV?J2%vv$N3B#*$6D9A2mQ$@PaCxX-8S66l*FZp*K0 zi_QwY)yz?b1_wLzTucL3SOf!`mi8edI4qLoF8P&2{;3V$1=(P@$PyLaKR zG}tju5{Ki95@8KLh>$=2^2Elgyp8{$ypO*$j^AaCuDuxFISW9oLN9KvwSqAa0{hd* zLiJdm)naQTKQuk{8g^b;hu2L-vlGQ;9Ub@pZKE0X`Mk6ve;L7%gL5k%cl8Gq&4oL_ z{tX~-7nA2BYajT$FeoCz7VfJ9p)y4 z-=LGTOU;=dnVZj9hFU*l$rRA*q;|MX8HYtZ>BetY1WzAxBWlE%!^(`{?{%Z1=ZS~P zl&F~Tk;vVV$0 z-5BnzskLDdJRSXc_GlrxEI!*}@Q#div5;BRE~?q9-mQf_H{^CCg=Xbr&I6CAK|0E2 zynZ{5TkaXTZN@>JQgND&FrT;S^C$xIJV$6xgOK#9;V2t(DPB7nOfi{ESd3tyHcMqf zKbyY2Dg;FO30t8mME+UMCky#3tr5!~O7UyZU5sb8|A1R8G>o03MpPUWG>C=iL|P*) zelBhr8HN!ti`DKKSGZV4)(Aw+CM|o${rbCJkZ`0}C25TO+yX^JtuvXqag7s{{=iRO z_rP{XY=QTcje2|ZvRCBo)7c%KV$zmey!rl9yhunvo?#XS4+W<01;v6&X$3b-{g3oD z@Lz>;Y%XWgx#22P$MLT!s?S1+PJ(niWlXI$T$*);;thxg9zrI&yMS`j@xGJ4ao(WxxVCl(_UVd0#&o`p-dz27y>kkBy zx=eQ;dWOO_6kqq0YXLPslo;v|+6z%=PlPLoS05+>#d8z9C>{KBMQOF(u*m+9fuK;b zhMT6(a$$3gERWFeA;2_mepv)Hp{O|YR;Hd}M_xuly)yc}6_t=g?(xw;qN#|{v=eFc zXMAV9bdi%mR;3%ygtVGZt#q?+9>eTLlpeBaq_PRFM1&~n_eoPo4Pi(=%a>d>qgCtQHsl&6X`s?3h==s-h&>o?8b_S6FV>)7y<|2ZZ-4eUlh?aGSh(25mlyh|ydJ z3$LSKVhFPovZ@i_2%tJIEJyEV%s{pCbY8r?KaO84oS(Ygo1E1z*j%`b8yLj4h&=~s z6|~b%BgblF@6)H0%@{*NB5q_BkGuMm4*!@-)h|{{MVU*Do=b&1j!@66E@k6Bmh0eA zy^5rB0Sb9skW8uM@%C|66)y2w%QPz*FQxf;>`nyt@%0Y(8GFy<%oq*TP_MiTjvHU2 zxS!Jk|Gag0mb3hhTPOi3#?%+>ecqShFeyJtNqi-1Q3&!w6TnbcY8M*~vMj-tlGX2s zW`y;@Zr;h-Lu+tp{bty+duf(x?Muq>d*Hyi6=2R5{bEtSDDT>_G}kQF$p3t@3f19b z*TXS?gVQhEaV}Js|IjMcQyxz4dtuN))G|qEh{E^{zyIyQ_aX1_Cy3z4;OlfPEczPL z1^HX(xN=)-HkK79Af;3rO@0_cS*A&1ze(vK6g1ru~!suO{3pC7ce@jOR1HL z0Mv$2i{1x{BF=~c=zBQ8`Npd(9iCKHI4S8L5DxH-)HD5}?GZ~3BYb8UIM zZp%1b_^Lg=8_hLc=u{whF%io&NAflVOBXGCj&LK7)o_U9V)BVl*?$Y?Yw!!K6%tis zmQ#(iZ&j$`F+_eEOW%9{O#lgc$AU)F1!SW485kKeFX2WnwbaeyVILEU$(!^xpg^GC z)=RNOeJ-VvPd-GPfQ0SwC;)E1Qe|NVX+{mf=O4ew&?V+K;ONeM4St~2!2jhSkYWY0 zSYXl~B_ehO(uYyNGTJ@bC}5R5lxQ)3o#=BbtZlw^m}QM@<2OJ2Pp?)BX=}#B;*G+7 zt)E_h&a1Z0y}RhRpU;{^P3!kE6(d__fmI5+&=i)rZBC7f%mD*HX4n0q6@(@ zwayO;n5;!89rGQw#6@>cWg)Z`HE**;|))H zxR@mZkJnHf6Hx7#|59lYm-CdNPTmpa4xOF32CH=bd@?doV~yDb3?G{M-7=ib&@QZ}HfC@wsza_<4rr6pT%5qNS%CVwzOUkH*0oiuR!bacxGr z!5V$`q3dYOmdeaUpmb6!)u?XFILUW(uL9w+Y$o&)^#bD1yXjgoJFFx4YJ+8lXT|xb6Yh;<_4kog)6GoflybyJcRI5V+eN^j8+`x7RQoS$6u|JJacD@eTr!E z@D6KJSwZsWG9gs*8^1XeP2*)})a9+QvHKQxUy8ToE8^t=CUC7fq;x$BgS^#z`x=3% z0{h6jA#+P74u@*qPe%pNJN?(pxUX1+JNcX37u(!qG8WvsQBaiz0nC>VJQ`fRy2}c9 z0jZA~t4N$z55r_slWEl~3N)Yg?7DC2gG5)n;3!p&x95ys$oG=QS*kC!WG6~vo$+6+ zPL{H?T8&$QkIJo`lKbFJGb!#wDRdGJE!rjQTLyDaH&^I^@Q2GPHshQ^PFo;-pNl@u?}xEbvVr7 zWF6ZEB3H$X#2f{+C`yhjKiJ5<#fiTgROB7l^`zi&IH8-Bz;8&jW$xFnI~MK@E8GZ@ zx*bc9r+yn9in43$3|7M@c!NKF;HzEz`K~gsp+qzP+*6!4vBREt3&m_sBP2ZkHEcqJ zX7(l(xfbL!+F~=Q&<++ft6G=sF&~wA2G$%7=v4bUat)J|2l#9GY-!yZ|H8C0mnw^p z;JiXJ$@y`h zZrqi+bfzS@Lt49tM3fQj4!duVug4L|?(c)S<`A_k9c>kBpXBL=T_4<&awFx9t5|CU z?EVzLav9=1$`DsQn3s`{sozNm6fN1>TC1JvAqqz8V$#W=7g0sgt9DJ_NLcCi2(rhMn%A$`RNjds46t~|V zC`awstG5QY!;sXl8cVc^9d64~fIs^VaGXNL!j_mO zebjkm6G<873ZOTviruNIRr6CbTjr;4(v}tAe53U){@yw1*X-eiMhio0Udz+F=Pk~E zLy>x6%IIY%bm<>DOgxFf+QTxmAoP^B)9*S1G<#xD0MvHBwRHI9^P+eeUOJN^G=T(y9A>ke@7Zp6(uZ;*D*qL zfsWpo%GHvGHTMVSYcwT{X;W@#8+`{f=1Xa2S*N30E+vmKe=T#)YNrtzR0Wz&a=oAT z?R@=UiXdcfpoj|1mDmd;V43^Yj4b2npJWLrwIM%~m4E4%+00?CX2h0a8CJ-x)6L7$ zRlWE;pKkq4pRk!PmUPV&a_&y#3Ga7BP%7} zT);kpdG}H~Vdla3>WDu<92#Y7aWX|jOO5-P^!_i=z4JheZ3XrhKx`MnyjY=Pzh(aC z%#(YY@lY^sxPLey;o};`Fy%`WL4@~%Dwt>b%~gF@%1K}{6PBFSkEyZFSHum2Hj);^ zN%L&sZRhu>`c=O-Uc4%jTU=kW`Z`~urykw!>!LOqRY@i;)Z^$TJh!f~e_~^0R+Keo zThr>%byjQT?1~6)0L^evcwTCz7X1`i(W2pNcRqxeZ$o$j zmnW!CuHbcWM$koewZfb{%P8Rc9cYh?P9Al@5LQ1P6lnM}&{RDMZL$W{$lb}}kKIG` zN{f9Paaf?PH`wfxtZOfIxO2%u=>x;!iSqh^ui^aGT}CxSK;Eq-Vv?UZ+RwdgXypC1 zYdd;7IuybjHN$9+!tp1X219qiUWS8xbyeZeOhU|h13Tmi-!ue}oQEb!(8PbnmHWd2 z8@YRat-D(UXpIs%JGRUophGuEO^kZnH1y1z_Ts0>JSUaWoh|?28)P)5`7#c;oe-D*roR7v>$hRB9UXD(`#eu?9UJn_gkLaIl_%*N`{9W|f2>EVVBT|j_THt~Ay<$} zuc37tIH4raZgciTxwSTF-@l_gHe&;CC6jJs>i z9P$_V-@Ke4*9ycnD5|khe5jS}2+$*uhRFpE2XD~K1GlHwS{Pi@#8gLc{WrB9p&i!q zE-$cGc>4qw*68Z0pYN%t#XXi?%86<@mx$&7DHc1oP)wKKY%1}7G*0YZIprHAN%u_fp^a*ieerVwBkJ`naVpzlUAnZk z^n~WY(F_=9kp6jllStrRw=iVhzO~jd{?i9($|e2wWo149-EDU2omvkuSdmV&QB9l) zfbi!tfz=I5YJ$bZElMo5Ut$JT{B~! zi6lFvS?s&JB}%1WFE~TH-5V!i}~pxxr$6Ey_M=YB;NL8pazYy=HRXABkIJvL3s!1;#Td22T(Y* zXH_`j+kiOI+L)XO7c@5Ds>3+*gk1(x7L+7AMF_m{&4XAD-8+Xi?Dnew#=fmt>)n9{ z7n)#%GyW)3!i>>M{J1pPVro|-xRSVR(|AZoOX7KmNYmC(;Vmrb;Fy#-#y^2zTapK2 ze&krXjhRks%`CEWD6PzDEy(D2@e~z()mmCr;|*zp4 zPJUMvtulA^!~UkgK}G@M(I#y?PN;6^wu~VEl+!@{1a)xA;&$X1h0PpSfE~ zMCbWI3vr`A)sY?RB%tnuo(ARaK~x9+=!eN>y2*DkEc~Bm!ePjWS=2SiDtrUGJYA6W zvm#D7iG1`!jwfIL0AFUTa4cmT8NL+oqDF;ei*;N7o*0rg$jXNPv8UJ*W{gmiw#Get z-qI7x-OC!HXW+QhX{hz6S{5Rix#cB0wpP8E`RQ^o(9+!R&~#Ln$7j%n%zFUHxR2_y}U-GQ8H?ObvaYB zeMMtWxMM;t&$%@S5WK@ zpnK}ap?&ENU5fxSsb7N&H5!f7^V?|W#lkm-<*R6FPS|I16%-{?5T#8 z$s}SPK1#p4T60MjKQ1!5z7u2lrwFf(td!KKYezRBM385;e}@Tso7InO7r9%f_#;B} zGuGHhs$pc% zM%I9Hve;#a3DTD=`4;Dpzj?RLAG91j$~oQlHi$04(@E?#Cbcm_3b0O5a_@?xzu6!k zu}&Sh3?ifZvWAGs-^LQuOv-(vh#GLtcsmdBu(h*=5PX?&sFCI^9H<%ENgx`h3a90d z0p#6PpTBW%3m-MuUKg{skf!rxOK4ie?^)gM+17lGbT^F;v~sPCCs53<rXalQ0*|^dVo{^JHF-q|Y3Y@;+glFArri-i|X|n@ZeqayU~35;ew3mfxD-dkx7L zHpE7k9S{ytJea98y%?r)g*CEDe!k9#1X#lezub>do5J(Qi`y4_!w*8jv{d`?IpmDM z4<5igTZ2Dsz&xvgAF)rvBE(QUV8ozA+`0;I8yB=c?INS|r$p~AB`R{H9zOeAm;7WJ zqee+TqJP?@Wb>uUw&j=+JczFxz3uiv%XI|_cx6quUJq^1K%3pr7Jj(KswNP*0kM+P zhuBOM{nSh~it8&0v1N4K=tPm(SHEhUM#&2zn9;_nfkb%1LjnD$~pFOEP^nrxEk zWMA{H1FkEG>4__l=veYQ+o9?kFes|=?1t>ME@L=Gx>DwgF z>Vb14I2PQAbJT7X934?dKX&;Gx9f(+HzV$V=GBKfQXs{iCrQFkj??N1;Ik;ba@xt{ zn3ibt`z%Pae~qk)+;J`PK_Bwkv3VrC%M61@B-Q{Ck3#lnPld_g=XV4WWINXJ>G)P-N&q7MW}& zl8GXu?`>eI?h4?k@d{fHz{*h_Z?>&mG5*a^hie;x%Z-Y_(_la?=npFfiK2yC@9IS3 z_Nt&oU-L!H_Uc8$_Ugx4me?JmFk0ZyEEN*ju}wCyc*q9U zI7O|$Yl81PDBjU9#k*;bwZ`Vhmm+S6E0i^o=K>q!QFGYAS3?^PbYw{i4CTf0U8`g9 zPiEIG9T~pF3ocnvB)9GTvLB&*;66$P?IKaXP26jVNt4`d#+r;8d47dd)(aX6pN z+Y%fNEJ^v#EJZYMOI`PI`(PQcYYA;6%sgRDd!C!)d-XU9kb2-A>BPvA>)Vr8;vDI6 zOq&%{E-EEtRmYkD63+#ZuiawA4Y8dmG;*i-RL{~>+R%9k(hZP&@dRKhcZ zqt%Glxlf!SY=brlpe$i`m1hvlQ@}Jet59Wz$FK-Tp>k6zr`;#g=%SekhTTHfmLZXK zSwV-`AfI?-I-|^>xDSA`^FI911_8lqnezIYwLmJn*761jcY7)>Uw$Ri2gNf1BD;Iq z6$hVs874v#olJL^s#xviI|fI%jIYa7z3xl-(C@UJ44>z&ZC7?IjOEKZVjHQsDbu=d zSm(8Ce;(|8KVkIGz9lYTUdpzte8g?vCQ!F}S=){=XwHhYxEy9P)Q6P$ddcWjh}VW0 zJ6!8&_E81Y8Sl`d?U|4ai?}S6!8smZw3o^lG2rIpGATUhO1B=fs$9B?VtCYj}NGMw)AWUT*|^NSe6Pek9#*H;fE z6O54TM7Eu_(|3t{QziQM#OHw%x*oTpGUhw$TBQWGpq{=zIlr0jRy=#$e*bm4s?ms) zB<~d#ZvdYw(9L{HV6ox*e4~+pe5Op9rl>@(5{vLZTk8nY5CE>KitifTW=+Q|aH1>b zTe5I>8^7bQFqiD!Lx~ttN3XS!RPf$TftruOY^5m)-&-Oeni11+Lp(p1O>(UG`L6L0 zt5HG!duIhGc2ZS*+wn?0Yxi#NF0`WK7V>@T;0aORi~k+rr^fr>50%(3v4JnoPu?1u zf~cH{2ZjrY3lASf6&*IlB~s#EKxXOhi%wZh;>I`6;Tv#$U*C_FPlA$$&y+v8%J?da z8#_Lt;U1<;eUCKxc^SRDeBZ0O4Cf6gj+vjc?xN8I9%?{j3QQ9t7c!K<;Ip$HwcFbW z7czDF_odWXB7L5sX6?6tW}z@E>Ohv;JeSrSDDT1yKcT|H}uR3tp7RHjeEG5 zpPRsX!#>)qd1rQWY~RS1uXCc_y({}=m~&!S34E<7ZaK&DN49_uA)W*8pPf6YjKFw{ zZ}q&+(MOnn02)T13Jdw?dm^29OM}w4?^O{v2hQ@a6n5?v5ER2#SQz>EOS0`DeoM9H z2U+|%`-x>AxdO-7=HR0MAUF2iwP0moE-dJHZgAxhoJ$>QBCX;sr0aOP>k(UTaXaxB z2R($JFf;OTRwUP5zB~Em2OUuAXQvo_rMe5mI+q!2hsh#NwqWE#ZB+ex8wNJ`)tbt% z@77U|8qfcI+(lNqLxw?Y!pseR5QJ(daoj8`_5>&1<|D+AG4SaTNHTPJ{6fkM0#AMc za}ndM6@M}Od0)sU4%Ma!3$Q@HJ5;m!9(Bsx@=7#bT>AS>Om%l+IKBx4O?6!zO>gzy z=^KK&Dt)Um-&)-1w4OSA60P;>%lIzKqXZpTD2SP*r^fBdd<;8UqLB&=iC?qP9Qm^x zs@s%EMLva91jTcdi8psP-Vs0Otm>&V1SRmygrmxqZpSU$Tq*9!8{yJf4R! zuKF+JF%1cK6!33e7Aw_hhk+zAg*)2$s6Z{(cfY>jyDoz65O8o0?f8L*j>+Mbjl5b9 zBIS+~9RtANo|Rji;BKI7=%}_gjZfeSWOL265;p=NSdW9~ zpb5W8zcnfT8MKYBJ#;oQRkrJN8eMD&{O0E-*Z`iat*q^kXJJaMkMTq1I5^Qi9DMuw z9?^96M$@duko=DbSZPtE6Tu~Z8;GeUGjxZ>``g%<*NeE)1+WXP=vAk3gD-Ic=b|A)7gJ<6IFiy zA`OtqvPn-6A3K3q!oWuC8B?-gsd`TOjz>cqKs zYsJ(`G+E2EUo#M5v*TeWmDg%sBXIVgWETMxq?5ONlliQ#`&L#ZJUPXo$~r4}&jO^i z6Ih&F0*4ix^JMFA*2E-`XWFF9&zFFDgOI!RPqmnyz#`^Y6fj zNy!ISS`vGRPO;EMy{b9^Z6NB2I5R4J(?rTR-bd@FNN{+e944Z(7s-AP!SG4!XehF) z#oe@+({fsowvTAaCO6yaPL(<1jg)!c$J3X?U$suj864XW8YVyNtulX?4o`8Y*HQ-v zera+eHVv0Nlt1XCI&ZcWmyHM)QvU@6mQm9>Ck@`LmkLiGALGB}&I=J1D>yGd)+e1a zfKPl-$7}jj8_jX$zg0C|_jaySU|~11qHg~F6|E%Y&WBKg-3Sw4KL}r3DBv(&4f3h$PT*t%6RuRQn&&L0~W=; zV5x_2Du!h(qh)3ahN(;Z$iL<9&$Snh5JeX@;6zy+nE8{ifZy9erq3jvh5*rIX!7Q7 zHP)?7A~^Qtu_!5R$lo*q%$MEMrZYPZJiG4G?C7V?EEh2~uaDU-20`N5@w}=nIVfp+ zIe{GFlApmRciI8sxcXulFU0mhsd8l<6dr_90w*trP$ncj#F`+`{TyU<{ zY~%Y`DwOc#C;E6TU&dg{E9MNai4T~6)Vdgd#jBsd{*i1dJ(;%ehyq|>yYpaRWdC2) zrRg`eTL(uAr&5#XAhO#pk1fu}VqS2mAFJR>wbgVwrY*b)o4V%k?GpqgJh2;$|4 zWPw5t87>#H=dw3oZXlCqZ{LfL&VwxP@#eo%e38v>^NozOI5MkBHLI2%G`o(U-5opL zt5ZHX7OG&qc1Ykcnt>@`?-Im`dI(xUFt4B5V3b1CRIb&!CdF4f}Nm&g6x2WcyC{h1ZjTN}llr zr_MTW%H&dv-K)K4wX1EHH($hsjf2TGMaO1zxHk~Q8*9M`VC6o4c@?o;^JArQt=?-8 zJJ=5wTY_>o$aq1xjC=ly~Z?zy)6lNgwVD{iN*b_D`Hnu*`kPis( z-ChT5NIb|ZX5MN&5e{s;J6~Qbm=oi5xETd~-&@NT@X$5V6*5S4yUn7vAgHi2Y@}=7 zYBv+7{A$+$99(=qIB?)2hbWGAT;ArwsfSnq4~m(M9!q6i4c}gLEME7s1Xr`Y zci7Crv%D>j!qYqPUQa`ULK@4wcaHkE#M3DQ7tmv%MB^_3cFF_h0?Gr;J&J>mKA{OC zY2B&k1sq7wUCi$Hvfj9x2i!qL0&9{DL2jVl|MgR#6DRKF1Ky<|+hjC%TExXLM-|&w zZo>p1D+3txYm^?^5!?&gy=F$18K;MgyZK<};_mDr5pY2~T}n*Ud-s%ae&+~mO2pZ< z3-Qlngt<{SHu|VUy0)$q1|f5XT2yrN@Q_m;e5@FCBq7R;FW@fT&_jaag$bMyA1x41 zp9cMaHIi&joY9x2(OsWKAs`LBvp&0{7`E;hev{EYPe(w&t+Km}~{ z?ICb|n8V{QjNaM2pKm@bB~~`xBtxcT~G~j1UboxE*45G~0o`T?R9``UuH|7UYJ9RQ_n%@J< zzds!~r@T+HwrB3BrnCi)7>+z8KLeB#AzOSCD`?ktid6B9&Tq?n8kbxt=%=_z-KT=j ziz0*<2RD0CDV6%Ymtfp7c!LL;-l@%*Xv7{Q2dzy$N9cc1XFM}7sXD`Mkf?|(vNWm2 zQFdR{XcQeL(s6x!ue>50-+i+*dC~W6?A)(?1o&yZI`~EJqfd7(9>L{00P4I2!=aPk z6Fpy~0Sp+A2r82Z zulrdi0cR=a+a&g3YS}Tb9wR=J;D!$_vvWntck%h*T)fG}n*Y>T|E%U`Y_^f9;wSw( zzDVf5Y<0%64U6UNFCoy`i*3KwN!2X*jzc%$12FrJ4YG~2J>5*4bcpB%DUf)i1LS+y z;L1hLgN2m#e{&^9|889>pXcRM18~hXT^lfeboTt~5Y4c25w7m0(7U`(gnh7K&YP=- z82k(idOzy{(Un-ZvdnlZCQRigp{;#j={1ksqwn&Y9MV79g6%IxcMf*W&W#zbrSQNKc!mYt=)60T3J2&|cLaFv8t zg*1;_xmEsD{81{Y_qBP1t!R3RODUbU=+CecA4{Hr@$=^VwPZgcUyTyJ)JvjT8H6Md z^eK{!)nOERvEs)OMd_81c;pnb+!r&9q-E1Fv55FmnQGiaSs$z|f~d(yOuZ(lo;_f+iECjD)IL&$*QMw3yK z64WTxUU764>R1dO#wW$N9B$o*RzR##F#0O6gq+qj804*HrkM77_#^Nq|5OVXg(bV4 z;_JDBLF?_9xs7ku=HxvYhuvYbhBl&vVi2ZMnj$6{?E_M%?$E`TTj2B?SG|pM+lLWXv^oY7W*c)IwV!~bVu_HT zs25`u6MIg0wvODi2OxBpf z|2FKI7tDYp|jHM;&WDQaC-7D?=!NqJY?gtpGT@}?&(?1yq_kM#u z<|hjEWMIFK`eE(;As(%h#7^U*^w$LT5`R97u&yc*Lb$rs;qoaR!pOhy2o}o(=zMmD zIN;&NHCP0P{4Ol7#%T6xE&t#aUl6xGQrC+Dtd{oQ0{Hjp{9<|C#W7Evj-Rc1US{e@ zuFU5sx1=?%)^IT{Rwpo&D=>x=372#TjMy_>-ciR(BI%lVV#hVAB>uW z-BcyIo??uXw{7c(QiQcM|DrPCpT!N=3~tmgb}khskmvvg-6dV6skg*S91;8WL!A7nK(8`Qv_4E@0gY<6nVPTqv3$Wt4F0;3 zSD`=1y`}oKXMW+s3bWq%>>_^5rkO^m1rKiW^vbyxV~~g_p&Fq;K3#)$@r8rNmZro4 z96g@GZk7QWL*pfsGSEQ;4_YXiDa)ArVRmX?N##s>pQW3q;SC-eK~NyI*xhfmj&@t* zln@XjW?8^btcUOWY88ytyTx%SZoUW0$XCcuH$R*!E54zaJsVoF_A?b-tP%-0JN=Od+U8T3MF7e1Kx@qJDPte{6r0Ov; za+eLZX1=5=n=L+TI+U}7z?Hd(yH8_+G_T%~ZEx;&f(8b>Ic_A2N=n0o+Fz@c0{C%y zYPMd0>1Uy*HK8z*6~(6~0+}^NFnUNGCd4ZQ11TUKq>J2rcTc=afcXA^+kM$?ho-=U z=!Dc^-J%5OZOS%tlV4LwZ~lg6-~R_2qKe~F1-i$9XoL_n{iOn%=j1}?GlJPdrm-P@ zA=CJf2xu|!&2+?P@ps?Plp!uykbdY5fC(%B{T2+$hV50}j6!_Y2_bkBU1tO9B0i&s zP|$&e;DXvg7x)lb=nY!1F7h)}2n7`w0q!3wSQa*@1Vn)cQHQQWe*O!+K@Bd3dyNMr z;rSfty1f#IOyT-8y}Nxi_|IYo_8$v)5$PE=qz$oKd($1RS8LOSz$Xd>BoX+OgG|`p z0(;MBncv3NW_fBr9m==slS%_k> zgo-{|AG^^w)6Q+D;f64%dB4Af10g<$52_H+q0eK$KozEm#48RuHMz_yPA|%(>s7uh z%r^780tM4V`W2^ELOAr9K7e`q`>xp5*P-HHz&l zbGdG3z&@&^OyVm)-?FMH)bcYWPeCx=xxede<(|?e41m2PiN@fHx%wJ_zG~c<`mv*s zV0^mvtItYv-pTUtu%VXchgjs7ndHR9nb4L4^)a^D4BO()iLo2bujy9hL$@!BZH~67 zf|(WyeNz#kFaBR{e-+iD3;W|(c{+0~t9WCpFzc!cO&(3k>enz2!`WD>2M;ydlldO4Fs8g^rrSiT-~*QVvEnBV*I&d$zGBlR`j_%i}P zY^Dh$+cWC0!l7T*eoyL!J6JcWC&>}-d?>qi(DHaQwchU+ofFO?Z77dQDC@|EZ_GC+ z%eQVeI1|a{|Lr1(TDJUEN9?y#gPQYGTEtg+3@acTR~7cwioArEoJM}`e#%6hB43T` z1{3Y2V6S4R(-Z~P1(Mp~K@sMCuy4f(&M}OietMcoXN6LoDsDA1;TE>k!#K?Zt&VDJ zP>FF{4Y<^e!LBtIDs8w1Q4C&moQl$z5<~Ujr4R+x@hS&zyw~C#G0>8sAC$4;<2kKO z4*)cm2TEtYJNOfb<2kHpMAbj45sMJYip3IZPat5^AcB@Jky6_IN z!JT#jck6un(vdGxf_y6#s#+nK1Z@T z{s|2w!%;^w>zhskL4?na^TVzI^?~2qm1#%z0&Q0C( z`;+H{O_&)|>W83|R`3(2mQcV+)NFhM(hl`z;)LlL@>>@ynnZ z9_QAR<(GN^bwxIF|f-?93%R(%&1a3dsW;zZm1pXXA1WBXv`(cY{4M2R&rAOPwdhOsxNNW78hmZziH_ma<3@iMG!NXZn0un*X%KnP z*1_lXI=A%Ba4vcK2??kBIEF4}D&^U~1hLyBjvhN5q6w-z;foFv%0OdUuoKBiJWw$3EgGpn z7}|q|c?y#+H{umK*=^VjmuITJdTJAKTtS?+AH7bT>dRIjE9EA@tW!!x)11eVB{UVXa?{LwFGEi zm7TtaZ|uCSH66{nCe`#`TNz?u{VOQob_*&R?TZ+rkbX!;=*v6RNiE9$8eQ@zbOlmc z!)}QaZ*}(&^--W;$rC5+_Ks%q7_T%2Ug&1_#cFw!P^aiWCQ8xrrDe-?HZVCh6}D(t%+>sWw?>?j z*lP%wwi!PtP9wA@;`ptJ*%d;ZM}qMo?f1NBu)Vv4lZlqlkE-84=JB~&bRRieyzEK2 zb7%B|nk~QOp>S4HH{oPM2}$~U0h_JDWjNhhE!`1E9&WiR^5xk_M$v-B8QNaZ`-fq{DgloRLT|N`{R#QmoFR#{rKKge<*1=0TfxF!JQ66 zEBFM!iX$9XiQgFsLWO9ApZ<_5jNG;_tov|VBNK?mYwed-fbbX9YS)bDcSR!}Xb-Wq z2sUX1v{m=htl=A!IW2ZBmXX_p{K!_kY3vgfeNs}tjf56noP2_*DSohb;Wi6w<>)1I z&W9aphz?H=#_CUDXkTcobU<7Lt6K4?8|z;V&~}oR z-^GtHiP*m}NeM&i$3&kq^@c|GGQP>W3|m4IXuz@(EvQ=VR0;JzRw#pg)im&aQKov3 z&@iiUNgu|`%BPFh-f4JvtH>o5C)-jZf5Mw#f$_tzXI$8!=*dW{^aGw@T6T$P43nC! zkkJkccUbJTL-B(t9R(Ae>lwWGnO5k&{p-Q~EUb?Blt^M>77Av;*2zXnD(vhxPvcpe zm@h@Nq?U%S&EDhb=dBi*CA9z0)oaM5FM_$z;na|vtzmy8;Na;WPgOQ%bzZj+53 zj}VL`%M!-!K*yQa136&v@HF-f+wcs<9+RoiMQP+k&{OD}ZuVySJO-4pZD{d4UxCk? z*>p0JJUS=W5KAzdsJ{|1?htX3{0`Lq=jeeJSadH*?_UL*L1EB=T;0{3g))0)(0qij zrW!@ZYZd{bAHlyk?U+9D-ekDpv*|3})7Xy>%s*F_HyCS1fIb2aAOuoR$A_;bK#LJS{_Iddgh3qO*wQBKl& z{5cIJisGYvQx?UeR1B|V?o!>-NlAgG``Aq--!2CS49uGs2EM)N?Usfum?#M4JYE z>jN1w>u|{}N)UNHxpA0!n6mA7ak+X-o{X5Av)@%c9hq-Y4M~6MGzH2ycOiTHfxxk& z!8yogq_cDmlm8EcC@KD1--IL1Gd7mU(gGQl2w;0!5{*c_$`R+b*5|G3aiW>SXvy19 zGvXwUK(#U*+?jooA;NrJbKXvaWXWmInP-$^JO61t;X{7E0mdBEs>dOD?2_IvxV2KWhcx5Ox~OOhx3y2)3czBRRy{O7@6eD6)6rD?0yggF1~ zuKq?8RWg_v#Lu*b7LGIxZ>?BNNVtAf0%!scUmP7F^HB;n8#S-@61*H}0j6oIhaixR z?-D4LcK|eRog5+{E|hiiOh)sT zzwwRk-JoT{z zS7X%FH{!26W?gK2$Bs&N+Y#TF0HirX$KYra2PNxKT|V{yD>mEq@M40K_O(@?z|&Os zhf9f*`%SB1S9;_n2s6GSFElqkcc$3b{0T@D;g<+|{w$A5iDBz1xMg$7n%F2oGuR&D z%VtGB>@&I?me5?U?#4nPd+*7>f{qf0^4d$byHgQhc2mESkng>#$2b6}~m#@3?F->o}>;Y8P6j4duO=2B?YG@PmIsKx|hI9IL-<|EL*0aVs?d(El z*xte(4MW43KtoKjfIE>B(6CfOM?=V4$%$m1l1v)*wmizm2y09$UZ1w|CHpfExKD6q z<;8<(O>q*REBDYraR}Z&b=ejUuYYvLAbXb0^HUp@w`YriiKh6f03Crn{1(nxvrU1# zsK3xjPI`KzOqP5RKZYtX z;I@PNW*&Uwm(l3{1$94kkPjmR1LqSzb-b@4!4A!GQ7x@p#mX=3em^w!4;x{PSeS@#ic(2zbI$S8ukn}#OFUSeSD$33ByYkEyoX^?c<8y^rq%|Z}x9kFo)idkYt;}`l+Sa z^4TQ(=|^L?l91!JJ1*9?I2eSV0aOEQZku3KPz0hkJ_33(ToQFa_EMM~6*Xq>4}J7$ zn1v|qK5iX~fj{<>&LX_xRVEB@r;2)@puRZmJ{;~|+x4V#z-(`003E$z>Pz(cBs%&l zPCZ)m`Vi_@gS{Wo0+|vjRwn;GpWMm7SCL3<{Dt&Ps-dF{b2A6@r)PNz=Y2O+7i3|m zOZ?o=B7mOH44d*jCvNK+Ti{iN`dY-xLBo z9=?Bu%y2H439v)$gF|ta;dG9DADwNH|Hhm+;7$KHa67106}sie7kSdL0k!4tiFjcZ zKRHL(OVF>Cx_Mze^JhP>+yDDg{E$(h&o#l);IElj(uh!t4?4Y=EotKYXL=jhu*`?o zvMp$&oGUelb@jcHb{@9DHk5z)>Zfh@g|t5=dZJ{|TZ0YdO5PNdBFd9FAiD5UvlZH2z8s=U1JFAOKi3zn{Ps zwDKS;>VUt=_2Zp-R9F7EASag9-jLE9f-=UarP1aJyQM2v!GQz+v2a#NPJFjia`h!I$H^YshH-##OiVGO(*j7q3tmiV16ONciAT#wUY6SbS! z+nM597JwFv_szGg=XkHPFVumW&E9~1A)#i!mEe>k4dH}#wI$1ocu_)Hbk1WlJ|*uh zI|F`%&hD`OGaG_#E1Xb909=YUWJ)%xdz+}4BG8eHcCQhy3Z7CgvKwftv2lxI*}ih7 z=QTudO`>+p(`Y4RM&Ga}X3~pDW<8{dlN)VD-%`2aK{XLa+GKDEYkRP5F7%_li8-mF z;CB6TToNK=G%BU}-~ zqXb0vib_#QWS$UsL>{O zx$K0~*2oEJpN5$q6?wjTqb-#s(I_SN(M8K+2dfAzLM-UUybXcUhT4_8z?9uv8Q!Y+GyirOqtE*qpfpLd9`ut6Y zbz*CPS#&E4*Dm?21^fMIVGMO7KkXF14KpO?OZ~F2SnY7ufh@y3Y4hsr%$#*Dc&bTj3^48Gw8%*-=a6M~ru5?Ti zQd6wlHCBL-u;3G_-}8^xj0g_^T;;{Ry!C_ztBsG9wyB3=Pf;KWGf#uU1S|9oWtcb0 zmo#C(MB1BO*Rb*o&7i)>@^cqkJF8+^be+20Qr9vGw3Q7Tn~3!(V^D=kU+qNX?q~hp z{_>8yGD`0tVJKippUM)H6JDMj$Uzo=gs8-Q8wN~C)h1byr*q@%DdpvijfQsGV(Zbp z*Zm!*%60i}JnE2#U0{PvPURreSHU*nn;Yfi=W+nCL>b5n z5G+z~CCQfTU(et*^Mjt#QDV1P2|-ijAlD=)TjxBBIHL50Oc{OW+8E2M(jA~c^_ey% z``|=K$UBRk4^z?R)f~W2+H1e^${_h-5-01-sD=UEG8rm}RFVIIXEbz$puz>ZB??Zv z_~H%bRw&LOP-5c^;n*7GcvYMXY2FGSnAaCSgPIROVLGcithhL-GL1U9!OKCZ6bkOV zu;=%jq?HV$sGw$#(CmWpWK?r$C}?fQI(94|cd&5ymCD<&6z=Pwo&SBoH>#n|etz&weorg=q@z#+#KP z(7|Iku2Lb`VsQ+iC*RyR1IRjP{j^BT_X)0{)AuKPX=y0a0 zV%aCekVL~v&?0V#2;{W2eebZv*r9ycHa9nLe~`1cvReJ@FfOYh#45zrJi3&Ze{%hD zD^GHA**2pfAhH)Iycd&4Mdczmzwd?0vs%`A3Ge;dd2Hx+CuCUwb3UPQcT7`=F}E*Kz+bH^+C+A&_ZHGaAf|P z&vcqV>)EvLqUrE{rg;MZoG=1u?uA;C~@jk`PWl&DLb(GDoSJI?6A%nsi!$O}V z3$5%&^vh=vL)qE7Qs3wAf1Q2So?DLZR1u8@wU)CK#5n7Dc&2110!l-K4>MnlX74RU zoyd+XL#I1`0nRS6g1C6F-VL7SG^w&8n;#&v)bf=a$zI!KKkESJ;MT4kLF^%uodRfc@-C z+2y0-7l=3FjEW^Ks&X0| zvJ;I|9b=Q!RVNLOIBc(gFK3IN6c=BGjm3t2Je|4Y(|ZyUbZL)Xn@!3eTAJO6UQz4> zh+YHjEV$Ee7`o?bc74)r*y!xMtfzNgNJO%0r9+(x(rOkCSN*+RpQz>lra54XN+WWi zGA4-F8j!0v#9e(@CJC@hfu4PuIRHg|V5WZ747{1PeuQ@2s`h+8JW;qosvV5APTSbZ>HC7^)y;?*J1nBJ}+o5?$; z*w<7iEK+q?dGv6);eJg&o%L|DR^8q4?`A+h1t@3Y)6j{Fl_Y807(gL0 zU@!LlLd8yaNzpQ!s_uw*NZERtFIf|IF^(TN*LIzw6P4l_uD|P~@Nh5EGLAO_)Q3X_ znylN)cm3DRe&Df8D^I|nL*$yF`KFG-y9MU19ugQy!~4Gba;Nk-L(-nEAI>wItncWQ zflTG&Thbo!&+3h@sfrJfz?}U);ArB$@j0nC3k1;(*+AI8FWApIeyB5fUlPgRF#Oe5 z?)U|$>e8(~nYbX@+u-Z{_kLAKBu-({#=+P{VYkZ5AUkh|%FBaD+5Ri!_0jN4l-ZZ} zO*l=?zkJot`LBX3Ct}s~ebXr$#bXz&z!#_S>^I0u^>dJR5Wv{?_G9AuN$Y5X+o|$a zXsgc22kNCOesQ7o>crvua$+UUoJvL~hcakB!R}tmaPe1RIo_S}&f;s4ovTvQp-UFL zi45zj(j1fg@k)i=X3=t$yh7T;B@HXxYD16l9I@cH=Qq2oY;b~Du^rgnfKz@8@Ug;@ z-;!=#ltVUq@|H)w_^$mOH*^p|4q1Y0QC~UC{oA|Hraa7$2MxMsRc=LmcCnY%Z)+5p z#xunOyupKJjA2V{$1J}gqPkm1P7p0DZMkNRc6@JPJyl7GKRfUIMZLH(Y4}nuGO={R)UbOSxsZnbJT53oLd%Hz(CigX>{(?H zSsJG(ElkotPydYyu0V`c+$;>%;IwZs_!){^VVf_f&D3K1k`@u0AD|7CYRz=JpD){R z-A|E;J2}1m(q~@2f405|?7drlTFdmG<~;04eYq4NC50DOd6vnkD-^Z#NrdW4c_HKI z_ipUdRUThgeI9#4zHW6D;_qmxQ(e99k#}QlnDSyugm?1)+Uv&9Cw8#MzAqlOZD8%B zwx;N)tgt+t8=DP<7F!GmbTPZFXRz*OWf;9|xv+cFw!K$0-*cGwa|t++49g6+46Ky2 zr!NzyHqzeFm5#6MYmF0sDwL_EoMF#T{!MqmJaxOAiB@Y48ITfT+r7;wN&S+;;2&B@ z{x!*UV&F*g)5KWwWpRqrY*4$g^XIzr!;_QWR#roGH3XgX$m?;ypS1i;wPnitXtDfL z>lv)xB$SjVaHrqQNM?6}a(9BxUq-{lj2DU+2W15I^uC`T9BVJjMb8rVXg0nb7Ry&B zr8SA#CnqZ}XOXsLKa9#b|5=`J>k{zF+5I_XoVfbCds?pc=ybR2+q%}-(Mk45n&_Rr zS*LoqP0 zT%o=9geQ=>qZhB6e4@RVmS0{VxK+_TCo3-!SEO4+dNOfs(u7nyBQNnsDKE->z&&cE2|9s2r6Wl90fmO8)g}6zE%rfM808+;9ekDC@ z+2k|kH|^gshOC8N_-+|6zb>PYR6}wI?hG&8*aK{bS&jLvhJPFDSq-)rFIfzpbc4&# zRDDfb$ddekO>&{2&5(mrNFsu0%TB&XJ?f=2#?433lMoxM{2Neqbdt{|?6!+=Jsr%& z9q10VdmV<|dh?B|61956lADaFdYJn#%-0ctZAZVH-YcWm$D-0V>g#s z)(m0mthREc!SeXzEaBO?kX6Y;&+QrLrIR6g3em+uOoEEChX8n;0E~ZxtYIR*ixEuua_NJL1O|*wvc$V#HU=y_c(AygtV8K7-4@lI>HG_jm(- zB(+T8{!S2SC}HctMHJK0-23@6uQj6&>`TC9W`NIPqR`crgOF${h6A^&FVr&|3;_YC ziU{HJ=75x< zJw*=aki1$kJpYTlz=&vbge<7>TMed14fqM2Tje+IV??}o1$Z#z)bG6ZyNYaY7p~oy z2&QMhKJ9_Awhi~dXxkHe^`bEMX~5tP5kyIVbP_Z=u~kRZ-a@Klv;DGH&x+$evGrGJ zO%FYa+YPO%940+|@C$l(CuyG7=J&1o{LEKZ{#}sB;ARPoYC`y)KIBer>03mX%m9qO ztej|fHC0Y!ZuTiEW=vVKJV?-BQj!4A3Y=CEtWL)3U+2m14>VEP=0DFAoszI}|$ ze>kKo(|@26;DO6BF=8m&-|6^lkGPhd;DQ#Cy9}Xu$`8wq1W|ki7rJjX8LbJx*Xq=J zqXv}uxRAk+Tu0^;qdi@Po<716TzRW zVU+ResU@r0JCHxgl(3T z-Zn^pYw;+^Ve|E!>T(Bb^1i&S=!}3D9doX6R^RsyZHoQT`R<(#0MM$}@0gFZf(Yc| z!p9Mdm}1}o-Ao8)(+31m9>P|EG8gj6NxzX5bG@^iYjsYRvG@4tNC#s@twMEoj5NYGzM-(!mQl3JSd}@ z7zHy6yDCDR-+*A?);up}YsFYV)Z~!H;E6l@Z5wi*5=@t?fkMgC+W~@z_H~ptf|na< zc2X9}5%=!sMH_Wal)o2v)TkvPpT5D*iWAGh*ioXrGr<3IZ^@NE3yq7PUcaaj>{&II z6n?(CePH?=Wd2=LZ2j8>$1b0Sao+7C>fWA$c=ChP4=I?@yWyNT8dILp|k29H42iRv+ zo$-eTMc!EJG9G+0y3ynx{X%VJ@P4Rz@Xr_&;lxbQ2&UU&%veb+V)w)y@)mW)^h&0d z%j3{JmsFe2N{f-G^R_nDHg;Bbj_dAJ-D#(5Pa?uiuz^S6MEAR~KKy8aBXfHrmDvH* zB@V`_Ty2DL&qBwiz_kFWc{{N_;C7WlxCJM4Jp=CBg!hDN_Z5KJnlAa z78^~4hqkv3qMXOm)h}KBEE(mK_xTX`w)ln@ZI5^D9S)6yjckDdBCfYrdcdc@EXhx-8;V}b0?o`KL`yYNg-EH~}r7`*@81~GWEGBa` z>gX4g9#YHui-JAQoiXOiI_0 zXxOH{oABbq^5%r;Aj8PM=zH(@>40xvtn5+oft99jk7X|@l+gcOApj&L2Z(sapTaY; zuX@g^y^gKg;D`i1BnLTtOFvN(xFm~=`WSyjgx}L0mY3qOMs+oE$+iE`#F=m) z`_4XUCr~Em$&vZwZTTip0%xlx+=Z$;-Fa!1=ZhoPP3+gwwm1F@W-nD`o(T# zA|*2qh8Xx6nD>WRBSYg&sS1>Edt=a%H}A_iKW?G$Ggp@@0MUf#&-DZ6fIpG(he|sa z;%8ajLfPIV%9mHt$oz>%Uu~mJRqWmzaAOAO9&@UV{T+V{&>M6&!+M}4itAMwG;tt! z{sRB>z$etQs5br98u#cia}8-`39lJq-?;dOv7Jv7^bpV&@BPKrIA4g9r|kMhjre)_ zlb^9_7=^@j5Zf5Xfs0aDTY10BIOgNt6H0P9M~X@L2$pJDM_$$^NVU?;bk|{EzWsoU z+#mpy0Tt7njT8cjCg1a^n~dvDs%-Cw55`^8kl-uM+$nKuj{k+wou znE04n?Q}^RCvQ)c!hk&ZgP`1<+Z&NgCAV^cH@5PQo}SD|K&}3yG>CoQZ=e*ucb^&%lW0!(_QFT^Ni}YWC+_MR+=co8<&QYVcS<>sEcGjrTKU!kG zCd48x2hnc%PGI^wBXx?0yrk6JEM~~wli_E?r2VXujU493ro3bBIB@zL*oMjS?Nicj z7sV~|140_bzp>Roa1DhKt_1Q166G`kmGtK`7q&Y_m!p;LKYy_Uo0Rq^r6aFqJGG0kdSqWe0kfN8?kIgu z&C`nO&CZ3MGm-=T^3zP8JG$0)r^kTtr^?<>d-y(+m*2^+Z@0+EuXI0|?6!-g$Rzz{ zeopFsFW<$rd+gpn`74tkcKZ~9(!KxKLaSYVzq+rOUsi z=O}-Q1J!Wio`k|)ZC%az=gH?OH;jS=LXP62J{1^ruV%;7xZqIYCUAFOP5+VCEaTx- zQva)WPue1VW|}Zd{Hat$dl2i4fRRow(Ab}5)5Rs>X=f|=F3}(5i3T`z!C*&~rov1Z z-ClP;(191%X3ydHoI5+aQuY%QRr+&!l*J|HNeVj8AMzvAVGyjwUsI``ZV|50javAf z@3u;}-qTUy;foyP$T|xP^Y6ELR=$Y2RTfV9ctAzxkA<$2k)dSBX{emNS;gyVdv3=^ zR{plXmR7EAEZX-_&gBA}wuct>d0T1<-4wVF{!}6!L-9(fFX$>0n9OsuScO_sVRx0( z+~$KLTm51#Tdo%@nTgAtNM_dD>3RiuO$RKkmC~LNwT3fto8Uc$i&;}16z_Ln5t<-l zHJtx8(=HyygX~KyvvAc}fee%ckd7 zG?;lyY|)$4oiV17Dnj(yteq;gs}>$BzrRtE#x6j@Bx(qnBei3!TUZZ~8+d6a3-`KQ ze*M|l+?C)zL*(V-({|XIoCNzPjbk{CYTeFxcDS&dT*paHR>GDRex=r8zlipT6+O^= z93c0E@5nM*C6qr8UkW+hE8{T|p9T{oV=nT+4 z*5V;d^prNIGA0p2foBvnq~0OYZRf_19Wg9ZJ>S~;I9qlMVHzz@GxqmosHND*14clH zE5Gb1_u+SLt7g;!j6Z#q4is^a{%jF%AG=A#n>gXmaW^rzKwLXkA&~dGrqh(F(cH&3tf4aWl@CQIhTEh-C2`GkylNh7Q zextfLR(MEFYDBRz)atw(0m!*__=oX-bcfqkkfimA`lWDfUl5);j*qyX7KLAW%^NFa z>&&kACwqYswk<`2mYV3!VyfkwIlOZr_5$^Nu9TtKk%`L}qsE%Yju}fXzA2{D95y-4 zPo{9K%?%yddbusixgY{_rxhHj4L6=_4Skaux7=T=9+B2sizQxt(V}+v_?}!cnJh#j zjZpbZeF~x$TRY2HDyg}r>U}L|u}F(t2?8ai?V7+@VnpbMEJ>T)!s;1SvEhz|t!U^^ z=S$y5JHy|6!&ei^4YN0m%ZVx*gEjWw!#q7cHG(|8m^VUcj#uA3B$s7Pk6M`DlbUTd zVsW#Z(_S(`4#NEddasvwFrMvmYDV;+^=a@ zYaakXhOHIz$(GFN0MSb#!?*bhlfYM$^>|1?L&*>8@mT@0G{(a5O)o88tX&aOlY|Dj zkpRssJiXZEOaots0G5*?pIFzp2Q~1X90$JoLBm2!2SZ+m7R;;OnyCxp9LsW9c8ZJe zp{TGl$fGESO({V+oN#8$$0pqI%}NNX<6-yl7sDza-MJSVz#Xli*i!wW#*g7B;F|lBFu~l zc2P`@fbZYa4hg&YXxoym+%DGtgmJSy0C5A(Mmn|6cn-k) zCn1AQY4(vP`{{+AEu}b@Jw%dR$08R-+rkJM+7lDj9n7fO$l;l_04w)Q%pD6#BCsfe z%1T#|sO5d{=-vGx8vbrK+-F@d6G~sOZq8>K-f8HaTLoIW{Qi(IMzK{~?2ahv2EAbV zozf4mwG0T?@#aK?YVtErAW{)Hl2G7@V$s(7=g|Joa@^f7f>6tF)PL;etrsK9RY&K3 zk=eXuqTlp$(!~_ZH~TRwZy$?Ifr9VQ@^QmR7}G-!IQf0nl8Dy=jZB&Ue}p#`mCq z8af-YXIY6VV%3KaO~ffWXf*%RzQdNJgrJcC?8)zI zAHnDSUh!7Wt#A%P7S1rJPFR1io!sZpa7$^(?kB}k&Q=+d)5%WKQl}JQ^l%R1Vpg<}-LJlv)`4#ER4XkmZd3P2pRu04ghC}w(b~ouTZ~-% zQAM4L=7@RziJPz=G!hok=oB9Rv&h8U2c@G*-)UoB?y4eyp2RBkmLI_teK_kDrq{Xz;N}@;!=(aP-Vm zly8qXxrJm~@c(YD@fMF20J+NLRIQZh5)Q&eYkpW8YSGvh32Gna{f~uE$jJdX|3}$d zK*bS!?}A8hhv4oG!JP>ZEV#Re5ZnU!9pR4(0 zX68%7@~%wAd-}}cG#*&{P7J*+%0-uAjEc3*t&w$ydu-+EQQ*2d}Vh_6%Wkzmy~A9fX~h*>iUW8 zOxik+C(kf0gG0ifHo?B}Y|b*G>v z3#XVu-Y97S0jt>M5$)BJ{m?ry4#T|XpaiiLzMqcyEmoiDi#`y971pZtKCY(H#A8!7 z@!i~QzO3F*xfBVO4sK1$xgQGiw8&JBL~kVaP8uk!I81*0@kz_{gRxd^N3CYQdckiG zmbL>a!Qu?e1(CodA#0#pk47KI%caFs(&VoCb`K_^q@s8*!oW6tHLakfU0HL zl!S<^5$}(9z3tY3leYZ#=ng`cEXRt(e4qjYesc1IcT8>>Ns+HaSv;v2f2cG{bty;7 zq7Q3UD4Fu=?*roJHYB; zyyc=-sub?iPH3$+;a*TD|Ngl3!XtP7M6ndaEP_G1&_OY5Emp72IIf0~%-Bo2zq6uA ztmUagKoHFBu(Eom+Yo0(3%0&gBMO)(_QtyNRAs}gNjtt3`u$nUSWTJ6+h1k1=fRT1 zRPeXpt_cQ6p%%y>`<$rM7@dV;(?N86;xX$PdgZ4d``+MM^3>Rk**@YolWu!$*(DS9 z{El`MZQT2_+M8$Q9}2IvapRhe=N0TgMw|~nLKESvAse+NNG-l+4$TsVV-TTxkm+xv z6*4WkbjCulU-b6=E2hOs1K_^zy;lo4vU-AA6DdYQh=B;qWRvdkiuXXs%k4kLVaE#4 zl5*GVuZ`pnG=~Yve;Pr~=U1BT!TO(4eN}c!rqd5^gyq=Fjb|;LscUap}y+Fox z@fdAhinHb1r&pJqHD8L4b0D2(QA{(!^+A6!A1nhwx9_HHC94)&W6j)k#k^-jF6vvS zrmBkvJ_F)4{26}s`R#A?2-RJUrQRf7lTALm=8zy}?6ad~RksLLYqIthFD$2yKcO3` zo(oBPgZElaXf2?xuWqL(iQwZd`m}uFvt*mI=!J3S+tAU!q_#;n%vE@vZDo?e)`Dh* z_$$pad`^xnZwx-~kd#JS%sI!&nsYz>O!mR}BUg_K1cLTI(s!iz-it>`k!g2 z{QaDS^@LQsJw3g>giVa`&@ev@wHa0Bb*4o=RsL`3|35rd|Go0#2E+fkzK5$3nf zZeI9R97FT^T1G47s~);U)AQO(DOC%;JNyIE#3r4Rcc9{f8$Jei`^v^8?diATpVJUb z6F=%9qd}eh#A+jgey2Kj)vP%*)h09=+`nUDU7QbC9aZEs6Me!Iu`-|SR=yrFP;Ru1 z33s&rmpq44vc=-z^L{bSw^hvt$Rk%n4iEw*?w}|2H133Zgt9t3E7^KKSfzAtogyxO zNmj2UoOvC|ew@=@U!}#kiEp*#b=#f>wQMpacXu%Egt0cH#&A&Gwbr%SLsvtpNk2l#DtmUJW>s{HS9sWj z#TqZ_(j;&!3>hsl1)pn-HFf-GihEIj6m#(-4qOYG^KLo4Kj~Q8Y(bFq3Zy?7E_xEA z@{97pN|yC5pFg4lR`gLSehB@Nstz3afYol&rG&A{wGJF(AgdySaDyu%A(SvC^myFQ z!t3D}#MH1G3`Lf89&`e%F#WDc3`LIhUJOOHbteo(A^WsQJzV&=$Uck|YMP$^^YZzoeA}bpksFjmw`y;LNEp^qiA&sbFVH%VA`g)IpmwJiqE{3o6-IB zn5WDJfmro-uOmR+%9#Sh@x6sz$J(uKF>)hpPeU>2mu0B%!wEH8p=bBYnEPLWn9yfY zwp;loUzZb%8{dmds=ia3wBdR89sRg?ub7JQSh?ogLC@QNG~9YuA_Dup(zh~2ZA$-W z^ok9gvH9QX%`}{l3knr(^nd=s_*GI)NkmS~xe<8bKC2c;`Q~cC!1|F_QOg%o$5VDj z(k4--V)%9TESI5;MR_ZoQdyGXGn-`6nLuLkR>w-Aitn}WdAumygMaKi^X1$9#=!Dj z`b~`AE6sfNBtJr9f5NAkQJLvgX&U>435&bVVlRFx2&Sf)NG$nk`U#Y zA9#iX7#n5dzr?#RkLjRu07rGZ?U;JJosp%B8}fJCl3^u#N^Wm3@hOfXhmXK}lK!n_ zGc+o?LCt69$IOU$O~Ha`WSY5(T~ev$6GSm8XHMek?7Y~s)Gnj|M%e<>*IgzjL|w+pDplhGSUT(}nW zUi5RVejuPGynK=$BlJ^c%&DVk-_VQd-B!u&A6E+?M#{16?Qa*aGMtUx)Y%Y3V8KB- zsuD1jArqTH5rN`@D6m_&#hFw8Hh0nK)=rk#t?wo2F&J_6E2ck}BP$QJa!UXM;r!k)Sn#iS(NgY}@!Rc! z{GYWp{oMu5mjT(n0WP%sawF2X6C_O42Dv4Gemi0fY}`XOIUG|q8>E&i{#q+ zes;*}b5Z^%*uUmjXM7G!qk$G%x-TDqCnX>4XDyT%hs7lPUudw_$I(f|lT*&oeM~L( zCnKVvBP5(*OJkMEbC7eF;wlFJKsWl{D}Pv6;;H*AL9xYm#vsEvk?~D5=Y3)c$wdGS znS~oCyKAh_?$y6YBMx;wDGSOWn>~Trrb@=j?4^wGhoMBG#h-;6U-{xG)`;5q0j|?u zNA7BodcoA^o`s1q_1@fMa%RN;9x~-)#nqnJx*%@M7;cbX&WK%PB%Fy!?=0)7V<(2J z1hxg!kDRPN?$-@`CT=q&w8}x|@LYW$LyD!9OkS6B@xy%lel3J0eXwj2dU?I(Kr`Hu zX>Oc_ogLMP-9yFoCE!b|;_;ti1t4uziie;+>xd){s@Ki+&(c*b zK5y28%}WUyUc&6oSJ{H&eQhmqEQM$L_Py=-{S!r>I4*;Rbxot`pdeMQG@6S?-kFKU zdn(n2G*p3R*|j`b=tQf!JI1|Qaa8y!dr0bzf~k7w$h&=xM{OdB>Eln~o4{b#ND$wq zTl!Wa^`I{qG$5|fX>6VhIg+^ijBR#`IC)g}Mj~gCU0dkKnNrD%Jy<{4^3o*(yET()R{VF% zC$c7?kNemp3N*H}c9%Xs=AU0_zB^dwo+emOd7wn_QsZ-De5*`!dvS?oY*9l|Hh+W) zU4EXZp37*j^l41vU!`)7&oNDmN7fa?49dCemxlCNLC-?e_k7hEarKlmTQ{DtU!xrupI9E-Jdm8~q6f7i5RBac3Qyr$p557(Sm zULFe18n8!=JN@$Q{;R@fNx#+OI(-=iKyY!@5Y-I#NuH{ls%Kdd%+MjHTKpl=n|sbO-|D`HKv9R|jV#FP%<` zAL`}zBR9PdZz|EO;1}+ky@;M} z=LqO0FF)w>zpgvfI5t;lD!=YI527Ck%k1q+`Q~$639i52ttYJ;Y6}sk>pT{13_q89 zYBY-jw_hbs!8MZf2WOV`B|OX}Pb&{$PK41;FK*klZ2+n7qd~@*scH(XvSBsduWD*f zR_)i{kaFp`!GTHr(O=o%Ki2j4yQ0v(5{MV+(57Md0ss{AO3ppCvQK1mik4{mF=?ms z0n?hBHra?Al8x%pb4*bXHCYVEcjje{iF*^o zYqK^jn40+Z(Ee87``P=JTyV-c?g($-b@ZQ4xqP8rGM-OK_d|cr(@W%i(PO#FDE)^+ z)uHIj{A3d-C_j6+!ISL$rGEM%#_g6RCnubGbeQr|<-N$p5_YH4jN#Lj_wBBS!E*!} z!)-BtJVeA58Z`?2I%#8$svOFqgW|Sa(;gEYBZbpP&GSHm&SJXE7rAEi@Sc_wM135^ zk6dGP3b=tTo2J#&PA~MChz1ko>RYqrg%f-kEcFE7-TSMyX55X@u;((L>KA_&%lHPz z)T}6EUu)?g_E>ZODJOMK{~9UflQ1CoHT~jt?4^#Jz@1@+9XurafF?ORDQttorjJCj zFYU)u>E7AZp^6&)5237L5Q`@kGKG8oxeKa5RmQ`?W~5c4hu5eX+24W>%bjVfylBcAOq`08;oZTYUZ_=JGN~Z{UDO&w`Y|gH zaGwsYI?xKX5w6((_toIkU5fovc&a)f<++uH?X)A7)!k1`xp7H`3=>v;qI64 zR*GjKh_eSUgr87n&%D=VQaC|QC`Zz;V|&Tj?JXmp?wl^Ol|0F7g^ zlJd{`dQCjjrkXKEHM^nH1y$!&9rQU7WX`WrwTR^2y%Vn*2QJEOpB_meQ@F zr+y)fQzc&5T>VBqIQJ^0lY}UT2)rPQToh>SRztwD24UJpeSd>Xvwu=_KOuh-iqX>O zv)R0UEBhA_a@uB^S?M6a5fuYfF_%!`eDT z#v$^7-aMa9ul)0e0oV`g97|QGhTiRsq~5ykm#$#ocbZ-|DZS1!q)uLNx^K4kZv_O; z{YgJXUHa!F)#3^~Nbi!}i1nSsF3ZKnh9k7UiTIGsH@}93;HHW@|wkLSLSc`t2Csxf!EZ2Eo zk;Iin)jR*sbtey6dKCPyyW{LnWkO;b{bk2(4YPp{TUT18yDyg zSy|`A!(6d%iS^9=%L66J^-=2C%++`8hPd>w5uA+1c2F&U#?zsy9*Q+35E_XsrnDQ1 zJvO~`>0#w>eEmF-uy8E0$q{CZee|*WD=jRX5UGHPjBb|0%m&zJU7jm!><~)FHgjPL zu||AC_jfuQBQ$4D(h{`~53!ju`t$0M_RrTH-%2cVSH3@N=%Leli{ht|)g z-YZeJd-NLZ*Db%#`{$5Mv|HXwOdnB%jpJ#agI(d-%tF*7i74T3f10|~)>9vB{g2Xg zV6)7)GIhY_I}GrY?<~Y0I|~-TvTfXq=dD{#amjGVVU*(RHIQS{h|a=;za{t3>e4Hy z8;lZ`frjJE4wCLgUpSjDW4R2~)e_k1#0s}9pPR6aYJZy$=4R`nzQpO7WioVQJ`WHc z&-zA59>LQ64o~TJBIK1$egcl>rioV3Oa6AJKD&=?TxCGIL?^*oT6Yq7NS_zFn^33G zbE^p}jGfVh<;Dtgb&DjV47+sS8GrWf)-!VGzyfK;fdvC>bb_0reh5!Px)n>n7{zN$>>vlyxa zQGvO;_W|=mA!X-@k5BB&>hd2!!%X1`> zFPl4JTdT;8Xo6^>WME9FMzLNPVy)EC0 z0Ld^#`qerIRmm{7Ua_Vw+l{-XE=_u`AeAH{tNX5tv*2$e0*M2Qim(WIWDUu5|5q@{ zQNln*2UI$Q=v{AFu=ON8-?4OsltA5PF2mz%SwU|$DThH17+xZAe=9;u)9VdTE|_VFpwgTv1- zEqBnHPemLPcG5q83&$1&mk0~IdZ83MWMUsouuVjsp38 z3hTE_vCS~9lczZX2@0A|FvFH6FPx?KYBdxHhX`%(U*T@_Vo)sjQn%ZVyo4R$4qGEX6)4aqc7XeyLlBb z&#@^=9VWQRMV)UyVKRPlZw*VJAZae{cJ0`UR=c*&70+CE;oQt8{9N>r9uQrBc-0y> zSHD~SNA5MNUUm|9=)#K!Qu>&P!yN&8JAJ>f+XDR} zEtC}|I&p%ps3$wZNkc$w0~onu)fIwgCLu=7`|JM-XuyO@J|8$yp2?Oq>axo%;i7nF zNuBFtb;2`&%3cg!$DPbdspak0w??#|SG=(Hw%@P4+z=zd)9IjX|6+F~NB+SkJ=~X^ zNQ^T<&4luIy*DvM?$&DuOJUBv(Wajy$`G^%=WMAwwE`wo1li+=4Oq-v%(fezSoF7* zWVTxfywqCtCt7?7N~G$@TeH6s)LZAJ=buWgDC+Rkf4!eNEU43Y49v^jz$<^qMd^v+ z5wdDyV-apRc`V?o_AF~ElQVOY^mAZ8<6<=#ekYdO@@Z1@b$&|%`br|U0IvB_so??p z(%mAZ;8NMuq?^U>EFiU(vh8G8{aC%)X6|!sS(}?@8S{01=y-1ENy~0dZeH>2!2Rv3 zldVu7^`dpx_f*EEZdE8Rq{Alou#xj^&$*&Q@^(l|bWz>hSFe*Ifzyu6ZibObEM2mAT?X;m+J>~oS3)WpANZS=Zw+&ouj zO)~$HGyx;5dZ1=Nis-C?g}ZQU*)iAGxuKaEZ=`!A+r(gYu10QQm6|qGT%{%QgvI;C z{*qy4aDyvbp{984kjfgWAC3&`W_{-=(js}tevTTo7|>sd4d#3RY9?Y7zbZ02Qnzrt z&DG(cyvZunTiI1Fn!Beeo*=gCsx$DMuk>8(%&TUN017!Wk|J*EPM6+hRGd62Ax?S9 z4SGELll-7r+h?XBgtENx@V+4ID5?lTEf2VeYwb8RjQ&j#iZDDCm1{?EJWn4K_Z@Rn zU~f&{uF8d|e@Gm#yes7XKH>N_HoPfwD(3Pa^oVa{b$5u~qdYt_Ts`PtZH=*vKez0B z=KAv;MSx3cCzGNkX6Lx1UFiVj36^RJ%HUn#am6@ND-jD0>x(ZMWvA_M?2KlhZ1rSc7s;r^1X}L5UiombIg)ZV3+LM+lYKqJ{%kC`qb)jt8Sl^LB7pN>V1Gsyo%Z*>6^pQP7C*ZD1y8#i~YqWWjbbT znv2`xv%n$kh65(HzzSB?Lt?FB^`fCy3F`!$2b;!Y5o@Wd1w&GKi=S__hjwbUwdE|L zhfn0U@>jlIM=HFWIdTR#QfbSiUaEPH(7&nf%i8h$`V~B~9z!mD{>&z-&f=9s5$slJY(AWCVpf*6v&WXpwL;c}v<^<`!z0%yD-gAb24C$!Vb}){fU3IQB?g zZ^7)Rzj(b1Wn5C!VJ@rx-7pm#`7Z_8bC~wk5kMGhb)@iWA&T8dVkv)=&S-E3NaZ|mvDiOuGNjKv%z5p$7%l?wx4p(e!e)x}Iu zCS-3xeWGEnC9`m%*af5je4m{G=KuP10!o`dW(XcAooAetfM>;-UjvBmsDHT{07mBA zVZJlUy=;M>n|_WxDmISfnaR(@P~UGw#^15*u=T0x6>g$dqoA^MB9o0#23C}lzZ>e zY>h>`L`|`jhrc3L@md?0WzDE1XbI~-Hc;ZSEs(Yy@ohh!VWawbx1ZzONq;9T6k4fh zE8=x9=lrjXtZYP($0l2BY-%%#Fa%GE3 ziIzQ$&LcQ%k1F9!UbVSBN$g?sMiY6}uxe%LU2Y;Mv^za}CQ}a76wJ>OGrZ6HSyR%Z zb~>qiv$(<5t5(p5J?=ze!*+!O7D!z=DPhZ0Cwk4adFK#;KG}i;6o(3<-hu~AvRD6B z#kWmb=%x9S+-LvWfQn9k|7+tM9GzEHEm!cCwpiOnZ1uejUM0Ulcxo7jWGuEcH$`2G zJg)g2;tS_9+;Mh*;xl4UJAzb0B%v%L;g{rX)c5C67T|qt?&;ZowR3y)>4Q#dt+DcJ znEQo3_*eqpE57vEnzu2XB)vD9Omec6mojae7H2u2%bGfc&i$m7w$^IG2XMNyG zK=u(|T5;Yb6JIU_=gl$u6t8hkG6xi|u}`k8KQG!A1_K3{4@wCU8JG_`%lIv~M7K1_p{QGN+1tz|!y z<^if)0IA~Df-==a@N*&|L~s|W2uV~E8ZwjvS;c`wF~T7H>L>^X!aE%fPJq0105gL40Qr z?G4BWuUwW!u_k>~=tZ@>p7cc7o#R;nBr`v-#DS`pjwf>ffGhhUTUdnq0fQ2GWYJ$Z z^EcEcd~;2mYLgiq5oGgX{5}qzgaki9fRJlj!06rCJfN|Ga_)k7TX-Su&=8=@!WH1_ zAHXMZ8P&bGj1VrwSO(bNqQ=NkHyFr_tCrg}0{Gfp=L(SY2Q~fu{QR^cBXFEbddj8y ziImIJmHX+grc}b2G{dO9-aGx5#R0E~#ok)p`L_J(eO#rud zUrjau+GL<}9{7svgD0YjNZ_Cs4;NQKd4Et`U68ZQ9idQEw=e<)j*JkTY04siqA*ge z0i$?{{3Fs$0OJFSTO4^L!#XP^vuQ_w6;zjuj8p z5D9Fm?@ykU2|!Uae@Uvx1px%`qz7U`j{Kbto72g-KP^S(So{=3G@;-1%De{##6#dD zT0P*GJt*;Y6w4K$n+;>eyiDDuK^m|i%Dh71ZJbb0mBz{1n&$Yz@Pu<@gu9Cu)Xso| z1jyk)eBnUEqoeXFQ32VoAZ?tJB5;OLAk~9b1?B?|UBop5Q#=4sCr5^&qkhZWJ+>-X zsO}!}asyTExM9aoL2QV!!MMTQRW-H?v-XcY5T;ySd<|BlKu^08eM4&HgZBhk*eY)t$4X4Ad__ z6JCT3XI|9;#zSN2^|NQ|F-Ityk-^Gu#|m=0`Om><2Hx4Fe!#HPZZY4?HWF%FlgLbZNOjJf!g3VG$F+rr)3f!-++;3X}4F15y|VficC&lev^;1fLleR?xU)o3@it0 zd(JLZN4z8b1k9}`c_4-{WKeoBc@LVV^=E}8^@hP}tl4&s8Ec+C4Mjs65-y3$<3O%a zUHby+mBPq_kx$szb%BCjD0STh2n|KirDC*==>IM){~DFuF>uG*1riO;O3n${3hU^0 z_dwW2LS8@m8C`YH((PS7;%<=hEa)DK5Dg`{>~%edE8@V`FcD9>s40g+sJm_+GY=Zb zzcpV9J&e5zLI8VYb~Y5)%|QbYiH`Zm*Qlb7_*Sw{D5|a?s?Brn>w)rt#m{QUkn43d2T`1thQ6eZD+cnG)+=4R7b5A>3_`<`{59AVFy7AknQn&+~?t zKEb0%F@lL(PV>$_E+B^$h|@vllDDimp@DCPLY}T$p&_4kfw17|xq#m)ChQ&^$=8J< zgtb%*hQOX$@8)d|fc^^UQL=zB5h9Vy*zi+y+u$Gh6PWN8Gzi1`5;~GUGG`Ry#3<74 zL(4a-6~e*z|6F1g|a{>y^JZ zq83LZeM{uWgli-|)sI_mxka*Au=G+O2V6dsyo3%ok$PJDx`wado9NsGRgJj`CuWkB zP|p0I(^}7pG8ZwY)O*o$_Ew>KfURG)^K}3xRmy;*8?V_yB6b-DfIZ|wi4+i)QY138 zbqx~&Bov2c+%_Aq0n;ZR_4*{DwzsoN(C1!N^ESZ)f62|j3@TUzWI?8nBeVP z8H6FjdKCMt`LuUqrHN?QPo@&29os;o>2iMH+!yd|09C%AgL7_XLw z9l}N`ClwgxG~D&0982ie^cUXN|3&)P~?@WQsWxCC+lX%~M zlHJeKV`AJ4waO^MD@RJb^>HxL%m1eDXZ3quP3nK6&m=wgS)%dY;QMH~WyY-qs8s0W z9O&X~H)IqU$Nq7e+V^#DCrf-v$+noGEaPXlidB+8krhlC@zTm6U6srW9afu(iMLv` zIK-coiStb7<9=AmC6Hx4oh0Ic=%as#ood~r*=bEn7RP-t74k!Nb%Cp?LrU>p$J-BB zvgOno`}!#{nxoKT{?&I_l@T4INva8ZgM8*}tw2R~x}7mm->b9r3_kJI0b+~NlqJmi zppjs=pfa|p{JJMYi9mn;2sV39W&6O5b*&_ZgydGuD*xi4HY|tn0 zxU=o~XNe?D{`-|S&T3UUGS%~=n#a`(SYzypHQYl#SFAD z7R-A2P2YJGu~<8~>ZsmujDMw+7b&1byl!_pW9YCP!|;A)wSEvY7F9aWSgK{HgYHih>aN0q;JC{|+;NYDTG zTWRxliO=~v{BKRv+xcTNbF)>t3Vh?|w@i>1-^dfp)mX`MAC-Q3Nm4YFXy%0M)F!a5 zHepvjK_1Tye*C=&mM$bFCVqYDYsHm%rKzFBX-`ORm!rvs83U%nAFYXp9JL6jv+s|HlWLixsqjCS@;P_GJSUL8_4YgWz#a}zLb~T8xO59U zCVQ@L7fcSNZ!vTg$Igx!AbFcsGDwwf#k}u=rMsW&bQ7Cr9)P|Ygi^WC^5F=o`httnW3HAFzjy5j651Pp`X!VZv=wY zZ_r4=+-SEd(eimscjPQ6K2mU#OlQVs-0gYr*l^Qb*+_$pzf8y~xM;I|t&d@M>O!>3 zJ7+zotO6UJOc1d!+C>B~K(aDWOaqXyvkEjM-9V5p!YO5~|IDyydE7Ath~G^1YIz8J zEXR=#4qZs2$cHAhdn_!!dw+th>k(<>4Fh-^E}7DVE{_jGkMzcZ+eV&Z!l%$cgs?RX zgi53yZ{~$zBtW@NjnSUG*>-#RWD5ta6SK~rgayL94tW9`wd281Si#*-TOMT4K;DVV zx(ZzobeL@yk_c@LbN<#P5`qSkLceE5L$If8mJaMdsdOZ6kMj|(k5iRWe{|cig_f8G zqmeT?nbm&Zk#HgyYvdX+tWBmfeiMYTRfGwbLqidCxxCv76B)P-tHy+Hqv@ev2caQw z&``AY0E#mbLIgu$9#QLLT->5T(AF8Jwjv}VNzlUh?NPRoAaqzS+T+3@0)Mc`kYjfj zy&ZksJ!vcA9_>~T4MMS=ih*G1vcrVqb}gjSTHT?Av33cg1=DwZMS~Ep%b`awcL5l% z95f_V7YMt#!hC!JyBPz<75O-IeB`FVfCK;4UJYnLQ@7pKMRr=qz%gp+JcIVX?u*cS z6ne_VW=fLucxq4&!|nj1X18!2bb$oEA~4uMDZ7Mrs}PNlFh+VkM=G+~dRSlZ4BYyT zT`=t4U!0<~*ZtWF1EB&^PB-B)kDp|!U zhe!|}te0W;5E_IY!LV*ywA{(Ot`+Vlr<0j9L1gN89(J4Hhd@sOGB#8F5X9q(q1bV$ zr@n|zl+A2Ub>cO_nGg4l=Fb8T#007E)(5BFk@-awbEc%D08I zOIW3~qo9s6uIxE938kuZK+H0q;S&laA)*{VviDSVKVOymJ~MtkWwFwGztS6$|N72s zU{v}mbJ6Ybg8z8iX7+ z9{s@5*rx1pE?;_fWm#h z&qLrFfLVH=19afC%>1HoQ%)T&J53uYd)k8uR)+?{x^~SyayH2qx{sv-ap$1Hfy%4* zW*7IO-v|BM>(@PY3wr3i{X(mPo4D~Hj7-5D9}v%^Cn~r;$f}m={)FwS78w3qVJy|H zfe}2eve=52Cyy6^o{LzJP#AvxF)vxf%jp)Lb4|RHuoT#R+#^TDb2uF|FYc5&!WIsR z8{levpn}Tu4BUCyLPj9B6MvtMjO~u*R`zZlcvrW}a0yY~it>9ao|=3>Q1RPs?=~eN zyre;?Gd&?459Ikpp-!?`P42Z#1A%_tQhmdD4=<^2J57PrWiTDb`PZ8E{oS8zsBdp3 zrr1Ee!^~$F{x2dF^B>v?$0Js8T|lSg4~UZa6Oo^G6)!qhhf!k_0M|L0=1D|~uHWuv zO}ndJ`d~#*2r?aO_jZIwdcugSTdGTI9f-|Il4b4+>GUTbzo$#~S(VakU$6nt@{bw( z8`_W@!2oC2&q_Nym-{N!2Y*--R8X$%a1Gz({`BCe7DP3E@yFMv)3&@jq)>WbIPc=m zwof5<_Tyy%s0Sy*D0&X#r0~3pj;_6W{9{Qd3WX80(1ti+vQEq3GMG(IDn6JZLNz$g z>IKkq+foUEiS!gbRn&sBO%`VLRJ|w&?*WkkUJ+mVuXTG)Nec;FNdztDM+fVDnGUbOz9nT7@?6tPbnk~sH}l#D;0mPu}(10g3k1JC6m$%Ywg-W5Y4vy20!3a$vI3 z_s0ZqmSAJcI#3(@;v0MZMME2`(-nG`-xO1Q_u6)wZ6f5VE=%g7ku+p{iMJH#VF2LC z!(`MSn?HW4cWE0=6&SRe6qnN@V|%C?5f8J3a<8`+US|z99YU`epuMrX00E!|_kfN- zkBZD<&G6O)5tT66$wK}fC$Z%~!`K@#MQgZI4?)~S49fr`a}No{%C)!>}cH#fhRmTzl%K;#jY!7;GGLa$WnRh37TC-yj$2K*KD zA`bl2mNN_jqwQEmMguTD=Ptjwq3vPiG^i1G-lNaW1Uq|(D_uRIc zvz@!ds=Fjz<1(iouh!m{Ih-C!tTqsgdOJ$Yjh&XhyQ+AgzA`C1w0JGMTy^2Wg9tjl zb<1vwDMB*0H^8EL*khYWS1C|yp-+2%pGBjHy&^2e4&&Fp0xQC-c8Q28pX*&&_z3JJ z{3YB}daYuyXRd2Z82;|w>ixr5BE2@8N#se$wN(j>3p(M%h+{_$#sx4}mmh5I z8JE`PI-TxE4*5{0_amF>L51*e&_KkSsIV_tkZT2%EzC8s*o)47TsRdR({y!tZe3Ps zb?!6qE>Z}HyLcF9AUQsal4ru6P3sP^+4xwwhPHd-Us}f`2z#J~?$Zd{N)5(dygRc0 zsp$3D{8W-`{K1lEM~^oD-pavq&f`>PO9p6s%rYAaRVlZf`V!~uw!YTCE}~)BRY*)E zAeH!b^)K6Jsf%SqWA#cAp3nHjDy#+;VY)hcVE{b-8h|c?_hDTR)Kw3!6$L1c?MUp1 zz*JYMy=L8@m^C~IA%uV_`6?i&SFYzY%BA}>j^sVb`_xF1M^yAgTxtwSod3$2B)o6X z2UHQ;1DK_`fCd~pmWtN*eXcJ&4WM_Yr4f?(6DP+>vZp2E>^`>Y1>Cc1$Ex8>IlA}& zSe=mxtQdb0JaZBXm0d-IEdq@FPB!#*so~WKW6&D&`1MCM##;Xg>f5uyi)Lec&pC#i z#20c&eRrK}_+ZjbRk$q_r{=Bh&He6&H@l#kcA3rxg2(Ql<-@e*__NRpr#;5pB?o=? z%rnv)EM;1KpGV5*1seFN54WC%A^-uU!p%0Ckj~R;@?(McP}K72T*%6K74oastkqJ% zY3kh4+f}fg)Am^Ku}a});QRrwS=56f^d{Y;2B)JyZ)AWAv<-gIIt39XXmWtvD8h;Ec?XVoZ92qNQYEBr(S1R16Ln^ zIZOWNdt*hOznMq03tuH5Sw=l(wIcFciyrE}<|T;sFOt_)2~`tBO8~ zSwYhIdfVZI;}z#rJx=R=uvr*2G>q?*9!BM}dWtKbMLjokYj}#YYZO8N4I8IA)jRv% zp11yJL22D&hr1gXd|D*GHgIB+G zhV$+@snZ@EL4KVtA;N^M^>(J-`Lo5`Vn#%;o!jMH;?#5v7hPR=TinV+E+m&iAmP+$ zdd~MX2x?@Wwwm!X?^R(W=Kp|E{s+-ab5*es_=rYse=HpTPe@ka5t6Lq;O_j|)x-I} z1DyW@a{fO+iH(Sd1_exEI-|NeBcqxo8bU911T=LFG=3NlVkLI}xx)8rZf+*`_j_91 zENvnl#@JyaK^ZXh^z!l&YN>+2^oR;mP>A~!tiF^#+WrrJ1)rcHSd0P-ixR*Q5>lWx zlN!)3BIS?!g(%%Jt{+1`Y&6ub+rjuRnD*1hQ@}<04@?@GE>58I3=NI6_5U|a`oAGW z{{xf$Px+tD4cNm(LmRas3NTj9?tC!X&(Y;xxkZ;nk1x!9%Pnw7BY#MU9;E%`{hSKU5-AfEuvV$2V0Je5#&O+p${GF-Ng2%6`tp7tO9 z7rxFqD6S`X@VHBYJAvSu0KwgzBv|kO!GjY#JT!#h?jGFT-Q7Y6L3VM6#liy1?(O$i zS9f*)Ty53N?3;e`YIgc{)z0*%n9@NTm*0Mye~*=xej zLj~+0T2?=y?Tumdg(~u2CL_9rJ&~a`aQYH#1ozxgR;$m(j78w-V|m!yz)HgbW5e&E zzxnn5{C~q^S`{=efaJuj&PX@CJ2sAQKNj(ndi;AGr!nSW zMK|wJB!X^DR!&nyyGKNg$u`Neov9RaWCcv4xQG}9C*!=rB~IpeN#fA0NWJrm{kuue z5>5jfgCdtrXdLq~$51lIbK91pPv()6Znwh_Dgg-Xyh!Fn&7yx34M#@|d%AyaIK}*; z08Wjnh6Z~xnlyn)kA}O%vJrKnSt!V^(o;?`uI$RnHzzFS+WtWOrBJ%1>dG~P#T>9} z7QQs>Y46BmJE41g`z*@ayeQWl52q~p!0WYkVv-40Bv#=s+P7Ek$*GKoR;L22i7nK- zmZw~-Z(2@6^R7Lr+CqGsF76wbv+Ii6zmUnP9DVzw9vRoDtKy(p6vKwPV6$safaO0!D`e}CS;4VC!Pr){M&Or@>7y`k_GddJ@bxi`x7ud0;*>Le`VUD}uekH1l8TgH zS%;r@7XRVC30*GOs5rDuY}Vnxk2`-Obx!%5&Gw^j|4k2p$Yz+qNl~YshEaR&=?W=( z%lDSKMONmsw{6r%zJmfs-3GD0uIS&WN5*-D~-7=)gs7^3~T@LtQz?2x3 zc$NOsUok`;CuP&v&LzVclmUN?nmWqvrzi1=-}$hBnY@%PsJNhU5L6t>D~0EMqhF6q zk2;z5tEvE5-+v{ib9uqRc7@C;&B$`!9KW|Dus&-0Jb&jO=)=j!vqYz6v!TC_Mr-jc zGB#(gek<24{&VWMPR=t{AMxQpRMibT{Zoxp0#|n-OQR(v zggHV)*txwkE4IxGiuIu%o%)X3Be}$Gfu0BQhWA%mFhMQU1-U$awW*@tO;e5uAp$Rb+xAtF) z>}Pa2X=Oz!d4_dP6Ej_T%lirV1#zg^wh%Qb4?;(_4SyoH~$7;N`q=yjl? z7tbfg%9R$(u@Yhy%K^5DlusHhv%QLVyrqtg{w+>_eQ0HW>n52gO@zxfzMDj=EEZ=t zTjoGITgbLXvZPMVLb!4*wd%N1dCOK%y=Y$+C+T_LIbgY1oa)b)m%kzt;zA|Bj18_U zEO-uiLl)Rv7pn2d)!tbb8vB*}^swrqj|FBbdZ(crvQ|Qu1E?hrxne7BZ#)V@$P81f z?H|dVmW%>Y$nE$W{vn;yt!uJd>^>y^!$LabnfRv~ygD*8!VDvo{+)mE%XCYe1ayBW zuyUVqL3bd*ejk^6@d=X4)0;fyqYMmv2^#W&qx+Ohyff|x|>E5~d*cd}NqO|yStIc&U3$cP!}%!_ehS2h7l zkSk4^c_%IOpefDT$N#pEpS7QBqt@c$tXT+sPmX3t4s>eL6feiW>R#xvun???rj`!~ zU1qr9GS?y#3z+6@ z|Jh~#2e-fWgfHzHDhZ(!>lL{UM6`wwuMbNHqb^W;^On|k#dt-=X^37^?<^#|+%7iIjraMgZ zkE5QAh4oqxV<45r9f(gbMWsNu6 zLO)BV7m3-aGXWoE>!c5h_)o}&`u!&N0AuC49iZg8=Zu6_sk5Hf`XW%Z+f7@)alS8H%49yIzJb zvvk!w$@tc_Q5F*1CLa;Gg$Qdt#z7IunyeYsDS2P^8IQFwn*n*@*vRdynEx{^;}AonY| zq+uRqYfniO}Ly2pHnL9Z@tRBNaYl< zFeo(zGPV8MBRL5jtN#RwTl0K336>a*+G+}=>M?od6o&!aP;3+`Z*6vfT}5_cz5_mD z*=CEqZf%?yY~R}ON!hm)xdtoj2D)q|!Gob6HlKIPanp&*vJTrPW)nrXsi@$#gtQB3 z9|pb=pJb8|7bgzhKVY26+kT(#r6fGbs?6PB88$L#J+Q63TANi0_B{YQc9QW9k8BT$ z9!1nr4)LO;5#(Mfxn6p33wLk4aR=UCnn7{IL8}c>nH=6Lr^fY>FMQc!%{>>r9FDzm zPl`g${{rzV&1zP6p+qjz3y)lE*xYvURIj_Nv`0=KxeBZd>`U#8?@u}NI`;0?EU%g; zkCM6f`)`bNx%MZfyTYCg7M1MX^;cgRrS5RC8O~-2VDHZhzm;YeEv@vp5(03v^Nm7m z)CBoHNH^h~bLT&=44ywdddYSExbnOyRAf`{)oCeh)xLJe<-f((zE<^X75{!*iQVa* z?FmlW&B4-#$@Eh_xozX|_4Q}oX1o#;3h*AOez9&S_VPoJ4kmeG%Pf;ZJ!T`F%Y=p{ zb8e>BZe)1leDzUk(kM4fcOSQG=vWIC^L*TSyPy1)LWc`Jx7}s(fdN{4hhzP1 z7l-UAdf_x3Dj$~8u*}Y(2?W(4jlnY-_kCy=KZfsj0Lnkr5nuJ@rMznMxWD^If9^jW55K}S*lc61C6f0p>X7#Z1+;4R5BO5-Lt`wZsX|_uF7G%qxC_E z@A_`#_@-lM58G*Ll3SRe(R6Ll_Ij6&ef`O2sr~WT)Kg|mR-DrU-8KExGhWg^jEoB z%CuE;2FBYI7t+vjZc~{&t0;uk1FT?vy7_@H&)xBBC=E=qa2Ibv z>8Lq=SJ6&f3<%uvF=LIO4C}kqF#B^~WXbTX%S>J1`@3}jx5bKxT0<(%u3%T|hs+1_ zd6dUu-R@AG&V?0N#j>3^x3R772J50!hF)fOus+Zl_b;iBG}7|G%9$$Ta;hzi?jw5O z0=#JVN@>jcpgHQjEROL|8!p>wxm)s-1=Qy7?I8ZFszjbrkozgh+0Y)A z0fPOc`LPDTVzPdNxvw|N{VJMkn<&WPnX;YWOx;XlMT>QmJB#B-N8EUFO2QMi^qp@EgKlX^TCb8_)t5t?a4Vjj9NNZQDEU4M+l?a9T? z&z!IFg8*zd@(jPS7Lp{lla%}*$XJ$H_y^PF05>It@qC5pc3!-PWjyBDWCSTi^Bc;% z)}k*R^}l}@>iu0d{{^_{m!hHj_>r$pM`Q$h2#7iDbClkZ&NtAK12Ds^VA~l zqwFrwSD5r3LzJBMb+im^g$5!*f9EA!{#YxRth+6KGj|&@6!!8%k>n|8MXlG9Bcf3Mj_2BQLpz=@5btWhbl7FGdYqZdWb}}wI@3V?Flf~= zpA^BUc$3ZpRoI-AgLLDng?*IKzLKjSkKrQ-x0^W}3W5Cadsa#(P%MHqI7BhHVGGDj zVGRc18;d^EHp1~~AkUo)<24w3n*6aj7Z&;4pOrYRh$+JZwJ_DMUfgEh(~rfW>bl_X zH^?;`?T@`d7uvtM_+y{Lqph2SDdTWhjt|0_G8zS1$i{kSwXG0Exx>8^+E&0gvMnMJ z^<*Ov_4O-F)KjMr$)lNreKxIj2~a4q@peb{WW9be#K5u>y~b_G9ozW$8$Z20(|&33 z5c)?oabpn_b4?Xu;$YsR!wkLK<)SW9E+>JWU?>VvGjC}XB)U0<)Mj4 z*FxKLRW1}qwS8JL$L0~)K!JRxZrT3gpu9#`KO3{@C7VoLJc&v~0TZEfcf_*+Ktl&dp30+YoFoz+ia+uOb%ve}O zUJ1^NMO0=lzQ1I26MG3T+v$>u?JSP`?bskFi6eov=(>?Jx(wQbU5mmWCnJXsd#KE` zk12)@OyNui!~0cKCi#y;U)1RhB+4`heDJs^@bU#Cr0a;{&3y$JIubNn5Go zxp}(;REu1~W4u4XwQ7RT+u_T$XhDGrt&drCA5{aUl(gci#h{H>%QKvZ*5 z+1G?{6FOKNNK7oFYl(Z1DwxvI^L?A^O7)YE&2_!DNo;b#UNVur?Uf5o{M}R7@xw`H zjHcYE{ZmrQ{2_TB`{6J7=1YCj+A{ec`NpK%!vbv|{)Eh&TmE@198*=E^W}P*Mxtyf zKzb&6>@$|)Ye`1HTE%WxsCC`qdp`MEZer8bNKFW;kKYE=?9McL?8uLq-?#Nom`owQ zl)l-n*VlLwHa1A5mt0CMCxzzc!pBHc{c(vgH&adU#AxR(O+U5Y#PoX@^*!;JlBqCG z`_ZTovwG39>0%IzBf=!1Zq{Jitb#koDk)XPZU=aYGtLSK&-)keCvnW&?I*8F4Ga)` zg=ZD$*nMh{q&oa)Bz`>B_cPb(?BcSNbc%9_p5fh9fO|{RkPKOvC*Yh4cItKHtq^N_ z!Tvj(y~`}iqiHNsgw<{rnI1itPtuI?-icchMaQ=Ah%z@+vMqkNaiBFROD7C{H=d){ z*s(}ZU`ZX&>W~sg*b=LG7^gpeDu@AjqV2PzuF+Wh`eumstv5TP99?39+F>b5+-Y=7 zjZ4?vM>7SHVo@=til-bET||mOa)DsqHw710o=Zx{hljIh76mK&5#pdpk7}yQFFI<; z%d@e_YKBT0^ibRKeR?qwBPjAL!BHsqC#s@j&DsWFW$>%$&~Ev+9WRy<2DQbrbK6*P5oQvHGrN-9g9u#l$kx zi-TGKcsXKn@NYQfZXfz2Y2MiGaly3zM&-`mz|j%tCmsALI9wf~2sx8b?mO9#sLup3 zUQFF;0q^p$n$){2ql0{+^vO0iIv(A;c`DN34*@r(=NqK@wYroJfLS--z5*l9cY-k){Abcz=p# z$Ecdx=6J?FPTHate7%Q6Ag6yxfFRCB16;23RhYNye}}x?+Z0{nCFqJ8c}TAzef9^? zhYzR}uhu_Kzo9(*#;rBfXZ!8$688gr_5uUsHny7o@s-clc_EXvM}w!dl2NIQmmomo zBKxMhpDik7@2eu~bj-O?KMX&^+hkaC&_|4zSf zk7hoBP=m*@h_WI}ZgyY~up~0Ce^cLohMpbYfS|eF!V_j<_~RGjLpF4?ejEi%TX&2v zBvb3CYM+18xhYI6H3qd%_MyroRMf@RYkFRwM`q}ogRm&Bb6u2m6E+Tha1Zxxj}HF| z{P?iqpCG23aQgV#l2tm9QPg)@)uGpqa(Kn?@Pv0h5+iR>RkGyqw(CW2Bgnhg=0>fj zE-B~u=(ENgZMg))U18+F`f)Y@&wK3}cR$%X(Q_YU_G6{eyf?TxUO(8YX{5>JtAcoR zVEiUwM!F1oheftR#UT*8rIS4~#CLrp;u^TSKVvlN@t7vo{la)*fuYN6WkIDQOY>T~ z#pfb1{^kp5&{%kU$6WRaY3AG&h2O5ox4sODGXneEcgsEEKU+F)B@pL;4HGhxf_ddP z@wBcd7x%Uex(inr$0bZ;H3u!P=Pw;7d(l;6Gxj4Nn^Pd+LrNZIX#kP;>gQeIcKBFl zB6CA_)ta)wg-DcrCn0`dQ%XbFrf*!FrIBs2!*@T5javz`i`Pj{} z`cUPq*dH?E7loX}zi9t>fP}bT@=g;8begglX;m21@fBX+8fP!|E;nGlkb2 z6xk~?nw9n?`D;{N`|p2c^~0^Tq-oniSWaNt{2jAkpSp%6<;|oCAdPlnsd16<==xO` zNrp7>OTR|_Pp)lDUqliF=6-JWPPbG&QRqYxz)X50w#oA^122h&!v28XQiCerKh(T_ z0eyehLPb4gCL_+5Ll}OmTzVO4m~4_U`k1i0UvgQiyx7`G_1ZNhlTu6kvc5x8@dCqK zMA1Ak+KV-sWp7$E8BifQI^t@h_e*8JunR;V6mI~1Y8LGrxs-S3QS*B%?ei2cHj_dx zUTe_@x#V7;$xCWZKqOn-i&pT@j>x=evi&+HRb#sudUVdLQ&Bc=_5qx`#d|~1S@xz< zu5GT48n&GYYmsPuCqL8Mx?p=T*%Uu9F}`b}7)?dX9n=+t04m(}oW1nguL5%P`R*op z45iV@Yp0J-RbhzK)oK7lW>KlSMAGCE+r|c06IC zD!0^spY%G60W_o9_gGw}OI&8>>V`oj=d8Ej_!SDoP_t?5Ol~)b(Fg+ zzTC))q9(!U2P4WR&PJiDZ-h2Nl8f{pHS?D=m2MD!j zH2H-F&A49k#E#(Ha%k+oaS&`SUqNo{-Ks1XkSr9nT>n^B<#@&&Rb6SNZhrAO=(P-Y z<-?nn&d$nu|K$X&Y5PSOy&ND^o{grg9&@JF0%$d`$vl##<6Y1DZFMtTfYk*@_Dhew zv+ev|6qK8xzA575p?5x3D@VC*6!^_b+4v!)Cd-8P-84jpZm42SDmEmq0M=PUQo>n; zF_=qFBiR0nQP=6@AeD3SDo&eqetC~q?|3LARh?y>0x7R{|^4_4`+ z4Ygynle)7A=jar3Zi9hAT>m5~ch003jR%;gtwed3b@1KyzwBjo1%tPi7w|fnu$nB%v8s{h*wya9(@$vnMrZ24E$cE* z@ubX@PH@DX?NMZb?ZL`J@j*HY{*&oYMD=ElHWbe6KF#z1q7MyvTO{L$^RRzZc)3C# z*QZf*K^q#>rr76=^N`K*fDpoXawd7IiYx#k6%koXhj$t`n8A=Eyz#ZnR zE-=?L9?R5vIMy~P>e9)v1z<#tbsI=oKz)pNWGt-owojNXNsn|I-JD>ogNRp=rifCx zo1UZ$=mFSlbb*JRA3@#v>#7bFj$fLW0}w(HvfIGVVFUvhsDtzcJVWT9AzE`s2R>u3 zBkZFP2nskp?=g27^_ct=5)$m%gHnI*13KL$?HAfc5wE`k+20XL0nG3~HQ2#lLIhDX z;=8YyOUOv=vSBg(F#pXMF$u!7OA@YhM>R%a!VG_%1oa7q-hKC$;G!<;4uevCN6;7c z4G-2_0z}(@{IJf+oUa-zmZ$@9rIECkfaoBa2R5>+=bpM);XVL5f_D({R}KckhqbA~ zbdsP5b!c0A8$AV_y_DUvs!lNH3hF#E*jr-VGMc}WG6rJZc6Ddl&S#G5L%-)2B-*3+ ze9MRiC*H85oB22Pq>~EmBZcx)!tb9#N$(N3kbJ*B_fxi~F|R1Zk%I?$8q|ETJ)hMVG za;C3s(s{qYfHF2Qb0^ro9s49`XCkN_ffj|Z_@ZojV>QT8q<_e4NN15*~C01aN5zS4&MJYe!9c88H?wB~Zk>LA}> zM0Ash;=6{kGJVSfBM~f{a4bDJ#-=0fYrARISsiFH$n)5+Q6LY$z2$edsrTOOy=uP- zdXO;q_rl!n0h%-x#6373d!4ZT)^WYP*DWD%mK+&vlyY}OP(|=CrjCR~S}-CPtFSH( zD7eynze^}h4mk?8$QYyv2lZ@|K?1sP{hUrjI@lvPJL%(jnIEQH23A&MBsIxEGY@O* z?Q4UCkH0s^cLh=S)h&sCet08&aRTU?RJ-S9{Uw?5%F7!hbT}!u_VDBWq8@-ZTOx&0 zyqJl*!@+REVS7c;U(-`D5s99M=_?Ed#5hl}B>IRnZ)o>n5chtWh5K)Lixl zYEQUN&&w~T`}slr02cJRAKoRdb7Q#QJ+qavCPs^#qXkMkF@zQAO%Ai|2MC}&+UA>z zfYa=?G+Oto*z5nARkbS}60BM!wb#Q?n_2FC;`B8Tp09CQjt*uZ=8j#`Wv!W4gXIfA1{{gf_rheByq^&hhH{X! zwbvI6D+>njp~#y(q+gD)^%>)Rv0rfGDZ*;*fWM7oe-`y8h4t2)ACQs%lHMn^SXwJE z-Z(5@O{6{{K~nxH^EZwDas>R%>whh!K|wZ3Jw?Of>0B-9F?_=Ut86vBdhg^ddqs!z z@$FiXn?1b68Df^wXDhf=eI28 zt^NnNcdWfHp#|E_3RYzz{%J0cIQQgLEgx%rY_(9$y{yl35u5)Zp?~=c{7##HL zosC^NZ@F?TGwTW2`;hI7%4MvXV?X8zY99U0_3l!%5tDf`a9yQ?Iq8DQpC~eo%p$mO zmhR#|3fdA!0iPpcNTXmN;6$ajAFOf+_~Oy^mJbue(`)W#~L9QGJJS2Lf-6 zfQs68F(ktm8J7||Fqc#@?1>kFM48?hCq1ThaZiuNl99r>1`b?jG7JXiag z%hf7~+UaQb&$eKE`F>@e$+tYCV8pojD$wHaId&NDAT168`3M}uH;D1z^{Iz6 zE`+)NfW%x5ibQ}=Ukt4-gK*P`2wNPe}V3u zhYHi`k4JHtOSpa=zU132+lvLM3dvm#zC+t2hQ18$!$1h>%-zwDk#LZbXwT+j1Z!SdIEXz;lAhljx$rBI*dee0iu)dU_?#@<`faUs zfhlR@-rkk%)ReybZUx9Oy4$E$Og5-N39iHh5GEMnmhJr@(VqGu!LcG$V0wq%rxWUN zhI)m~a|K{Wz?@{^GIAYoMJUN&lgI9!I6M35#YH&iQZer#S6=kiL{)U=Aho~F@$xYb z(uD@?NplkiI`WXk5qr+8?mzUP3}jnOSv1!th8C$Cs-n5^zv2_tEX3qp0NQ$hxW13^ z55V|`@jPjb@giw0UrsbvN1s&taV;V^5o0u^C{>X!gvv}0?Z1y%?bYFb%yN`erSO(LZ z0Uf5_55oK>gTB5lZ`UI|fgZR`jVs~B;3J(_b}dSrShgazhg?N6q=5}21h%}STgbd`Xh@oGCU9kg2AdlQH5LjSF^gLB^YXzUR+z`26HeWn&OfKx6(LeTaef?2+Tf3$t^6M-1y8u?|qML^ncb!g&I+VYc4%{`@@fInhaSNo*eQzuM0X>A#9#+x*Q z4t|D6Wm(e_<-PD8bm%z8g1y%8o4M_^2)$xJ*?U6gxQHz1wg|iGoJ&pr-?t<`c=ns% zlwAhqyU8RX#I~Ay2%c%LO*MEXUsD6po-va7b|l)@VzF*na~eo7!)Myp703%6X#n!d zO!!jw849fM^~9d@^Giv*$wxGfi?xDoYU{nrc-EZJ3#Ob=BTdm6lb2gJ-Q3osdqq!< zR1g=dNb>{N7gsmp0DfZG)3tXrj8Yyx)_>85CP8d+nsN5ESLr0ZDM+JPc2~z7zVs+8^CD;S zpd;I@a~3XHe~k(@+Sv<4+q&s}`y5%!e;!Q!J%wU{3Kd71s{gSAGT6`y6vTJ8zpkQe z<9^a$b7VLJ1{6tVUv&fuv}Ty-OvHu#su}c04r!@C4v8X+iR%C+Od99(wC&I9l^Kf{ zSIzW2a-wD;h#UyYR^~ZI*?~JV(tSy)Z#W(lgj;q8n3C=VZ3I)I1&M75&H4{INUoqraEIg)$_8O? zHNz!QcZrd724{V;nA+?Zx-=;St6w4b?pdB!J}M+19q!bR?0yYCI+(a$4Guuz#|~ry zGxa02m~?+0e| zaNdP8v36D|tuptvxB2ah*S*5e_Y=Ddw42R+-02w$kO!g8N;cfGYE_2C*An6;I3o` zB~{3fwwiy`a3q`uwTVTabV3ECi50t^ zhb4gLFc2@G@`(r1+x}3jO5M}%7L_JRJ%^I>^i&g{vl;1s93&lb|F0z zB!VM#;EMT(0I#=IV;;aQv%zisene+X(ARFW+<3pULk}?WpTW{IfWH@{>2(x=(H84L znvgJPuT?Moa~Q#Cy+uw~-lMkJyr65HY#-e}*YDl|5xFT#wNKJ&3Ul264f4sG0T5BB z$n!S8<4w}uwj+f4z{(KOP6w|4X!142=vHuh{+ACBt*JPi5P55s~13XsP z$93b=PdbkTQHb$mR4@=6+c2f!`!3&2qpkSJ%R7tN%!Opx&paf3|$=-}s(2Y!IK57=QKUP4J; z15PB}8;}>gVe#Xi5&j2-GN@nKZ`gZab2gR-;=Y16QuUxgQ3>zK*k0lXgTo6v;QgRB z4UFt2K0=0iEN&xyN*;vsQ~Jq}qA6|*xMXL|oSu%_`7MX$?vJO#h zs%_~8+T-81nzM>l(TRjSf_XFYd9W@#Z(7YSzN)yMn8(S_A(SAq3u{k_9HX2D8v8Iy zPN+*vSV%6A({PRBZ(zuPMr_`Ceqk~;o6;aiC{@LW@VY6Rfj-A2KX&eY!u-E<$A2J% z-G1@+LS&Mx^i&iSuKyq1frLB$-+XcZCtF<7|HdEyRE+-!i2o_7O$Y;Z_0s@CvPrvW z=9CK@Ua|yX2M6BM(87`=d3SD3W4W({4g~|kQEJ9`Lvi|Vk7Ru$#(56UIf7uy%HDKt z%b25t`eYdouay+v$#FA!C_KkQ(^Wj&^t|xnzhmX6*dXk?`3IZpxC>nOoRhlHXXNAn zI+vbNvU4(q37MfyW;j|lV_rT<>xWx)yyt)LOc63YmOJv5n`JfcEP&rHk{Jq+*Y`OoBGpkE(F9&ZtVgI|!-Ct`zO8sMHk9I~=`i^gWYApIr;N z_AclaFrE*x(Sx1Dfk=im=J{=MqC5lvOg|`DK0(URg^hRfn9D*;UGe(K^Xv1{YkyS{ z_`B1b$Oo&D60|czWgA8TF0*J4Hak6Jb~>+)Dkt{k{e8{J%$)PPpLPP-NpHP>n_Xvi zlc>D>EBSU?3OH~3n081)#tO zsjm=IgefSYlXaFpg4^-iud~mhAjEZ~M&yNfZ_u7ad`xHYib(N!dqP9AmEV?vYbiM$ z*Wdc%ra@(8OeWK>|o`+znm7c2}24Z(6(;Fn*+djt=mRJ{Vg6N&c~r@EsM|p&IAAiuqG-fi5jEN*vXFA>(kYb^J7fxvEtBPD)$V4RN*3m;Q_i% zb;20cU@?3C0R2vN-(J5b0ao@lN|gsV^?3livic{am)P2RU%F+2=9oKj&+EoV@T7f< zJZi$u*~7fv{{z4lNCh%KnzvLXafxKpOS6;bU3mMgW4Q? zj_Qn1J2NIMEbqjoY+U;dMOC`q3!9V|6!hEjH}8#OVhRd&{j!^FNlypu{gmTq!wg)G zlWm!+a1=S1t|1b2!r!P3o-#i&mV4>6&po@q=U}U`F9^uYh=ukE21-AAbmsNm3Mc2x z484Eb-(w>$);Pm0A-U}0n9@Cd?n6Pb@g~Bht;^W^zXbPt%<(%+t0xs+Vri70D9AFj z1U}o8B>(WZ9<*$_XQB$C!S~cm6N{ep>3Gu4QvG-Q7wz5ZS>x$HXyqvqUqK1bJ|JI; zvZdLe{R2Gm(3rB_={|q!T~jrjP2#36#`%74c}>6Ob8FAydDXGUo#k~ZZzFOexM^ZP zxPx`7N1Hy{BTW>YM(K5!f7hHEy73NA`7xJ;hhOiy^=E#!k0mcq6crsI`>sA|%BeeI$9|(bJrvDPlmN zmR=@FiayX-A}jL6<>S)GpHb<~OtWm#7Ji|^V#A`3rF7Qu!^Jq;evR)_;QRrPO~@a2 z!_?f=W0x<<{l`*lN!Ll}N8!iSeaI3+i+rL*4yr-r9P zaz7Bg7;9?HlhCX)ai=#B9bWBq{prP1h{xD)RPgTn=m>3j!EDMDu~_<)q-&-Ee4;t* zvT?0C$E9j+XtJ?q{@t??@B1ExKEdhI`%h&D*|jG|qv2Azzq+}3@^lploBY&2F|?Vc zkImtYec}*slzh7J;LqEcukHBmt?IIBNh}I*VZ0R3YSi*J@I#-fc+Y<}ASwMwqKe_C z=3I07QT<(h}f0dP2N#8t0LGE+HVmipG8LEuFnlnL0Q@JHy z6+!`g zpM6F>E}9=`ln=<0|k@fO1)6 z?x^hS0nMFT*oc6gpd$3=hTfoTP^-M%4cO;UTI4#C85!Y+=JX-cY`M1S~yRLC6{?nXHlR60X8;R8V>|O%gV_mra*tDT0-5?{`5_CdB*6bO z<9m)dyI?I!w>>dp2tWq;#6K3Go&=U^G3qBo%VPs?V63x%H!vj)`;{bk(KdDaYtZsu zfJe|aHTpF$yW4`(aeV}7 z4KpwtnOPI6iki+qv{Q^BD%T&2md65a!}Dznj;C-d4vv2g0fbO>p1UOnm*M%=2AAQt zEA@}zwd?nLVswA#AH(&n2rk3*%?ysGgy5m(zW|$~=5zdQI@S#R;tZo@Ri1e8)qRmC zN(FTpWKg?4nhpy2;Z@qJ2(ZO9&Vzz$Q4Gpkl7rsoqtd8rmPag{gnU~hakRQ|4gQuy z@^|!bEcn}d65#LjA-SvqF76wFzt}=4eQ)WD)6od`BfDeFF@#bQ-clD+q7at6)SEu^ zlU10&F`|;9wdWvbA}ziS{zmd#K)|1C{w(5)eS~LAzAp}I&>+TLzaD+ z;z%^9;QP75?BL!gWVI;^nuggmqaKD#1Imbx#W%du0HADv;{WVAf1xad0oi%;x*$0d z5d%j%WJ1aS1UGMyqhyCLj3T=vWQpzIg8r2AFEbD^L4=2iNQpkQ8;*U%n*s}zzvD;| zL0Qlwvhpub5kycw(H4xoLRM7soJh$!6!FyiIeW%=YvlOmcsHWRfhkde-2X;KD;Pt{ zOA#O9A_eEL{F$%w7lluX?ONZ3N-CK(3V&9u2Xu`=0xqYG?t7yi}@jF7=ziuye_`1t|oj~NKkBX>}$X8fo& zAQ$|#|Ba#G47ctNHCl8u&XE_H7#~)(MV)s`8O4eTsFXkY`T&Cs?1r@--$dwUlE{)t z-d|y6--*Bcm=^KNJUV7V3Ow*Pm9r%$C*l((yJsTL-mb6mEXGcX{?Whg@#+lRG+cZ1 zA~wR#Q!bH@$&USnnjc8RW)h?_O)E-HdS*+eFaeEXWG){#`*QUjS92)@ zaRy&Ctgcpd&xI?ozgO>k;2IJxFgjFnPoX5v^84dD{U?n(v_a?TnZqecCsjwRf%i%A zg*@&n5#Dv{8W8E%B&WLmRalnL5BPhz*2zb<7rkt7dp0JZ?!E1rNF_0yw5%}NI$eui zuG;ragR57nJ!Gsc{VeA!5*EKkb;QLb);6Q&y)raI-w?fRJVSqY>7MfIAJ0lZ28rRp zLXjLv`CE?kRN_=6^4z)kUEl3v6IrOnk8cl(xP94$42`nt7PgBqM7QCX1>fFs^e9M~ zqjJ0wF)nN#UN)2Dl z+!%GuEkz179$w_;c9Gb7V}+~}0#qyr`G$?N{;zEcTL4-IEgYuQ-S3Qf-ga#I!e@x_ zX(9ZkWW->yX?^6-U{srC%v7~*CjIG(1qMr7jZWg*-nyZhSwbnz@ORAl} zI6M1CMqV-}S7S>Me?CBPu%gxVUNJlB81?!{^U8^45&PdL zpi$U3|(CRks z3y>`la@KtK979H6E1M}5okOwPZy^)bhJHZ03kWe`GZcj_1vi}tRDFzNHec2(*jk$E zmPbcz`Y9?sg*Lr6-uyxkH*>^mS7g_YvP1E;I@P-(0+tU`?o*%+MWdXz{3TP8sD+RF zxcNb}GJ_>t0(8%-e%vUt7%GXgUEvMY7^KWM9e0e==KfS2l^TDgEKq8;Yq^s*Pw_&O z)C%}F%u2MfMQ=gDgZ9TW$|9)xH78LTS}P=JKI20uU%#yx2@UDzkBxj0C*#|S#J?e9 z5|r%Hjag^xU#dOP`Zyl1*v9a$Yt46^Znw0w2|t!)NUX*--KRCe>(^KrE}eZ>*-)_? zLtxK+s&MOeC{kX9V(d~KMUm84HK8Mv(C-0Az^nsho!fVbhAp~YT-JGu%fNNqIrZIJ z{G8nI|8RFdtS6#XaDdN<40>)Aucr@esa z+RwxQG&>Waa>@lO!cLS00YY)ocXe1=G6>H++@x9jrzyD?!W=Czk19F|jbU2`?t_?{ zT%qqhn$4<7x5{Wmha!v8+z9^)#a&po*9D*@ zx_MGijXemIui!KOv37Z%6ELQ8xF1iMc7wG)zLK3 z;u<`-26uwHySvLFKyY`03=Tnq2PbIo;O_43?(TN*%lE(B`*t6urn*;Gb=6w)HnsQe z$>X>q86a$2xWxh6mvNEdP?7CvFgc9VcVCAMzNO3d8ea2J zV%e+6oRe=-x42U~@6>Hed`6ZOZ_CreIs^uei8Jaf?ogHPbjP}64PMbEk>8?)3d2T! z*mMAKC4z;7Ly^U;-y_itQc483#>`+5&VORM=37UKvp4C39IZ?0D>xE3e7hivm*Zy@ z_|#otWh3g-I1`lbU_FW~k-xD&CbVyM6j<6=vg?*3>GeFi25Uh2AY2#2-W>5-a;_~p zEA>>d#DI(rb{Ke=2d;341~x7Goz6Iggrb3+5uWyd55C!Bfh!*&xbVC!R8ija7m;xx z3J%2ZJr%~pqK3t0)@7NOfU+EfJ2th+fIuxyGmltRu|kZDh`@pp<1~7mFa7SFc&zny zER&>>gyJMP!w+H<;J==@1eLc5?^O2*mnI3jY_T;L<2z@882_+}nrf`zj6@)M8(0Bq za9vijt+C?J^wdkld1Wm@7d7op47+t~&;zWEb_Du)X?xxWxiJ^_Rv!LpFg5LkJI6PG z$X!BFfV`C=!gRQ!C5ho8Z~cqHV}qT6yu*48>li7;d?dFmDI|`qsr7|q-RUqdG13OT zf?aCP{K(vV&N9sUA#0|PK?jY)?QdWl9^<5ouuT~jhcZk0VY$b zV#c3u$Lq!eX1;k`EU^t^!DMV1rZ$^4+N^X^rv66iT9E=&%I`pcSawGMviVX0Ye@EZ z$kE}a|Bq8+xs|eRj2{bgrPa;4$6OW|2VLr02u&jxc7~1pMTCi^AO) z@2zQc;gLNZ1NipnpgJwkZ9nsmjCHb-ThuIS*sI^IMLgC2X-^K%%EO)qV}$6be&_e! zaolpx_|s|<(jgPC?Ffgy&5%nOlNbK@E>B>h2A*w8baz*^@pxZG3eC(m(#*#r zuyjPT#ul|iUOkR3oMUZLynh}`tnysSp?gB#B2;!VtVa;u*YlO6{2nb#()t~ltTxl# zhk>!670uWE_qC7);E(o;CXDVv9M%*03hKoNhDiC`lpscr@LX9|qbDM|KWrc*oV@;~ z5nUl-u7MQ{ixf7^pH~(|Lo6-{yOn96+@70JU#E(7Z$&L4_2>9#AjwS3c-rYpEIOgH zLAuzBTgUr`BOx!y4fX+b-bin3*Uh(U18siYW6Z3)-Priw zeTgk~kNdE3BrRr=s%;Sb4q&z=b8YTbTp)GaQQ3YI1sw@u^2V zj>J`xk|pffilmOY#cXOS(rtfgA?XXs?YeuqFzh1xRlZ3LJh)9>J^5{d#UyC2Lq*ro zus@5k7O-g$;R#_lFD%FIX3W5}@pW80zk$au7S2!I?oH3?7HlqD#*K{PnkAk>bn@Hi zrcvVnt?Ye<-(@o*p^u|9GpkD3d5;y^`P8qX z>0Ju=Tu{uY6$$q7R+TOZTFSI58ZKo8`Rq=F_X!OS_nCUm6uvWoG%&8b^N$-|VtAj@ zf_h)uJ`*b?;t&Pqp^_!c{VG;M~HJE&kP_Zc)*-eQB;qqCxQKWEG~}$F7@e z{symKwEbM9Ht(TDrn@|n!uP_couqk^7=*_3jj;dq!S^Be@TVWqkrC%~4LsHw^998# z5I(Nj+LDc9#SO@))WlMp#ZbHsLBwCu?#KX_7~)Tzjxih+`8?BD_s;oD&YChBrD8N7 zY`@f=xESH!F28xX3!(FF->f{%6OQIgQD%GNkC`-*TrhKrr3?#2OH&r;n! z3wF(p1DULm!EHrGl?A;V`o~|#4`R9>>U}{Jo1Y~Zw;2vay?HSrDM_ZJuVuQM+~vvF z-@j0712`_^+a@lE`UWU3GM*Xn;ga;^$gX(| zYFXcZ;mXHQd1?wLFR;(nJ(o53mrV{mMU!@6g|ApacRE78yGw{0AJ}d9NW4bm|4a9t2UYKo<#3 z+M`9qtw8%Q30cOv#~KH&QiPKLi+SrGU}$*TJnIO{YWaq5{)7lGRtsrsCZv)LqW&!i zFF)tiTjrjTei{a@d;aA2DCNfQ-S3w7o=r!VUm}rpy(0O;y&ah*?p~fCn#X^t_Z}>@ zf%U6b&N0rPQY$bPS;nMESXl&f+v-?f5n2Wu9qer zd~oW|37-9JUG14k=^J)kk7x^((`)J*N)H*YpNz|;4`dir=H(H$;k zi6Ifx7ySyXa?E?Kw204n%+RE0k8y|1&Rm06Ie$7CnW(nL?i@Ze^S@<0WtTFUm}Lh-G}`k`8V z_TlT8ES9P)g?>Q#7aaANF6?;eH!QDw(Xwo2tYXc4((t?K8ge_FBcv*$WyWb=@-vL1 z2J$h?J&%Y3?Vs?yG52Q>krm?opowHvhG#yy6nofyPto;gZ{e7G_^NY(vK2Z#Ey1T1 ze7mY}=^@|DI+l8ogoP{WmVt0rgIwguQxhn6*3T_?EX{ypaf{>MmPx+3v&BATOhqJz zwW+KS#dEnZYQ>Ebt`AjH`58?`Yh2vE#ogzkZN-WNMP^8yT9kAHN~7G>Jo{>)seJqB zyCHK+CoYF7euSfZ*q#1s7W@~Sf}Olg-ivKsaybj$-58ikqd=C+2R<#H9{pt{g1}U; z)+!43)dMh0PCc1c#i~S$uxHnGQx_t>;)O`1cDy}j@=URpJkDBmsUts88s|*-Y<04f zrPE^4@?N>MQ+yxV@m+>DNd}9IONVX=_maYFm+RRrooOXXX;fLy@2+Vu4-eKN>H!oi>SSoe<&GyA&1 z1VbDv4S!|0n=LW}jYI4;Uq>gOcdCzfg(l7bm~)YRols~C|Df0$jHW6#&vQO(Qt@s~ z#kPEelC2eb{*8q=xhypf{x6E?tN^#yTh9)Nu7595pF}2yZ|D$h%APOfOPOIuiVXH@!>n{f8hGo&bD-ZHFcP!$m577vSJ=Y^VUV^H1GHxf44aNw+X8=CmTB^PCYAVzje2 zsi-x6PNU5>lS*w6A+zeW*&g#TnP(79k$P3Wjy%I;<$(b@K3h7sCV#Q*%w@`=q`0pz zO>=?|^c;Bh@kz5KBheZsaUUf$hYmizNq@a4_x8&J%}Ep)S#8?NXI3eR@g5PEq6T1B z=F*vp=niG=BKnh@cvr-Iy<#1nSayFO+%=cDW$9>(MB5}^7ySC*o{ZZU{`iWu2D{#( z7akA+SdR4HgLxUnnC6|7P~noTt+mFf!6#8EQB2iBuL08g(MXDcf`p`hAwn@b2-VFZ zeHW~yHmQBKA9adYaJ9VghB9@pZeKh&x zlfE#{<-=}R6}eMaspq9;HqTGrq%A8U`opp+_xwe9uV^yQ z%o#n51up$Vhe;>DaQ1LO7Q~*ic7~m2fOhvUGytQ`e=Qwp`MfYeZUv*r;Sl3{pVj1@ zwBNhK^tfC7J-wYEPU0SUMqfQe=;Sw2rM{`d%{v1M+~Uvqc{{Su>Sz%W{EkuL3-k;o z)UK9%Y=45eUt+1?%o>3|rL7F@u-MO~nPnZ0ZhxrwOayCKa#lNxF<~k&^-}8my>I91 z27e1f_XLTl(OyYBLtFlNZ9%WdYcR59VounsHzsnyTT(pSGg zpHH{`W=LF4|H$U)Bt`;lTZ4AcGvdQMcNn(4n1k^bgD*#dd1o5XXp;IMdj+#m3CsoV zBb#?EwGn3?jIWLa5XECswG^dLMm1NvpULk372i7#ve;JQc$V0OGA~l9*l(WyIrHe= zYBCgx9~lryOblM59Hx4XA&T;TP>1kLzqzXG{CyIX!i=L}6+AW8@$zZIsFkew)1-N} z=(h7)Ox>#gD?b77kL(uT_j_HfukmBI{*QHW8?ArIrYJv8T zJN2d|pepr3(R$%zrC=_gM6!G+zQ@(FBh}FFkTcyPzOm>BD6?Y4BiHVHh_PRX2!t+A zFdkhYYY~lM3+-w|x%rmSAotrb!AwpbwZITgKLL#2FnVLvB&_KgOapHRYXELH$qOCs zZPa1Dromv-hccAC%;C-@E0qr%t0&saJE4~IYgZZd3=u__j+kj)=4e0fGRWAQvvWIk zJ2o8393ulT-lKH1ir$1XB>>FY{ru{q)y|Z&-Tkolz z!KLj+)Fjx@g52++*?4Z!fSVYf_o})7cI&@kv>pj4*ZhIvFyB7KEzUbHWe%$DveP8a zhJ|MoS7^;7l9YbtV3*!289a?#8{C~>Jb;zDaWaB`aEoZ^A(Vxeg_wJ*r?rMA=Ur~N zqdPvtBNf-$?=xIO^43&)A{`9Gpj6%ELmuaj@}$&vfOC-Wrn3yiisX0>(VdQ(Yl&e> z_bUO6xOkUi({J%$uvOQ?#u%ddC+q(XEQkIOe=N(V} zNG1H-ey$at)?lc|MhRh7wxj&OBwD5ycw7K~$jk$;r`K8pV&lYAduZJ^jc$=0w)0Le zh!-ULL>IQ$s((MSk~&Vtj&RsK_s{EEy2PF zbK?Ao-#)L*2V%L+PQB3>dj7jMvzWQGdh z$9O4Qn3+cy-!pQc91Bx&;`CsKj991I6=MK1?Q4j(A7~cbkn7yt@$DxQ&aux#F2_wM zc%WE|D@r$I4btu}W^#CTcfc=`Dv<$j8u46rrQf>?*@6g+_SomPh=MT7-Q2Y^_L<4D ze>X||aJNLO6z<`Lps(nB#Ltn4NV;^SazNOzy9MJ z9geOa^i9~V$YObXNcl%DlHNl76qaE7K2VKGRekW<_#SiOU9Y->a&Zf2Jo5ocr}k`0 zM*-pm6OqCuMm)8j&phFmAykE>$xo4kZhUj0mc#eXVL@H~6=UDltPSq`1{WIP zL^FC-so=)wq=GL^x0u^Bh_0kATeTh%(~|ifqSAD=)c6aE+qov?jtNho*q0Q6U%@$+ zZewQCIx~wLTq-NGItzfDo)=$XA*asLss?|^CS-<+`wvBNS`jg(yA-%WkgJvHW#E=% zUk{6Ama0dW=3LSwQI(!oc0k&^ZmY68(E-a2yl4D=A$~w|)t~)+IIo1+P(3}U=*b`I z;{VK@{c*n;)|+4-rX&S@4ucX|XER@nMb8qk2vLO{V1GGI6T<=spsw^c3r{R>4#8!!QXD*j7@(^GV4_-nf-W}l@?MojPN zLFdy(f2tz~&PgCpd%{4AcK0BzhjbKdx|weJgB*wO=b2~(>ZdH4YE(6WfnB~%=(<@k zC%hy9h9Soj&fc#xR(O{34UAv&`7vU`vL(8#OD2Y7jk2;~gZGrXBTSI1)7E&0&zrmB zczf8w42&F?IzT#L^|CPO%q=hZv9+qjOoYqDOqXbqmeMgme$uB z_te&PTd@8E^zl|}V?h0B)G=F&OIB6)d791j;+ow)l6RbH)$$sNR`IC))#Xg}_7$x? z@s25lBKOwR95wGcU-y6j)^cCb#lC2QeAU^bRQ8%%W%2a+IY-J;*k~S!_tYv!tgo5= zj}Tw6@;M;@6N2RV7h6lq=sCTqA$@9pfvf`r^Wua6cm=K1-@-fHw>n$yw20A4xS+p2 z|I9E>*Qws`UhEkSva+hC2Fp}-T{@!5Al2%r?#mIeL*Z^Vk|h9nS^Y%Blu<+o`G(%< z5Iu2$&C`*0c7C$sqL*dn2a-tDg^ZL(m%4$;K`RBksVZAK&GaO8GzC&i-h{+s9ZVT< z1_OU|WapN1ho$9O`JO!f;)a+o@5Gwe=8`_?^GWQp(!?*)gq=oOX2ZKs7WLe%v9=sQ zsn)0OUo+1zll)<5*MR=Xfer16A>`RJx%n|S{rR>iJ$>rld}bm__L*UXLrJ+Skp8hB zkM0@h3SWzYFs)ld3^yK)Ht^qQSm3U$t1v zLEG`YgjRTY{rlI{QI&QBO_Jv}4ApDI!{}kiblb4Yhb#3ngh@nC}vjf^qIa;lPvt1N!of3|SvCi1y zqN#^b;ae4xWAQTr{Nz#0Y9Q@btr-2YA-oqC9ln0aJ4#bf6W{1J+zZZGEN;yC-_Uz zUH$1B7q941z3p`oNAs6-fov&li-bL^+dbQA&S-bDgdi)|$^;_iyh`37!=qag8ZVwq zzhg3%{Fy$~YkR)T3$pZ?qc8lApU;AvsWe zTx{6^@et+1ceTc6P%2ME1DiDZbw)H>1o7AV5gIcj!30VBB5$NYXt?Goe*uS_5v0Kb zxF>7K#|^kA4ag&oX?Wycln|EnVhR3^-Casj=1M($^0_Yl2{4b* zprs!%JnmAl`%-7ya?J=IBvg*xcKKlbarG1O%9?Ju9@?OV{eDAN@a`I?o=D;rCp~?L z%S_o%!(2@feeNDtIelo3+r*Mt;TG3-bSsFvl&G}f6bF5DONaX?%_qp|KUz0w2R$f` z^Rx3*#>@5Qn-G{u;6S>sOcxnu*@oKi-31Ps?R%_$kb zc;FogkA-&N9kp47#zxgLj9vc4@4TV)&4@pseet1*7W!?^mn;QR;I?`cuqeH9+R5aa zmTK@v7pC36MpZ{`zZQFE2zzPYJQCeyfg>Q1sE0~GBY(gO{l# zs<1_0(?#|6>P7wb>ib%R3osnv(m{rG@Rp>I@qCx++7wANn*X7!3WPHU(YFtn$r#5>aG zn*N?&xu}wuRTXC{^^_m|(j`G!AJ_4lR^gP8`bm~r7dAIhwjNp_fd~S{jLIR+stDU{ zwCdCKpGWR6_CcFOzbsLAwI@Hg$G~YCHj&BEsD!p-LNc=lM)o~vQwn0YrTBf?bX3Lk!uCcs{bbBl+Uw$Ds^h;m{#CG>| zD-IBP7$?G$oIV6F{;}CBwhxZ*m|T~sd)=1`VBP6Dfu82BZC7?IOccx7;~HpqsnWV` z*yeR>eje=o08W?!vTsTAS(dUbE5Z0}+eDgH&uiPijGD6IEG~!HL59$BoR>^q1q7|w zalRmy4FSF{l9~r(abCzvw~)~ znmeJ*ECgK6i;fSMx!cH*VYp}>Xid~A2U^yFM1F5n;)wY3d^URj%*pe+aI%!*I*tj0 z0+P)Ewz4WZJiL9nn=I0@R7U3nLh&AIlTU#+CnpFaip5r$VxeSh&?!9}xml+Kfu zcICofYq%zfZsB$RlFf&9z;SDN5?()y)iHens?x0P>QpM}2H&pyyhyS1^@si~7h26Y zP8@(-{`iSy(%gxVlTb~dO($_5UVDm^J}F#Ev`S}roD8SCJ(?K4=KLl7>@Q~M{n^VGax8m9DR`RjwszD2g z6{qYK6m0C4(*<{Dc=ywf= zI1NgMKRPP>;wIH4w;iuEvv%+H?!tkJ_FL$;t%Ju;hF*ei$Un8-27}e&A|wXBK0SJC zX$xa;Cmnzmk`^A`3oF`fOp0YBy)sKnE;{73NgG~0hi@PaeSP0o5QL>cPgFm7$^Y|zL4n|WHxTW_UmB9WeXowpJ#dzbqqK9cgsdF7!pbBdSe$JSRU*@p z7h=(S_7lfGdIgcG)xk#zwW05>87B*SVL{JxgD02hT;@<4WfgxRUC-0qfYf@6*GaG_ zUctsd2k9A5eR9EJ{9z^d%e9Q83G) z%Khn@-LL0FsXLnxWoQQ)$b>SoQb2VuzhsgCa#2eJ^bh68bLM1SD6N zbqm|MP%B&1J4xj+guPUe!0R6v@y|}>6YMmx&;k^at$QngTJ4Q75_tOaWya^L>`1HY z#`Xr}M)ggX8h;1HDJ5kj9z_fq9|&pJcHF{n()ElipY?+R+73=Z zFQT2S+g+#fNcCRcN+rmS4^B|^Zd5to+(_P8AiM4oJpF^$mqPM6yU_n(M25uUY54P1 z|Aitphfxwud9}`k6${}dOTD+ zZRAb*t!dHEkZnTUp|g>xvR$Xs*dj}yM39$g&C8|&S{x33S3 zskb+pW;KQqa74sLhasB;DOF-5p`OCn6`l}aV`E+?=3aYo%oaOw;eeue>&?1rM$SWz zoCF*Z9z85@{{;87Z>4Z>%*}H|q0Gaa*xdbzlC_in$BCE5VWq=#^;}8Y(SLChQ-1#} zOD@+U%rgdxoLbbE#r(AaTG-8Ao#d!0>;mE1T3hw~ZO*SpWJQBJ4vlVf>Ta`-o^VV43gqzH6lzO|HYh;o9f=&KdZ9gwR?~KMtg;qC@;TPB7Zwu(Vs{!wKw# zCcXIUoC$N;vt|3Z9d1plX-X`9c5;4>y0HD$CVhE1xU+px>Bka!PAIAEO5Z*IHey=* z&XbnR5vErpa#5$QNkkWfaU#ir!O%GIdz|3C1tA&|NhF7vWboXN)Td0zm0`h57e#wjI(Yx_aVG}zuMv!rx*ic7PGCQ$fm zqZ_GNr1YWUK?n7DldYtDRHTUJ-=H!YI_KoUoApxB>EmO<*FWjC4Xa8WZVUit8p9P0*&Yb{K#TmDV_iratfK1^6H0($30iL>f8}3Q9@=LuEPN?kT`0*w2!l)O@+y z-SIY#8j{?KAYPj`Tn&Z@-|TE+uOaBPdgaOne8e|G%QtAPFM%tyfG?qYW$IV`cL!Or z{1bVQ$U}z9h5Whv4TPJY>65qb#e2s=miKtmhe;^3DKX#3NQ)=8ssuEwnuBR~9nsw# z+uy2wBRCeQ;k>j<5ios+P{Q3MiWm0~?$NY)P+(4h(-v-&Q^#jkbUQrHYszp}#yCHU z2Rk309}CMhw@{{1}gc+ z7o0k3y{S@4Kkr`cJpme5+b*xZpB6R_CfAf5o3IdH{h(gi@<#wG_xa1KsO{?DmCCg` zufY%Qh=|B*==C8;;9t{sXL+lu-R;~~8FeGbCA017hS5yvYdxZ|)%|anBapVjugkw` z#`E^No@384b=ZG6&5yBZW7R z7Hk_09Os--1=A+7Pqv=u43IaQp_#c2Py&@m4I;Oikt+%^kh`(F@#^h~8x|W{o@OWp zgamG{12?1|6anSTTb)PZfsHrk%ZmkbQi66j~BqIFGSA`|_iIK|a4>0BL27WyYj%ZuE zR9E94#~*qmEDALo3s1>&FUnmMSXuGO`NEIu)lXCWd_Ymr`6GC1ZppO2?QRMyyS(k` z<=qbq_TCsjO2x2!mA5lqsVSi1#MTT`9&Nw8{e!3(X7Qk$+2FBM)>;4USD2+l{?&`qKl7@0-!p0Uomb^{-iLzfWLUd0PU9`O62?r zZ1L&zanK)FBg^K-8+~pZ-St@%0n#8l>ax4|*nV})7_T#+?E4mowvC1Pv}g^-joJ_e z7diUD^#VkzxO&8VkJwfUM%&jEJPu>LX%0BOcluiw+oD8`)qF$D1jyoWuyF+Y&np_gIP z^cGb9#~5>CC_iHQ~czv zQ{ksYG2)AZn?0G|m4-c+5WI2(g9qB)sZE)fq#k4kEsZ`$SRYAeJTtJVJ0flX6lxNS zEN$v>wB2V7T4jfcbbKG*E3YWWH{UF6eyn{PJNIiJAwgQNc0uv`*wdX0Fr-2|P3{6{ z=%j?AYutkh$KvO~&twN`du6l+>B#hDVXv&!to{YJz$_`F6fwk`7D@dD0aU#=3l#x) z8F>5Il}Fx6!ZXB^hntMulam{OF-ti1WRJ=-8%xjVNNe?ST`GRBFIrKQH@C4Tf>Hju zd}NPc!vv7H3Sodcm+8fg+dXdPtzN+0cU{Yf5K*7hKCnjs>*!x6!@8Wc|JuN!GY?uq zvcdM4JVEt#xPOK+N}t2EwZ9EniYWH03kB(_cxqq42R9;y4XG&2hj1#>lZ5npLe zWa-0&!li##mX9nK$y(dhr4LJk@|zeg2`nj*)1PxbG}6xenmfkXp@qopow)z6vZPWJ z)=b8$Mi^pz$u{dBT2w0TEjMgx^Rk53!pq~iNR5Lz__S$RoBx`DAghweRhDAXKt zrIB>iLQ&&u095)y!{8b~q+-T>9JmSRgWhqhmu{fx<^;0S!l&*bMdXz9k!@p!EfPHU z=U3PtV~YtNt6wad<>FTPY@+U1g`U*hw%#Q4+sZ+ptd&&f>LCW^(SjjsrVOlC66E(W z?E%&noxe8w{ho)H$U{g~@yOV264mY!sQv2SCyIy#$FeH$?FkQ%p#u>?V#KQLy|czQ zPp9>=r4wJzaFUWTSX!~AcC^^R4%srGqtqn*TmF)qo~FATtVHQgC^M7I z0Mx?Yq$ARTYXdW2`k&G?wJ3X-x>-$inn43wqAd-5zZ~O}$5A3fL;(@Qy{vSTE8Ag@ z_3yK0yBx-P6qV3kL2Qd2^MqnHkiFMQ%?gfn#Z=aFo?V0FukNq>dZf4Nbn0ZC)xwQ` zkFEo4^Dg^vh|sS3>i41K5X$8YoOu_Kl3RNGJXF5PCy(_3CGjpp=lUG?|sUkWubQKh0 zzwn;!AbhfDmb5gAbi9cw(hnTF3>DA{Acruh5pYr#ZH-!j zAC7M}e(2O+aKx_tJUI82%m68bD3Y}o&1Gl}*aX?vBz3a;nNbbISd_ipR)s$=ZR($* z4U;r%>jshr)zuDu0r5TwZsl%&`a$$z@uJ}nNSUHLLhQV1WR zv&lc9n$3IJZ|-m42oI&WDY8)(G{rH2xuXFwfdN#j+@-3&=|*t-AJKYii(LmMk$6Y| zU7AyaI4}ZC_nx~5fT)!B|P zn7R52hJW5m`Y9vlQ!tJ8Y_C$1h$>u+cy^kU)w#_rH_dZA;7yT^^X&I2K{jb|!CNi= zQ)4Y@qPeo#sjTgsuq;%9So5}UF_Ang(fhhu#4bVq8>(^W2&dm31Y+3`F^R(lz+B56 zdu_4BZBC7R7VWz{6LQZn#7Ft&5!&$XV#TMY$CcqkxS*iWho|XWrr^Va)mFasI`P8) zyviR0T9$u)#+6#7yF?ABL87?Ai|Bsa&PSoYUp0+azP&(h8ZEU;PtZq3}6OOuRDsBF4R=70%I@Q;8A9B>Gf zu*hZ#!i&g9@)xoI2Mll*)CT<}FVwq#XF5!~;$|4ai&g;cyU-f*r8dF~Y5*zqB|ofR zW9J19mQWo&1u}s2%OyPQI@6^zj9-2yDK=OYsuJmC4r+tq63Byni|UNU_B_&d zdLsxJ|Lj@wk@rpSzmOf6b;ipD#21)=28344O&8d9jZFt!&#=x|T+gCT5X(oI*<_b@ zP{OL4xJWOQ0U+{AZUi5hO^455oPbK4kEXL3FVPTuvOX}|KJA?j*dMJnAh#-RdLZ}& zbY6T0iv?@|Oqbh2D^!>60m5XLGca#Cow&#^7y&F8;5?{zw2v}HMGk4h(kuX?iujH3T1ub{B^`F@(Eg2ob_X>5I71Y$b$O-AV&1h@{d{RKm+rU_2AXyD6>i3ks>5 zyf58h=%z!l%#<&DAG@l!TiWQRKV$h2E<~dW1g)uF7)=!; z$XiiZlg}+DV0+OhxZ>V}fdEg0M`f^3ulNwqU5XA8eZxYfAdz~*YDd0uyv}xn-ewpA zfNo@TkmMUyg{VN_3ylxMcHFM;mUD03AkeLj4pMx>Vipldd!Z3Pyu!QAzU1jO{m7Mv z4ibOMmKOM2mHI-H5e?S+NJhWy^buI|!N>M7$Sm}8QS8IU@T7dC{B-5{5&0ZmN%M|K zDkc!{Li576{qy6nHo7wTJAim0>I3~zrfx5H{CjY=n7~KQ7us!sj~K4r$K7|{G@w`O zW5I&H@=?>n7iO||#7Z#(ng5z$CVxlV2w7Bm!?G7KPK@^1`&p)vTtQva_NwvW1a zx9L8vq6>HUIEqV_CHIDv73@jyh~{Z@(DJ~OuLm3~V>K4eNy)e#>@y`Zm!YPEsyCM^ zce<^^@(V?sdAOI9jUks_$T)NSvCq97?~3;nHlZ;W#ZYJ+(O2GlP*;qq6Um$Y;0{kz zID0PFW}Phc_33N4K|~+J08G*nmZo#(etC$n)h z;r5g&j6k5_>e#4Gu#I`OYOD;w4n*ONjRxTQZfw5aB{Ug9Q{apE}Bk-D2)jqJFpY*{VKT>{Wm_)cpuur#Ha^_OgPwfs}tRn zR|g7bUOPA=2*Vjn$t2xqdY3C1gXvopWt})&BVmc7ikzt@F-5c}nT_9z9jJqwhJb8*nX>LzTADUr~=4!CWJD|trL z6!ZsdznR}Q>+!*h+s^hm`uy|#<)TQ{zvpYQj1x%_LWLpcei}k0G5jqJo@5nB=#Dq5 zr&1?I#xO%pT%JRXI&Ai(8+Y3<=e{`Z?JF@yGo~KE9YQ531uwr^u>R;RI~WbX!`dG` z$E!jAJ+6A#nXvSPDO(yi|1is5>*Eq+ZU~vf_x&k!Sqkn>KK;0KFTJ~@F>~+ z?#Q#R?KWf8QSYw_C_|~t1g#sOJ-%CN%-T|z^%DHyK{oRD$J&rUV?mr3nHI|(&|^W_=mQBG7q<=9u5?) zz8GQZ1ewc&V!TppkY?YVS-y!AFZY5jQC0LEYP%;Y60#K29%^6BfjI%th{NI zkDhhe6br7w-5`u^B^#|B9loJI!f7Q6Jbb1y)9cHbRp_AbOZcx(Y`pCw|ju+`U_2x^1w%*~)S>gBp+9@g!t*ZA5eAbF9Q8sfgaAZ9tXY@T0u zeOF+|S=;maGtAazwBsCL+t1cL0L_7Z8~8Y#GngK`_x|2UJ{n`adc8BAH|(VIdKfdgwJGx)!Y=DYO3%Ta3`U<%DcS5=h^>+IKk)ftWEI*PLA|H)T+_}lf| zd~}$_ijT!6O@JoEO@O3)rtHzTIQfp_o&@y---jUQ2Yc(gV}&n(9h*~Mv2w8p-;yB2 z%AH7!(<@_TBI=s*;jG9l_1`bieWMDrwzSno&Z&CF@LyA&c04gfrr=t!X+J(Y`}p_c zr6UMkbaO7;`^n5T>3;8F2aHpKo6n+;RhA!O89 zruC>%Rl$w?HV!<+O={n5e&zM8BT_A`8ql|e!+R*PmOkGT`bMHpt}xe8%u9PGAHQ8k zVP4w+FLonmD_2|frlDXes^W2D|Kui&-(nX#mOg(bTb}OsMnXSdo|BG|PGOKVE)hAl|<4+r1El`}Wcfu|)wx@n1ep?6P zYn4tZ%|I}lto4Yf-9sd83&R)1={ekpZ9-euohG4qzcPEspXX>*4=qoOW4Zv+I@w>O zTItxC=jw&{EZqm+7(3D~n7{Oez4By>uabm*sPk6dP~+wUBE}P_e;bJr_^(KLXIcDD zYE4QDq-`V@f4;}$lJsh^*E7G1{cp+mM<@~~r?z$LeRVg$|^pG|t%xWhLm&>HZatm?Ho{ zxl%HWqjP75yg|h~^*Md!9RF7~zKJj_CqmVQS}F4NT8~VW9AUK(u@~}7t8&y&~8(l{T#hG+NNIHtv+&IDa&FsooUr z;(|AJ-dAgmW*uY8yKcVgV_>Z3V!r3r=|VT^ znmfTuVU;X5Qf!3%Du{xJ=4A=a|3qnq-skpnsThimC4+Jmy=~2(IyKXo@+3HOgmKZm zDxV39;e)(nJZt=~W9sNF0R%u6AsR&T*sPGhwTq}7NP|DAbS!BWhpkTgiM7t%nutAf zO2@C(>{k{tYZ*liRyriVn78|8y+9#Wt43XG$dRj4rjl$?`jJ66U*2y`cc|ef7OiJ<1#V^`8_G48nfLxHVOyY|I^r40M)UqTjLNkKyY`5;4Z-lZoxgcYzVFs+##?D zF2NzVLvRlg2ol`g*|@_`&bjx#bIyBp>z}Hb>gnFSdi5t=J-cVVPWbQqM5@n$i|y-Jt@vtE^$FUP1}g&-(03Y=>syU7j<}*0DM%&3=XSUG{!2L*WClKRMN$~Ec>iQEfAlle&RsiY9f(^hzbEP=QVfUnEo*H>74acLSfW^bS#MUNUBb6#5O1>{che(`uR8I6A-htAH?!~Bs|#9M*zRS#jT+&c7@L|9iH9J-sh z`}Z#^et`gtFGox1w`}Q66Bu56IAgV(y#+HL5lYB+c56mTp7UfUT-JBfSJXCxU6c34 zg;j>?!5x&g{ z8*9r|L3I{($~@(Ru6x!2K`^*BhI<{QnYiZ7<3$f$A|@wK!$S>N>Wbn-2y zo>&9#H@tl^Zj-s!Ro=V2=*ygn;-$)!dCeH0s{1l+(ij8gc>NKg1kqWB;q^m^q=h5v z;KH4dz{l{wm@rk4G4moS=w~Oc#GO!6Aj{p|GG>xO4q zH4NH8O%B|3QO$Tc4(x`#Lb9yVvDlU-u?P}hCGQ}uBsC0F4OFxm%q>(6&y*H*asa$e zD``nQbIOQ16Gq8U2063YqPF?>>~xPhEc?FYPrT&&NiRZ-`_w*UhkcKQDLgYpf+=iu zkQatTIR84*X`$U+%Vl3Zo#8!CRau|C7$U{o=*Mqq=gGoMXJto?)Gyw%+puTqr&~>O zEk;9SB|UH2hc2Ierq8a3aP^U6ickrN=j|Yp->Z~<7f4tzy1K6@UHWv125~!-_W1lx zkzC3`EX*3m%`mbXFG4Lrc?DK1lV;aVStqw!OQuIx9%mk(JeE3T_!K7rP?+UiB2C~8 zyq)aOSK85pd{r$mQ~oUAC)~jbfn<|tIc(HK%waM1hoKo331kiD=q@!q_N-6 zvs6bfjLpTDT=;UY+{rZ5tyF4|Chwh9p9n(px>Nl47#GljUW_7G$minYFYe~412+bC zcGr`kZ_k&@9X@{M_vaTAOg+D+B|=v~Aj8*#BTEJm zxA_pE4(o2Tue1_*%CH-T!P)~IPENN(JKxi(O9?hf;#Xv;XNbHmsdpuFhHXdBvouGk zQ?qhtBX*X;b|#?_pLUg8NfKhz2;ZQedxo_w(=eXS4lsXj(?+q~0%K3m<|!)NDMH!E z+wM|F4{D#PsHJyni4hv=_Rr_f5u3Bc9f~`C3qY}|H!FCeOd^u%!RiKj+U%!q^gGR{ zla|_PJT@5fmVcg|uGGLWFBa4l@bl!)hpVVAN1eKigDgD7%ZKbLcGnvelD?KwhN+il-8_C9c zk&nJ3*5cD>R~jCfJ7+~d)3=nQk4eosEYO8lHP%6##t5%5m02RnQ711K?I7?Xiy%X@ zV~mM}@#lFZeXXNtS@CV>8{hR16_H0PB>tlN~L>$2_%{^h`y|X zHbj@+DN?I9p;Hm(U_-H(I`GZfI$(_TDezV=Ac(P}hfxM_e*UO>CXbdHpQo7pg(*0m zIPhtQOulwZ3QNd{br ziV<;FW6dd=+LI?Dn_3R%8Hnvc=jn1Ghifa&n#OHc6Wpt*e!xLqYxf9>H@saucyBU$IE-*WY3P*azt?cd!O{>3i}=-tqKnmO1%lcU zjO31sh+Yf8$5Q#E%>-IgP`zyXtb;xZI~A(g&iRh4>!;1IgD{U+i4i^gfxLEyUwfox zJ2q#V)ne?C$>wqwI(pHFLD=FjI{G7aC0f{G59$Zq&Cg-{DQ}d_jg}<#FQpMmh$NSP zUAGRapriD1GWm2SrMe1b#_6dFFw$sTBEkZqc}*jJ48HB&Q?i(yd2u* zNuun!=vFBSSoPu!*>75gUiEfGIyR3Qp1^Oz>(oeCIkp)4x#ip99XAuzqo3_@hBwpw zeJp|)2o5lVde`={E>W!WPslj?el%61kvm7IpIF#fXwDu^0+9=1**xDU6?NB+rcx@4|Na@j&D`{gw~EQ)e&7 zV%bhHpD)#zW4X)LVLM~TZDBhy#EZ`b0H5h|2_LJgn!@TW_;>Ezb1omu@09IxifHL5 zZ>JyYvrwg9vEc_&c(2k-M1J$y=XbCL%w0%wnh?%2ym&@#RTU|xWJOaOtH8DXC<6={ zY7LHXt6H)}udxv*v&R)=#ky=bDX?B}TXYp?r26?NGQDW$iIdP(-2)o+l3z?2J3VM8tiX`MOj$!fD`*l*ok3N)$NrDJ>bHkJfspQi2sxs~%o3*#{o%emx?S+OkGa zOXh1wLbF+oQ-VOQ9n#WJX6Y2kymsn9$D@bn7)xcBsaB8AguZM;$fzA0&$2@uDcfI* zzN&P}jcO$JVucf;YQSsUnl!txo)gVS;zK0Al z>s68PJVE9^m^%TB@#tvfJQ^XE`jV=XtqeB&Q48c~6|P>UTY2se?r1XwvD6Cj?X+QX zSRa)Hr?1Uu2i*1YtIFpteZd@s`stuO{7h9A*T3MYj%r#dfeQ7Ku&5`=H>u zzR(V$*$U}ZLLWFm(Q@q9WPqXSwNdMW>#rp;iyqUAtu>b4C1l#nPo%$jJ<$P(@Jmofv!$bQ7fkgkPF{cKh`*&W5!Rnq6kd2@Pa zDhdq^r=G1YcWeiiw9k%o`EFS|cwP@X9?SD}kI~Q}3tWV4Q*m%dg06!(#q07!B5kC+ z=~Vj?v8dU4UF>dqrdJ=P-df<8YQe@!1t$}JiednIE)l2&BeI?lsIif&G@)xQT$H0` zy*~8HPGJvv!p5)mj6T7}{|M5y5aURCNknXnnX$lvkN<@W)$9KAV{)(y04Z_foL#*` zgVV&tOkB}Mu_5~+0y{~K!U!k$9Lq4XpEq_$myx(Ft)gn~9)@0Zn)&MnmS$?vsK_E! z<9jXOeQd{S8eBvSCRcK|5`~V+zVPMO%FV6Wb!SDCwr>0Y;5}UevtN2pVVdu2lBiuI z1`jHt^nw0>Z&p~^w9jAu?Ip-IdoJJc=(2nI$h;hzj`Wgri97~d zogtr+Qhxh!hbZY1TY-wXxSkdnc672yk>l+H#F9T>F(}n~`;U~8#RuTf+|bj+SDqw^ zaOp#ju^>hVve%=hA1#AcmJ3ObWC6lA1s!ezId2__(!@I#lX*WIqhvCHK4@(U0R;9!M$C>LNq+7&Rn;vd72g8As28EoyM9_2DCKvzdFO7k>bZ zB4r#HR4`!bh64m%6lFni_Xo`36@g&tLg6)YkDp*K1S55Q-z+~N?pdJh&5Mz|m^89&(y&2k-K?PLxrM?z( z&+ZGUt(ooj1fHsyAWvJ(thinapENFjDYJu&kqs-Ys{0D3bJ9U&YiS5;A1^6y_yzw2 zrR9iGJh2w@$+eV~=t*Hf^m%BZqamhUBmQObgN}h*viMMUW?O!o4~5@pyp>^ci3db| zyoCX8)%8~6Hn|^*YDPWNW;zvxaRmO{orT`l)}=SH`0Yot=>f#nEK|>9AaxrU!sB}* z;{xf9hIj{8o(^TzSYFQyvDJ4+vm3fUrN*0Fd90UEG?2E%J38d^CL%edM9~zp7KcTo z+g;|z%dK>H9A8I02k<{!F%~j)5t0*>w>?x=dfzTcA4|=HS0|>21f(}K#5GxAtdl>i znVRZ4-^kjSo6k$w4$7zrvIst}fA-I0X6>InT*wjapEQiU_}BT%Ax*=BF%1o|e=c#TK#H13Vtk85_W#%h-VdkkOdE5W*0 zS%7U`?|eL zWh6rZjoH+-Q9q{_fAAO z9DT^ZCq93E7lm99#~Cu1hFUYcF8UEWx|RtmP3Df}9P{x=&fRHu0T$0)N84J8Y){F- zissHw?54(TFHzaw%t4oAC*RUTBjkEfvYfKi160p2_>M=Bvj-#d^>zP4XD;cRf(xm@ zc|lDJWESk8pLdzS>5W^}DD^r6r(bwaFlm>~_(Q?Tt)0QOM<*TftVf2y)tSe`t99<< z2$PlqLz8v*Y>^3%#cQW)51fc?+v|SO*lA^ULp?TvzLGsGqKeYk?mpYKImpR)?w$Pf zqmZF!uZODxXH-&abhH-D?qjV{;Z0qw6Tu^jH9x_lueBM#nRHIyGEuhSk$BEZYwc$7 zycZ6Ua9X)kfc=-mvZxr-AGNrJaa=9+ym79ML`Erm^7e9Cit5_EVMlIVlJ-Ow`Bb66n#xHB6li%m zOu2xT5`2KZijL{h?Jjfvx0nxZO>e4`8sEyK@Jz_J*Hj1zm+Tbp-W)7DpV5t`-kdL# zwlpoBcjGJ#d`eqV&+s#?z$qa3fr>^?3bwt7i~uV(`2Or_TP1xAZ>iQGj`0w3M!W6ug`} zaLi(0KbZD({ZM-Er|AbUv_E|wI=j=@UFNhez7Slku=jv|XpWkmsz2Sgjhl@r#-310 z{`Ok2V=~(MN<(k@dv+ntrQ-VZV~(|>LJhD3N<}b|W_eVYV3gaNE3#h6nJtlfn|O0V z%|bh0)oM6FDDdh2$vQO+5-pl*4YAQ>ms|BP$CO)@s-2caHo0?`Lq3kvjC&Q(jVO!! zhGSYsG0^$bbEaispdL3GOzXVtoY?qu8;#d$KQgr|*afo8jb?~pMPs8X5ZnW|aW|^CPao3?V#|*^Iq-Oc* z5nT7T_wRk_5tQj|CUxr>sY#;mZbogj6jO6&#aLDa371*(J_WXx#KwI6cIhqR#*sqJ z3qDT4d>5#K)mF{%qUY!Bl)N~MG$7_dy_r3_D|n+m}al;EslNLE@$r+ zQoKjmcUlu3PK1ex5rmZPrPC|2Mc#YFK(~V*NM3ikSGQ{^4lXL+58NT2)dP-#d`&eK z%JWyPa!xE&BW{c_2=?9|+MMXyMYlKEw!{M0bS>;v7Ub;|-_DL^M5IBZMdb1~GdV3L zvuva$>p!f5*xYFvp1*0ndTr#*!EaC0E8SbyHJ8_zG)tIJO>;?`KRCCgF-XXjEnQAN z#+DWTgZ7wd5VpfXEEcsQKXx4$n7-m~63iuB4&1x58`4XWY3C7+x;~9DAbKBf@NBKE33)K% zwQHl{6IY( z>N#Z9-tQ=5^>{s0&y7Jkx;D*b;dDP#sv5QJM>pL9ObqB=eRW1vZ(;O)Wp=~YY+8m= zzokL^BY3f;f_EeQvZ5OAHOL^ANTwTD8you>jwsV@iqa-TNZPQHIaawGFs;!+hkIX5 zjH`Fz$PapqF>!3iA;U*d);TFRf$%adaiG4NbL@S6osV`G()Et$(VYH%DdZR1{&L)`m7K zMPRQIyVnV>75IsVJNk!KD+n6vh~HVRJ#1F8V0#a({!&=bMi1q5LMthRO-kxEKyUd* zoawd_w_2H%^61F7(IMQuk_W3C9ki))eJMKwdC9mUQ1=rGF z(22ws7km}-(Ke&e6mDU!hYAN`OLtWAkuLq(82-R{Z4a4mr$^1dWR$y942iXTmw)1L@{sKQj`MXMYRG$J55D6^NSx6Qk+TCdY zaBvHcmq`fk%X;tg%I-UxuNi7f%4kIen?7ik<5ea-W2+NAGlR5SYdL-e0dj1@3d?qv zKURx7*S%Ep5!32*;&=%cG;h+xb7DU1O1rHJT2lN;m9 z)mw@#dL`}8WYD!M_JI^Z)2Ja#kcT%NK*75I8whh6V&F<%{zcUrNI4D}DO{G0eMLrh zS*F3$+`B%=v|aic_WbkWp=GW&I`5VJcE`Nik8sLz0Yp+Up}pYgm!gm^t(0r_8Yn~& zZv1-w0ubr&uvg!lsrF&|nJPQ;z%zYXBD~KXi?iuZRd(g8H+BV&x5O=}o&1eJ6CCMK z=OGQ`%}9}GV#i?R2Bkv^uI_=69Ke*_^3vOIQX~26pmtrGk7VmRz37fW5Wk>mS zj6;Q*=g;*CVO)$*7S@^^?LRk9@x@=W4Rh}v4OH+p&8zdWB~CAH7$y8n<3vOkKOMi` z;8iosytqZ(+ysexOGkPQ)R-k1GMt_&Ou7l=x=9j}E)wbZ2Wq^`Zz)grXRyUND(-jvKOIB3J6?-ErG0_2b!zSakgJN`{5Dy z?tQpn-1Gduh){!L=7#6L~G?>-$JEQaFiz z!Z#^!nXE9Y-T5u&XEjA%gf}3b#Ff+o@_-$DEANuaOw3Cm^}?#vT=VOYIKd? zFbC4tsrw}#5nlRIu9rrgdGCBW*r@pdt=9h}5O~iXFDiZ7Z|CKg9h z<8(V#wuv$X&n8wKul(@n1yH|gf+1I=h>6~p9N%9~mQ7eM^I z6|6%G#(*-TApftpmgyD8=ttgJH!%24Eu7Syb}fi> zROuQJ&ICjDy@YsID=r%2A>Cd=0Nbk3MI+NH?}dk77unuPKv34idhm;<{QgKRKe<>u zy8CESHuhGd>Z)dmU9$7WW?*UGQQ1^v->G8v>}P$?qBF2|X%o8cE1iC4$F%=6d+z%N zd$lYGMA4GuFf-3>V8?MD@ge`|j;w14^_sEE0O1~8=ZGsCAS@fLAN;@u;PL@e(S_SD zl9oXW(q2mx4my)9%dsZVc7`aeX{yQ}-QqKjrhd=Y#ISNo2Q#Bo)# z!0vnd*wYY=W?f~kuc%%}Q`hQ;oCeKqw)Zzb%`49ytmlTnDVeZ@z*pbQpM+}3YEKF! z9nlx(x=oprp6ru@W^ca&8Co1ZNJa!d&u-Yeya^1UimgF}_cA=$GHtQs50AtlSwpv; zn))lsSZ(Q$>g0~Amm>{quO|TQZf6tB8ybShHl=POTfBP%gu5GF!MbUc(Wm;zTbHr3 z>sscZvL3eO<4+iCS=4?vKJ8KN23Ceyg6!M{XXh$}_p@AHhRT5e*_$&z)`8dCAceq& z!cNfO%iGO6l=#BeV57o5Oyz>6%v7%HQiZY6<{jTGn=X)SHvkkAjj~sh@y8g&WlFst zbq$JjNAkNN4Y_YSZ$Ni`7%l7+RStaOlTTn;*1LVN>d(x`%jjsWMN~a}ai9Mf9JKO!ocb|nEUB&=7{qCGu7^Sakq`&j!8 zoCxrSR+!&S(|yP|9KyW6i#lWP*MAx(KJ#m23GM%>A?j&FC=A*TbIP)Z&|T`QlHFv+ zCS;^CU2op$Ng%~;rS31MGu_r??r}y!k|rRQjPIYgVu6ijvJ2!J#6Xe}t!IAZ!l%Nt zv*mk2&W}ezs-ub_PDcF@pAtmF=Es3LZ0W~F8BU)1h%tbFOL=iii3O22Z}HFU&lxqn_k^$;oB zUOUm^dNdt-a;*iH5_juNRh4f@v(t|cX|}xH6?Qh{b!IVT zH`;|2qUCSh8(2e(@;hUbkmu9v8%0@M($4P2S45pr+U;vc^>$VPWuJE=yLmn|cKVioRQ4{dgJUjdDkQ0K#BHm7;hV-M_Zzy!{SAOp`7BwM54F_*}u) zm#9(_Nug#ZFZaRd+&@WoQF}Y!)P5ODwzdd!^e>kqtjAd?T)2;hiUm!-CPjV+OilgX z(6jhK)Tuao#KQ$TENdWOk%R;#SyoNy@X0(%ThnzdDx~;_&6$*9X?4z)i(&?c{aVk| z7EfJSw$oeA?VrU+d(b?Rs#98uct(@2>&yczD6pFI$}X}XA@yG2Cv|62@0kb-?TN+~ zoN3zxc#ON=TPP&nA!+m`XVf47F1@)d;2Zg?bvVSD>wz-%rIp0vn?XOD{36*Z8VUKz zLQip5XTFYrKe zaRDx<W}$Z;$I{nVt@h1Fb5DaCKgLzcl){im6a|4nK#yvj zK&z1xH=<9kK>28Om2$iQ*t=%zFHAJ__M|T-Th0XfI(Fv1+mLS|tY4IjsrVRjeUS1M zww01Fi`*+I#pEtUkulZH?>mKb3b5oCP9*PGUx{bGV3bZpe69BNoz^?882ME8Ww-j9 zzA9Rdc~i91p^b02VLDuA$FIGCju#rewq?;MdicW_{fb`wTG-||z>69cf(U6URi4*A z$QjnSJ5fKidK>0mNa+xCO1`psK)h?(+vU8Q7J6tituB_SFge>AZp&j`mDjB+3;UK! zF`GVtb0o<2MRkh<+%w)cbn>8IU3S|v_MU@xgz+GqRaTwL7{0!?swqu7qfRlS1JBfc z4qI~Bi91bA$EeIH1Nc7g5@Mk-o#)mbCSr|?>&hXW!c5Rt4V^X9{#L|nb$vEfDIw!P zwY}~z;>9#aG=H9PqxxY?aKN$*QG@l={2@iI-nyBUNWfQz6VF>~y&t^2r$dTW-3tvZ+4!pp^}j4QgaQ(aZ2Y5qQLf30J-m%=U9u$NMm zB~tRBp!`9>)@ofb)AQqWnw34XVr>3N%9GJB&H+6VEHGw-l@p%M@A>PX>5cVdm{nf$ z-tzJOE2jKlou8$M*i^=XoFI!Op;u`xfS4|OHLZ#Sj_o9upzb#*w!S-?(W%x|g-FmQ z5>bX-4v4`jJD8eg--u=XWoUUw@7RKmxpT_Pbu)4ThzO$6T(h6Z`>VG8%c~tU+>I7^ z2`vaC3ef&hD_w$`XB1}r;w=qrR;Pa;gXsJ#tfo+^D(#PS>-kV{<@AVWQKkg=Dsp3Y z9mK-$L;-hX(^l?Zd$zt7zS=MlfS!e?`e{9BF&$E<+&}T1#PTTx{k)T%Hau6Z){9AT zZBKNhVJcVeHaHfEl)GHe z*1r=Gmaz<0$bVxZ?^b#MpFQzXi}Tx(GxnW=rTz)Z??@)`l=xs%Y;stUv^H=s_7&PY z^=-*aD{R{qI4G!5aQG`GfV%vf`g7?&1C<Kn@a_ia z<>Ar~dMkq|qG+3w%(j=4`ni)L(UmImHXaVF;o$Q^c>qI)fvX*A8W*S zZx-(wQ(NWEq@p@AA?B->B&vk2z?5h;Vw3agIN@hFcU9F6g3mGz-FRj@h{7q)uezW` zF;-IDzDWSjrW*hbSAo9rQ+gd2BDo{km@HEEDn{c*}1SMEwmKOL(%NLlB8&E1k$`40Z#6twTeL! zjynH`jp9Y)kto;s9MKd;@5;IycE@g^Jt7?rn_3?y9~Kw9jrOkiGO-ClWe+(}lM+O@$&V-0hW828MKJ+>^Ao$vLykBru@W&rK%I{K2= z#bpd~`)?;Ov=|Iv4cwa<0TQdRm{&oWm`EVJ7V>EQ(--CKsYt>i-WRua>B2JW^WNAE zDRXYtcJg@l?Hmf-hq8Phbdn#1OwAe_y%U391jQ3HhQ8}zor;^Mz&ke|<2QY$C;l`b z^tqC=#OIBuZotB$@64snjhoF#^3p6<|L}3Es=dtT)Hgq=XA9KmW&m>+;-;@(#(Ya> z{hc&F!l#bDK=`%B?b+a!W`KB%m4sdh>jrtKM7ieQXpU}afGw4)Ln|#xjc8V`Ujn(a z2Ku^JAky+0TYrbxf*;VHA) zSv$CTuz2wC7(EA|`YVq=0Prsg*Ib_)YI!~x|JHEZ9{^HQ8j0*NYiunyoF8kkL|0hD& zGvJ>*|1d<}vww8@??N+*`U|M@2eUsFt@~#`DSp@GPE3DZc7lScA%ub=_*Xyww6KCG zD6k$H@o)3`v%((@iv82TasMNn-v;gz|BnX$FH`>mNcx}Z`n#tL|8Jnb$@vdM`>xAzW)O{?oY>7`OUIXr;q)6kogV$;O_-k_97BlESw5LWAij1t|VD4aSCo yBKw<^cl@Biv9Bn9`^@v~`1fD}CZHmPeF%gG3s7;OQ$SHbji5q7bq4)*vHu6pJXu%( delta 4047 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 diff --git a/documentation/pvDataCPP.html b/documentation/pvDataCPP.html index b94284f..1823fa4 100644 --- a/documentation/pvDataCPP.html +++ b/documentation/pvDataCPP.html @@ -37,7 +37,7 @@

EPICS pvDataCPP

-

EPICS v4 Working Group, Working Draft, 01-May-2014

+

EPICS v4 Working Group, Working Draft, 08-July-2014

Latest version:
@@ -46,11 +46,11 @@
This version:
pvDataCPP_20140501.html + href="pvDataCPP_20140708.html">pvDataCPP_20140708.html
Previous version:
pvDataCPP_20131023.html + href="pvDataCPP_20140501.html">pvDataCPP_20140501.html
Editors:
Marty Kraimer, BNL
@@ -78,7 +78,7 @@ V4 control system programming environment:

Status of this Document

-

This is the 01-May-2014 version of the C++ implementation of pvData. +

This is the 08-July-2014 version of the C++ implementation of pvData.

RELEASE_NOTES.md provides changes since the last release. @@ -141,17 +141,25 @@ be much easier to understand.

The documentation directory for this project has a file examples.zip. It has the code for the examples. -After it is unzipped just edit configure/RELEASE.local and then execute make. +After it is unzipped:

+
+cd examples/configure
+cp ExampleRELEASE.local RELEASE.local
+#edit RELEASE.local
+cd ..
+make
+bin/linux-x86_64/introspectMain 
+bin/linux-x86_64/dataMain
+

The examples assume that the following statements have been issued:

-std::string builder;
+using std::cout;
+using std::endl;
 FieldCreatePtr fieldCreate = getFieldCreate();
 PVDataCreatePtr pvDataCreate = getPVDataCreate();
 StandardFieldPtr standardField = getStandardField();
 StandardPVFieldPtr standardPVField = getStandardPVField();
-StructureConstPtr alarm = getStandardField()->alarm();
-StructureConstPtr timeStamp = getStandardField()->timeStamp();
 

These provide access to most of pvData:

@@ -186,9 +194,9 @@ It uses only createField. names.reserve(n); FieldConstPtrArray fields; fields.reserve(n); - names.push_back("secsPastEpoch"); + names.push_back("secondsPastEpoch"); fields.push_back(fieldCreate->createScalar(pvLong)); - names.push_back("nanoseconds"); + names.push_back("nanoSeconds"); fields.push_back(fieldCreate->createScalar(pvInt)); names.push_back("userTag"); fields.push_back(fieldCreate->createScalar(pvInt)); @@ -202,10 +210,9 @@ It uses only createField. topfields.push_back(fieldCreate->createScalar(pvDouble)); topnames.push_back("timeStamp"); topfields.push_back(timeStamp); - StructureConstPtr doubleScalar = fieldCreate->createStructure(topnames,topfields); - builder.clear(); - doubleScalar->toString(&builder); - cout << builder << "\n\n"; + StructureConstPtr doubleScalar = + fieldCreate->createStructure(topnames,topfields); + cout << doubleScalar->dump(cout) << endl;

Using FieldBuilder the same can be done via:

@@ -214,31 +221,55 @@ It uses only createField.
         add("value",pvDouble) ->
         addNestedStructure("timeStamp")->
             setId("time_t")->
-            add("secsPastEpoch", pvLong)->
-            add("nanoseconds", pvInt)->
+            add("secondsPastEpoch", pvLong)->
+            add("nanoSeconds", pvInt)->
             add("userTag", pvInt)->
             endNested()->
         createStructure();
-    builder.clear();
-    doubleScalarHard->toString(&builder);
-    cout << builder << endl;
+    cout << doubleScalarHard->dump(cout) << endl;
 
-Both produce: +

The easiest way to produce the structure is:

+    StructureConstPtr stringArrayEasy =
+         getStandardField()->scalarArray(pvString,"alarm,timeStamp");
+    cout << stringArrayEasy->dump(cout) << "\n\n";
+
+These three ways produce: +
+hardest way
+structure
+    double value
+    structure timeStamp
+        long secsPastEpoch
+        int nanoseconds
+        int userTag
+
+hardway
 structure
     double value
     time_t timeStamp
         long secsPastEpoch
         int nanoseconds
+        int userTag
+
+easy way
+uri:ev4:nt/2012/pwd:NTScalarArray
+    string[] value
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
 

An easy way to create a structure with a string array value field and an alarm and time stamp is via standardField:

     StructureConstPtr stringArrayEasy =
-        getStandardField()->scalarArray(pvString,"alarm,timeStamp");
-    builder.clear();
-    stringArrayEasy->toString(&builder);
-    cout << builder << endl;
+        standardField->scalarArray(pvString,"alarm,timeStamp");
+    cout <<stringArrayEasy->dump(cout) << endl;
 
It produces :
@@ -252,6 +283,7 @@ uri:ev4:nt/2012/pwd:NTScalarArray
         long secondsPastEpoch
         int nanoSeconds
         int userTag
+0x607188
 

enumerated structure

An enumerated structure is a structure with two sub-fields: @@ -261,26 +293,24 @@ is an enumerated structure and additional fields. A hard way to create an structure with an enumerated value field and a time stamp is:

     StructureConstPtr enum_t =
-    getFieldCreate()->createFieldBuilder()->
+    fieldCreate->createFieldBuilder()->
         setId("enum_t")->
         add("index", pvInt)->
         addArray("choices", pvString)->
         createStructure();
 
     StructureConstPtr ntEnumHard =
-    getFieldCreate()->createFieldBuilder()->
+    fieldCreate->createFieldBuilder()->
         setId("uri:ev4:nt/2012/pwd/NTEnum")->
         add("value", enum_t)->
         addNestedStructure("timeStamp")->
             setId("time_t")->
-            add("secsPastEpoch", pvLong)->
-            add("nanoseconds", pvInt)->
+            add("secondsPastEpoch", pvLong)->
+            add("nanoSeconds", pvInt)->
             add("userTag", pvInt)->
             endNested()->
         createStructure();
-    builder.clear();
-    ntEnumHard->toString(&builder);
-    cout << builder << endl;
+    cout <<ntEnumHard->dump(cout) << endl;
 
It produces:
@@ -289,17 +319,15 @@ uri:ev4:nt/2012/pwd/NTEnum
         int index
         string[] choices
     time_t timeStamp
-        long secsPastEpoch
-        int nanoseconds
+        long secondsPastEpoch
+        int nanoSeconds
         int userTag
 

The following is an easy way. Note that it has two additional fields: alarm and timeStamp:

     StructureConstPtr ntEnumEasy = getStandardField()->enumerated("alarm,timeStamp");
-    builder.clear();
-    ntEnumEasy->toString(&builder);
-    cout << builder << endl;
+    cout <<ntEnumEsay->dump(cout) << endl;
 
It produces:
@@ -322,19 +350,15 @@ uri:ev4:nt/2012/pwd:NTEnum
 
 
     UnionConstPtr ntunion =
-    getFieldCreate()->createFieldBuilder()->
+    fieldCreate->createFieldBuilder()->
         add("doubleValue", pvDouble)->
         add("intValue", pvInt)->
-        add("timeStamp",timeStamp)->
+        add("timeStamp",standardField->timeStamp())->
         createUnion();
-    builder.clear();
-    ntunion->toString(&builder);
-    cout << builder << endl;
+    cout <<ntunion->dump(cout) << endl;
 
-    StructureConstPtr unionValue = getStandardField()->regUnion(punion,"alarm,timeStamp");
-    builder.clear();
-    ntunion->toString(&builder);
-    cout << builder << endl;
+    StructureConstPtr unionValue = standardField->regUnion(punion,"alarm,timeStamp");
+    cout <<unionValue->dump(cout) << endl;
 
It produces:
@@ -364,38 +388,50 @@ uri:ev4:nt/2012/pwd:NTUnion
         int nanoSeconds
         int userTag
 
+ +

union array example

+

The following:

+
+    UnionArrayConstPtr unionArray = fieldCreate->createUnionArray(
+        fieldCreate->createVariantUnion());
+    cout << unionArray->dump(cout) << "\n\n";
+
+

Produces

+
+any[]
+any
+0x607188
+

power supply

The following creates a more complex structure:

     StructureConstPtr powerSupply =
-    getFieldCreate()->createFieldBuilder()->
-        add("alarm_t",alarm) ->
-        add("timestamp_t",timeStamp) ->
+    fieldCreate->createFieldBuilder()->
+        add("alarm",standardField->alarm()) ->
+        add("timestamp",standardField->timeStamp()) ->
         addNestedStructure("power") ->
            add("value",pvDouble) ->
-           add("alarm",alarm) ->
+           add("alarm",standardField->alarm()) ->
            endNested()->
         addNestedStructure("voltage") ->
            add("value",pvDouble) ->
-           add("alarm",alarm) ->
+           add("alarm",standardField->alarm()) ->
            endNested()->
         addNestedStructure("current") ->
            add("value",pvDouble) ->
-           add("alarm",alarm) ->
+           add("alarm",standardField->alarm()) ->
            endNested()->
         createStructure();
-    builder.clear();
-    powerSupply->toString(&builder);
-    cout << builder << endl;
+    std::cout << powerSupply->dumpValue(cout) <<std::endl;
 
It produces:
 structure
-    alarm_t alarm_t
+    alarm_t alarm
         int severity
         int status
         string message
-    time_t timestamp_t
+    time_t timestamp
         long secondsPastEpoch
         int nanoSeconds
         int userTag
@@ -428,6 +464,8 @@ structure
         doubleValue->getSubField<PVDouble>("value");
     pvdouble->put(1e5);
     cout << doubleValue->dumpValue(cout) << endl;
+    double value = doubleValue->getSubField<PVDouble>("value")->get();
+    cout << "from get " << value << "\n\n";
 
This produces:
@@ -442,12 +480,13 @@ uri:ev4:nt/2012/pwd:NTScalar
         int nanoSeconds 0
         int userTag 0
 0x607268
+from get 100000
 

array example

-    PVStructurePtr doubleArrayValue = getPVDataCreate()->createPVStructure(
-        getStandardField()->scalarArray(pvDouble,"alarm,timeStamp"));
-    PVDoubleArrayPtr pvDoubleArray =s
+    PVStructurePtr doubleArrayValue = pvDataCreate->createPVStructure(
+        standardField->scalarArray(pvDouble,"alarm,timeStamp"));
+    PVDoubleArrayPtr pvDoubleArray =
          doubleArrayValue->getSubField<PVDoubleArray>("value");
     size_t len = 10;
     shared_vector<double> xxx(len);
@@ -478,8 +517,8 @@ via getData 0 1 2 3 4 5 6 7 8 9
 

enumerated example

-    PVStructurePtr pvntenum = getPVDataCreate()->createPVStructure(
-         getStandardField()->enumerated("alarm,timeStamp"));
+    PVStructurePtr pvntenum = pvDataCreate->createPVStructure(
+         standardField->enumerated("alarm,timeStamp"));
     cout << pvntenum->dumpValue(cout) << "\n\n";
 
This produces: @@ -501,9 +540,9 @@ uri:ev4:nt/2012/pwd:NTEnum

power supply example

     StructureConstPtr powerSupply =
-    getFieldCreate()->createFieldBuilder()->
-        add("alarm_t",alarm) ->
-        add("timestamp_t",timeStamp) ->
+    fieldCreate->createFieldBuilder()->
+        add("alarm",alarm) ->
+        add("timestamp",timeStamp) ->
         addNestedStructure("power") ->
            add("value",pvDouble) ->
            add("alarm",alarm) ->
@@ -517,7 +556,7 @@ uri:ev4:nt/2012/pwd:NTEnum
            add("alarm",alarm) ->
            endNested()->
         createStructure();
-    PVStructurePtr pvpowerSupply = getPVDataCreate()->createPVStructure(powerSupply);
+    PVStructurePtr pvpowerSupply = pvDataCreate->createPVStructure(powerSupply);
     cout << pvpowerSupply->dumpValue(cout) << endl;
 
This produces: @@ -555,10 +594,10 @@ structure
     PVStructurePtr pvStructure = pvDataCreate->createPVStructure(
         standardField->regUnion(
-            getFieldCreate()->createFieldBuilder()->
+            fieldCreate->createFieldBuilder()->
                 add("doubleValue", pvDouble)->
                 add("intValue", pvInt)->
-                add("timeStamp",timeStamp)->
+                add("timeStamp",standardField->timeStamp())->
                 createUnion(),
             "alarm,timeStamp"));
     PVStructurePtr pvTimeStamp =
@@ -650,7 +689,7 @@ uri:ev4:nt/2012/pwd:NTUnion
 
     PVStructurePtr pvStructure = pvDataCreate->createPVStructure(
         standardField->regUnion(
-            getFieldCreate()->createFieldBuilder()->
+            fieldCreate->createFieldBuilder()->
                 add("doubleValue", pvDouble)->
                 addArray("doubleArrayValue",pvDouble)->
                 addNestedUnion("unionValue") ->
@@ -671,10 +710,8 @@ uri:ev4:nt/2012/pwd:NTUnion
                     endNested() ->
                 createUnion(),
             "alarm,timeStamp"));
-    builder.clear();
-    pvStructure->getStructure()->toString(&builder);
     cout << "introspection\n";
-    cout << builder << endl;
+    cout <<pvStructure->getStructure()->dump(cout) << endl;
     cout << "data\n";
     cout << pvStructure->dumpValue(cout) << "\n";
     PVUnionPtr pvUnion = pvStructure->getSubField<PVUnion>("value");;
@@ -728,6 +765,7 @@ uri:ev4:nt/2012/pwd:NTUnion
         long secondsPastEpoch
         int nanoSeconds
         int userTag
+0x60a2c8
 data
 uri:ev4:nt/2012/pwd:NTUnion
     union value
@@ -848,10 +886,6 @@ copying data between fields.

standardPVField.h
Cteates data interfaces for standard data structures like timeStamp, alarm, etc.
-
printer.h
-
TBD This is not documented. - In addition toString and dumpValue should be reviewed. -

pvType.h

@@ -876,10 +910,9 @@ typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; // float and double are types -typedef std::string std::string; -typedef std::vector StringArray; -typedef std::tr1::shared_ptr StringArrayPtr; +typedef std::vector<std::string> StringArray; +typedef std::tr1::shared_ptr<StringArray> StringArrayPtr; inline std::string * get(StringArray &value); inline std::string const * get(StringArray const &value); inline std::string * get(StringArrayPtr &value); @@ -887,10 +920,8 @@ inline std::string const * get(StringArrayPtr const &value); } inline StringArray & getVector(StringArrayPtr &value); inline StringArray const & getVector(StringArrayPtr const &value); -typedef std::vector::iterator StringArray_iterator; -typedef std::vector::const_iterator StringArray_const_iterator; - -typedef std::string * StringBuilder; +typedef std::vector<std::string>::iterator StringArray_iterator; +typedef std::vector<std::string>::const_iterator StringArray_const_iterator;

where

@@ -918,9 +949,6 @@ typedef std::string * StringBuilder; which is a std::vector<std::string>. This is used by introspection. -
StringBuilder
-
This is used to implement semantics similar to the Java toString - method. It is being replaced by the C++ stream semantics.

TBD

@@ -929,15 +957,8 @@ typedef std::string * StringBuilder;
 typedef uint8_t boolean;
     
-
StringBuilder
-
Should this go away? - Note that it is used for both introspection and data. -
printer.h
-
Not documented. - How should printer, toString, and dumpValue be related? - Also limit number of elements to show if array is large. -
+
Not documented. Is this needed? Nothing currently uses it.

@@ -975,8 +996,7 @@ transported over the network.

class TypeFunc { public: - const char* name(Type); - static void toString(StringBuilder buf,const Type type); + epicsShareExtern const char* name(Type type); }; enum ScalarType { @@ -995,9 +1015,10 @@ public: bool isPrimitive(ScalarType type); ScalarType getScalarType(std::string const &value); const char* name(ScalarType); - void toString(StringBuilder buf,ScalarType scalarType); size_t elementSize(ScalarType id); -}; +}; +std::ostream& operator<<(std::ostream& o, const ScalarType& scalarType); +

Type is one of the following:

@@ -1054,8 +1075,6 @@ public:
name
Returns the name of the type.
-
toString
-
Convert the type to a string.

ScalarTypeFunction is a set of convenience methods for ScalarType

@@ -1074,8 +1093,6 @@ public:
getScalarType
Given a string of the form std::string("boolean"),...,std::string("string") return the scalarType.
-
toString
-
Convert the scalar type to a string.
elementSize
Returns the size in bytes of an instance of the scalarType.
@@ -1159,10 +1176,10 @@ public: virtual ~Field(); Type getType() const{return m_type;} virtual std::string getID() const = 0; - virtual void toString(StringBuilder buf) const{toString(buf,0);} - virtual void toString(StringBuilder buf,int indentLevel) const; + virtual std::ostream& dump(std::ostream& o) const = 0; ... }; +std::ostream& operator<<(std::ostream& o, const Field& field); class Scalar : public Field{ public: @@ -1172,14 +1189,23 @@ public: typedef const Scalar& const_reference; ScalarType getScalarType() const {return scalarType;} - virtual void toString(StringBuilder buf) const{toString(buf,0);} - virtual void toString(StringBuilder buf,int indentLevel) const; virtual std::string getID() const; + virtual std::ostream& dump(std::ostream& o) const; virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; virtual void deserialize(ByteBuffer *buffer, DeserializableContol *control); ... }; +class epicsShareClass Array : public Field{ +public: + POINTER_DEFINITIONS(Array); + virtual ~Array(); + typedef Array& reference; + typedef const Array& const_reference; + ... +}; + + class ScalarArray : public Field{ public: POINTER_DEFINITIONS(ScalarArray); @@ -1188,9 +1214,8 @@ public: ScalarArray(ScalarType scalarType); ScalarType getElementType() const {return elementType;} - virtual void toString(StringBuilder buf) const{toString(buf,0);} - virtual void toString(StringBuilder buf,int indentLevel) const; virtual std::string getID() const; + virtual std::ostream& dump(std::ostream& o) const; virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); ... @@ -1203,30 +1228,42 @@ public: typedef const StructureArray& const_reference; StructureConstPtr getStructure() const {return pstructure;} - virtual void toString(StringBuilder buf,int indentLevel=0) const; virtual std::string getID() const; + virtual std::ostream& dump(std::ostream& o) const; virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); ... }; +class epicsShareClass UnionArray : public Field{ +public: + POINTER_DEFINITIONS(UnionArray); + typedef UnionArray& reference; + typedef const UnionArray& const_reference; + UnionConstPtr getUnion() const {return punion;} + virtual std::string getID() const; + virtual std::ostream& dump(std::ostream& o) const; + virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; + virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); +}; + class Structure : public Field { public: POINTER_DEFINITIONS(Structure); typedef Structure& reference; typedef const Structure& const_reference; - std::size_t getNumberFields() const {return numberFields;} - FieldConstPtr getField(std::string const & fieldName) const; - FieldConstPtr getField(std::size_t index) const; - std::size_t getFieldIndex(std::string const &fieldName) const; - FieldConstPtrArray const & getFields() const {return fields;} - StringArray const & getFieldNames() const; - std::string getFieldName(std::size_t fieldIndex) const; - virtual void toString(StringBuilder buf,int indentLevel) const; - virtual std::string getID() const; - virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; - virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); + std::size_t getNumberFields() const {return numberFields;} + FieldConstPtr getField(std::string const & fieldName) const; + FieldConstPtr getField(std::size_t index) const; + std::size_t getFieldIndex(std::string const &fieldName) const; + FieldConstPtrArray const & getFields() const {return fields;} + StringArray const & getFieldNames() const; + std::string getFieldName(std::size_t fieldIndex) const; + virtual std::string getID() const; + virtual std::ostream& dump(std::ostream& o) const; + virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; + virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); ... }; @@ -1247,39 +1284,23 @@ public: StringArray const & getFieldNames() const; std::string getFieldName(std::size_t fieldIndex) const; bool isVariant() const; - virtual void toString(StringBuilder buf) const; - virtual void toString(StringBuilder buf,int indentLevel) const; virtual std::string getID() const; + virtual std::ostream& dump(std::ostream& o) const; virtual void serialize( ByteBuffer *buffer, SerializableControl *control) const; virtual void deserialize( ByteBuffer *buffer, DeserializableControl *control); }; - -class epicsShareClass UnionArray : public Field{ -public: - POINTER_DEFINITIONS(UnionArray); - typedef UnionArray& reference; - typedef const UnionArray& const_reference; - UnionConstPtr getUnion() const {return punion;} - virtual void toString(StringBuilder buf,int indentLevel=0) const; - - virtual std::string getID() const; - - virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; - virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); -};
Constructors
Note that all constructors are protected or private. The only way to create instances is via fieldBuilder or fieldCreate. The implementation manages all storage via shared pointers.
-
toString
-
Many classes provide this (actually two methods). This method is called - to get a string that uses the metadata syntax described in a previous - section.
+
dump
+
Many classes provide this. This is a stream method that prints using + the metadata syntax described in pvDataJava.html.

Field

@@ -1318,6 +1339,12 @@ public:
This returns the ID[] where ID is the value returned by structure->getID().
+` +

UnionArray

+
+
getUnion
+
Get the union interface for each element.
+

Structure

@@ -1358,11 +1385,6 @@ public:
isVariant
returns true if this is varient array and false otherwise.
-

UnionArray

-
-
getUnion
-
Get the union interface for each element.
-

fieldBuilder and createField

 class epicsShareClass FieldBuilder :
@@ -1472,142 +1494,7 @@ description of the methods.
        End current nested structure or union.
       
 
-

A simple example is:

-
-StructureConstPtr structure =
-      getFieldCreate()->createFieldBuilder()->
-      setId("enum_t")->
-      add("index", pvDouble)->
-      addArray("choices", pvString)->
-      createStructure();
-std::string builder;
-structure->toString(&builder);
-cout << builder << endl;
-
-This produces: -
-enum_t
-    double index
-    string[] choices
-
-

Another example is:

-
-StructureConstPtr enum_t =
-getFieldCreate()->createFieldBuilder()->
-    setId("enum_t")->
-    add("index", pvInt)->
-    addArray("choices", pvString)->
-    createStructure();
-
-StructureConstPtr ntEnum =
-getFieldCreate()->createFieldBuilder()->
-    setId("uri:ev4:nt/2012/pwd/NTEnum")->
-    add("value", enum_t)->
-    addNestedStructure("timeStamp")->
-        setId("time_t")->
-        add("secsPastEpoch", pvLong)->
-        add("nanoseconds", pvInt)->
-        add("userTag", pvInt)->
-        endNested()->
-    createStructure();
-builder.clear();
-ntEnum->toString(&builder);
-cout << builder << endl;
-
-This produces: -
-uri:ev4:nt/2012/pwd/NTEnum
-    enum_t value
-        int index
-        string[] choices
-    time_t timeStamp
-        long secsPastEpoch
-        int nanoseconds
-        int userTag
-
-

The following example:

-
-    UnionConstPtr ntunion =
-    getFieldCreate()->createFieldBuilder()->
-        add("doubleValue", pvDouble)->
-        add("intValue", pvInt)->
-        addNestedStructure("timeStamp")->
-            setId("time_t")->
-            add("secsPastEpoch", pvLong)->
-            add("nanoseconds", pvInt)->
-            add("userTag", pvInt)->
-            endNested()->
-        createUnion();
-    builder.clear();
-    ntunion->toString(&builder);
-    cout << builder << endl;
-
-produces: -
-union
-    double doubleValue
-    int intValue
-    time_t timeStamp
-        long secsPastEpoch
-        int nanoseconds
-        int userTag
-
-

A powerSupply structure can be created as follows:

-
-StructureConstPtr alarm = getStandardField()->alarm();
-StructureConstPtr timeStamp = getStandardField()->timeStamp();
-
-StructureConstPtr powerSupply =
-getFieldCreate()->createFieldBuilder()->
-    add("alarm_t",alarm) ->
-    add("timestamp_t",timeStamp) ->
-    addNestedStructure("power") ->
-       add("value",pvDouble) ->
-       add("alarm",alarm) ->
-       endNested()->
-    addNestedStructure("voltage") ->
-       add("value",pvDouble) ->
-       add("alarm",alarm) ->
-       endNested()->
-    addNestedStructure("current") ->
-       add("value",pvDouble) ->
-       add("alarm",alarm) ->
-       endNested()->
-    createStructure();
-builder.clear();
-powerSupply->toString(&builder);
-cout << builder << endl;
-
-produces: -
-structure
-    alarm_t alarm_t
-        int severity
-        int status
-        string message
-    time_t timestamp_t
-        long secondsPastEpoch
-        int nanoSeconds
-        int userTag
-    structure power
-        double value
-        alarm_t alarm
-            int severity
-            int status
-            string message
-    structure voltage
-        double value
-        alarm_t alarm
-            int severity
-            int status
-            string message
-    structure current
-        double value
-        alarm_t alarm
-            int severity
-            int status
-            string message
-
+

Examples of using fieldBuilder were given earlier in this manual.

FieldCreate

getFieldCreate
@@ -1651,14 +1538,6 @@ structure
deserialize
Deserialize from given byte buffer.
-

TBD -

-
toString
-
Should support for steams exist.
-
ScalarTypeTraits and ScalarTypeID
-
These seem to only be used by shared_vector. Do they need to be documented?
-
-

standardField.h

@@ -1671,9 +1550,12 @@ following: alarm, timeStamp, display, control, and valueAlarm. An example is with fields named value and each of the property names. Each property field is a structure defining the property. The details about each property is given in the section named "Property". For example the call:

-
    StructureConstPtr example = standardField->scalar(
-        pvDouble,
-        "value,alarm,timeStamp");
+
+StructureConstPtr example = standardField->scalar(
+    pvDouble,
+    "value,alarm,timeStamp"
+    );
+

Will result in a Field definition that has the form:

structure example
@@ -1928,8 +1810,6 @@ public:
    void postPut();
    void setPostHandler(PostHandlerPtr const &postHandler);
    virtual bool equals(PVField &pv);
-   virtual void toString(StringBuilder buf) ;
-   virtual void toString(StringBuilder buf,int indentLevel);
    std::ostream& dumpValue(std::ostream& o) const;
  ...
 }
@@ -1988,21 +1868,9 @@ std::ostream& operator<<(std::ostream& o, const PVField& f);
     
Compare this field with another field. The result will be true only if the fields have exactly the same field types and if the data values are equal.
-
toString
-
Converts the field data to a string. This is mostly for debugging - purposes.
dumpValue
Method for streams I/O.
-

TBD - -

-
toString and dumpValue
-
toString is broken
- Should toString go away or be changed? -
-
-

PVScalar

@@ -2139,15 +2007,9 @@ int32 val = pv->getAs<pvInt>>(); int32 val; pv->putFrom<pvInt>(val);
- - -

TBD -

-
dumpvalue
-
replace this with ostream operator<<
-
-

+ +

PVUnion

A PVUnion has a single subfield. The Union introspection interface determines the possible @@ -2162,27 +2024,24 @@ class PVUnion : public PVField public: POINTER_DEFINITIONS(PVUnion); virtual ~PVUnion(); + typedef PVUnion & reference; + typedef const PVUnion & const_reference; + UnionConstPtr getUnion() const; PVFieldPtr get() const; template<typename PVT> - std::tr1::shared_ptr<PVT> get() const { - return std::tr1::dynamic_pointer_cast<PVT>(get()); - } + std::tr1::shared_ptr<PVT> get() const; PVFieldPtr select(int32 index); template<typename PVT> - std::tr1::shared_ptr<PVT> select(int32 index) { - return std::tr1::dynamic_pointer_cast<PVT>(select(index)); - } + std::tr1::shared_ptr<PVT> select(int32 index); PVFieldPtr select(std::string const & fieldName); template<typename PVT> - std::tr1::shared_ptr<PVT> select(std::string const & fieldName) { - return std::tr1::dynamic_pointer_cast<PVT>(select(fieldName)); - } + std::tr1::shared_ptr<PVT> select(std::string const & fieldName); int32 getSelectedIndex() const; std::string getSelectedFieldName() const; @@ -2227,6 +2086,7 @@ class PVArray : public PVField, public SerializableArray { public: POINTER_DEFINITIONS(PVArray); virtual ~PVArray(); + virtual ArrayConstPtr getArray() const = 0; virtual void setImmutable(); std::size_t getLength() const; virtual void setLength(std::size_t length); @@ -2238,6 +2098,8 @@ public: ... };

+
getArray
+
Get the introspection interface.
setImmutable
Set the data immutable. Note that this is permanent since there is no methods to make it mutable.
@@ -2377,12 +2239,6 @@ public:
dumpValue
Method for streams I/O.
-

TBD -

-
dumpvalue
-
replace this with ostream operator<<
-
-

PVValueArray

@@ -2431,8 +2287,6 @@ public:

TBD

-
dumpvalue
-
replace this with ostream operator<<
Check for completeness
Michael should check that PVScalarArray and PVValueArray have the correct set of methods and that the descriptions are correct.
@@ -2457,6 +2311,7 @@ public: typedef ::epics::pvData::shared_vector<const PVStructurePtr> const_svector; virtual ~PVStructureArray() {} + virtual ArrayConstPtr getArray() const; virtual size_t getLength(); virtual size_t getCapacity(); virtual void setCapacity(size_t capacity); @@ -2465,38 +2320,28 @@ public: virtual std::size_t append(std::size_t number); virtual bool remove(std::size_t offset,std::size_t number); virtual void compress(); - virtual void serialize(ByteBuffer *pbuffer, virtual const_svector view() const; virtual void swap(const_svector &other); virtual void replace(const const_svector &other); + virtual void serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const; - virtual void deserialize(ByteBuffer *buffer, - DeserializableControl *pflusher); virtual void serialize(ByteBuffer *pbuffer, SerializableControl *pflusher, std::size_t offset, std::size_t count) const ; + virtual void deserialize(ByteBuffer *buffer, + DeserializableControl *pflusher); virtual std::ostream& dumpValue(std::ostream& o) const; virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const; // inherited from PVVectorStorage const_svector view(); - void swap(const_svector& other); - void replace(const const_svector& next); + void swap(const_svector& other); + void replace(const const_svector& next); svector reuse(); ... } -

where

getStructureArray
Get the introspection interface shared by each element.
-
append
-
Create new elements and append them to the end of the array. It returns - the index of the first new element.
-
remove
-
Remove the specfied set of elements. It returns (false,true) if the - elements (were not, were) removed. It will not removed any elements - unless all requested elements exist or are null. Note that this deletes - the element and sets the array element to null. It does not change the - array capacity.
compress
This moves all null elements and then changes the array capacity. When done there are no null elements.
@@ -2525,7 +2370,7 @@ public: typedef ::epics::pvData::shared_vector&ly;const PVUnionPtr&gt; const_svector; virtual ~PVValueArray() {} - + virtual ArrayConstPtr getArray() const; virtual size_t getLength() const; virtual size_t getCapacity() const; virtual void setCapacity(size_t capacity); @@ -2551,15 +2396,6 @@ public:
getUnionArray
Get the introspection interface shared by each element.
-
append
-
Create new elements and append them to the end of the array. It returns - the index of the first new element.
-
remove
-
Remove the specfied set of elements. It returns (false,true) if the - elements (were not, were) removed. It will not removed any elements - unless all requested elements exist or are null. Note that this deletes - the element and sets the array element to null. It does not change the - array capacity.
compress
This moves all null elements and then changes the array capacity. When done there are no null elements.
@@ -2775,13 +2611,13 @@ class Convert { public: static ConvertPtr getConvert(); ~Convert(); - void getFullName(StringBuilder buf,PVFieldPtr const & pvField); + void getFullName(std::string * buf,PVFieldPtr const & pvField); bool equals(PVFieldPtr const &a,PVFieldPtr const &b); bool equals(PVField &a,PVField &b); - void getString(StringBuilder buf,PVFieldPtr const & pvField,int indentLevel); - void getString(StringBuilder buf,PVFieldPtr const & pvField); - void getString(StringBuilder buf,PVField const * pvField,int indentLevel); - void getString(StringBuilder buf,PVField const * pvField); + void getString(std::string * buf,PVFieldPtr const & pvField,int indentLevel); + void getString(std::string * buf,PVFieldPtr const & pvField); + void getString(std::string * buf,PVField const * pvField,int indentLevel); + void getString(std::string * buf,PVField const * pvField); std::size_t fromString( PVStructurePtr const &pv, StringArray const & from, @@ -2843,7 +2679,7 @@ public: void fromULong(PVScalarPtr const & pv, uint64 from); void fromFloat(PVScalarPtr const & pv, float from); void fromDouble(PVScalarPtr const & pv, double from); - void newLine(StringBuilder buf, int indentLevel); + void newLine(std::string * buf, int indentLevel); ... } @@ -2858,30 +2694,38 @@ template<typename T> void copy( PVValueArray<T> & pvFrom, size_t fromOffset, + size_t fromStride, PVValueArray<T> & pvTo, size_t toOffset, - size_t len); + size_t toStride, + size_t count); void copy( PVScalarArray & from, size_t fromOffset, + size_t fromStride, PVScalarArray & to, size_t toOffset, - size_t len); + size_t toStride, + size_t count); void copy( PVStructureArray & from, size_t fromOffset, + size_t fromStride, PVStructureArray & to, size_t toOffset, - size_t len); + size_t toStride, + size_t count); void copy( PVArray & from, size_t fromOffset, + size_t fromStride, PVArray & to, size_t toOffset, - size_t len); + size_t toStride, + size_t count);

The last copy is the only one most client need to call. It either throws an error of the element types do not match or calls the @@ -2891,6 +2735,8 @@ other copy functions. The arguments are:

The source array.
fromOffset
The offset into the source array.
+
fromStride
+
The interval between source elements.
to
The destination array. The element type must be the same as for the source array. If the element type is structure then @@ -2898,7 +2744,9 @@ other copy functions. The arguments are:

toOffset
The offset into the destination array.
-
len
+
toStride
+
The interval between destination elements.
+
count
The number of elements to copy.

An exception is thrown if:

@@ -3080,7 +2928,7 @@ stack. For example the following is permitted:

A timeStamp is represented by the following structure

structure timeStamp
-    long secondsPartEpoch
+    long secondsPastEpoch
     int nanoSeconds
     int userTag
@@ -3753,14 +3601,15 @@ public: void or_and(const BitSet& set1, const BitSet& set2); bool operator==(const BitSet &set) const; bool operator!=(const BitSet &set) const; - void toString(StringBuilder buffer, int indentLevel = 0) const; virtual void serialize( ByteBuffer *buffer,SerializableControl *flusher) const; virtual void deserialize( ByteBuffer *buffer,DeserializableControl *flusher); - private: -}; +}; + +std::ostream& operator<<(std::ostream& o, const BitSet& b); +

where

@@ -3819,8 +3668,6 @@ private:
Does this bitSet have the same values as the argument.
operator!=(const BitSet &set)
Is this bitSet different than the argument.
-
toString(StringBuilder buffer, int indentLevel)
-
Show the current values of the bitSet.
virtual void serialize(ByteBuffer *buffer,SerializableControl *flusher) const;
Serialize the bitSet
@@ -3828,11 +3675,6 @@ private: *flusher);
Deserialize the bitSet.
-

TBD -

-
toString
-
Should this change and also implement ostream operator?
-

byteBuffer.h

@@ -4632,7 +4474,7 @@ Also he should check for correct descriptions. STATUSTYPE_FATAL }; static const char* StatusTypeName[]; - static Status OK; + static Status Ok; Status(); Status(StatusType type, std::string const & message); Status(StatusType type, std::string const & message, std::string stackDump); @@ -4642,10 +4484,9 @@ Also he should check for correct descriptions. std::string getStackDump() const; bool isOK() const; bool isSuccess() const; - std::string toString() const; - void toString(StringBuilder buffer, int indentLevel = 0) const; - void serialize(ByteBuffer *buffer, SerializableControl *flusher) const; void serialize(ByteBuffer *buffer, SerializableControl *flusher) const; + void deserialize(ByteBuffer *buffer, DeserializableControl *flusher); + void dump(std::ostream& o) const; };

The Status methods are:

@@ -4814,7 +4655,7 @@ public: double period)); void cancel(TimerCallbackPtr const &timerCallback); bool isScheduled(TimerCallbackPtr const &timerCallback); - void toString(StringBuilder builder); + void dump(std::ostream& o); ... }; @@ -5002,6 +4843,7 @@ class class epicsShareClass PVCopy PVStructurePtr const &pvMaster, PVStructurePtr const &pvRequest, std::string const & structureName); + virtual void destroy(); PVStructurePtr getPVMaster(); void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback); StructureConstPtr getStructure(); @@ -5024,6 +4866,7 @@ class class epicsShareClass PVCopy PVStructurePtr const &copyPVStructure, BitSetPtr const &bitSet); PVStructurePtr getOptions(std::size_t fieldOffset); + std::string dump(); ... }; diff --git a/documentation/pvDataCPP_20140708.html b/documentation/pvDataCPP_20140708.html new file mode 100644 index 0000000..1823fa4 --- /dev/null +++ b/documentation/pvDataCPP_20140708.html @@ -0,0 +1,5380 @@ + + + + + + EPICS pvDataCPP + + + + + + + + +
+

EPICS pvDataCPP

+ + +

EPICS v4 Working Group, Working Draft, 08-July-2014

+ +
+
Latest version:
+
pvDataCPP.html +
+
This version:
+
pvDataCPP_20140708.html +
+
Previous version:
+
pvDataCPP_20140501.html +
+
Editors:
+
Marty Kraimer, BNL
+
Michael Davidsaver, BNL
+
Matej Sekoranja, CosyLab
+
+ + +
+
+ +

Abstract

+ +

EPICS Version 4 provides efficient +storage, access, and communication, of memory resident structured data. +pvData is the storage compoment. +pvDataCPP is the C++ implementation of pvData. +It is one part of the set of related products in the EPICS +V4 control system programming environment:
+relatedDocumentsV4.html +

+ + +

Status of this Document

+ +

This is the 08-July-2014 version of the C++ implementation of pvData. +

+ +

RELEASE_NOTES.md provides changes since the last release. +TODO.md describes things to do before the next release. +

+ + +
+

Table of Contents

+
+
+ +

Introduction

+ +

pvData is one of a set of related projects. It describes and implements +data that the other projects support. Thus it is not useful by itself but +understanding pvData is required in order to understand the other projects. The +reader should also become familar with project pvAccess, which is +located via the same sourceforge site as this project.

+ +

The Java and C++ implementation of pvData implement the same data model but +differ in implementation because of the differences between Java and C++.

+ +

It is a good idea to read all of pvDataJava.html but read at least the +first two chapters:

+
+
Introduction
+
A brief descripton of pvData.
+
PVData Meta Language
+
A language used to describe data.
+
+ +

The material in these two chapters is NOT repeated in this documentation.

+ +

Doxygen documentation is available at doxygenDoc

+

The next section provides some examples of creating and accessing data. +

+

This document discusses the following:

+
+
pvDataAPP/pv
+
This subdirectory contains all the public interfaces that describe pvData. + The section from Namespace and Memory Management + through Conversion discuss these interfaces. +
+
pvDataApp/property
+
This section has support for managing "standard" fields. +
+
pvDataApp/misc
+
This section has support for facilities required by implementation code.
+
copy and monitor
+
These sections provide pvData support for implementing pvAccess providers.
+
+ +

Examples

+

This section provides some examples of creating and accessing both introspection and +data interfaces. The first time reader may not understand them but hopefully will get an +idea of how pvData works. After reading the rest of this document the examples will +be much easier to understand. +

+

The documentation directory for this project has a file examples.zip. +It has the code for the examples. +After it is unzipped:

+
+cd examples/configure
+cp ExampleRELEASE.local RELEASE.local
+#edit RELEASE.local
+cd ..
+make
+bin/linux-x86_64/introspectMain 
+bin/linux-x86_64/dataMain
+
+

+

The examples assume that the following statements have been issued:

+
+using std::cout;
+using std::endl;
+FieldCreatePtr fieldCreate = getFieldCreate();
+PVDataCreatePtr pvDataCreate = getPVDataCreate();
+StandardFieldPtr standardField = getStandardField();
+StandardPVFieldPtr standardPVField = getStandardPVField();
+
+

These provide access to most of pvData:

+
+
fieldCreate
+
This creates instances of introspection objects. + It also provides fieldBuilder, which provides an easier way to create introspection objects. +
+
pvDataCreate
+
This creates instances of data objects. +
+
standardField
+
This provides support for introspection obects for standard fields, + Standard fields are alarm, timeStamp, display, enumerated structure, and value alarm. +
+
standardPVField
+
This provides support for data obects for standard fields, +
+
+ + +

Introspection Examples

+

The examples all produce a top level structure. +The reason is that top level structures are what pvAccess passes between client and server +and what pvDatabaseCPP supports.

+

Three ways to create a structure

+

The following is the hardest way to create structure that has a double value field and a time stamp field: +It uses only createField. +

+
+    size_t n = 3;
+    StringArray names;
+    names.reserve(n);
+    FieldConstPtrArray fields;
+    fields.reserve(n);
+    names.push_back("secondsPastEpoch");
+    fields.push_back(fieldCreate->createScalar(pvLong));
+    names.push_back("nanoSeconds");
+    fields.push_back(fieldCreate->createScalar(pvInt));
+    names.push_back("userTag");
+    fields.push_back(fieldCreate->createScalar(pvInt));
+    StructureConstPtr timeStamp = fieldCreate->createStructure(names,fields);
+    size_t ntop = 2;
+    StringArray topnames;
+    topnames.reserve(ntop);
+    FieldConstPtrArray topfields;
+    topfields.reserve(ntop);
+    topnames.push_back("value");
+    topfields.push_back(fieldCreate->createScalar(pvDouble));
+    topnames.push_back("timeStamp");
+    topfields.push_back(timeStamp);
+    StructureConstPtr doubleScalar =
+        fieldCreate->createStructure(topnames,topfields);
+    cout << doubleScalar->dump(cout) << endl;
+
+

Using FieldBuilder the same can be done via:

+
+    StructureConstPtr doubleScalarHard =
+    getFieldCreate()->createFieldBuilder()->
+        add("value",pvDouble) ->
+        addNestedStructure("timeStamp")->
+            setId("time_t")->
+            add("secondsPastEpoch", pvLong)->
+            add("nanoSeconds", pvInt)->
+            add("userTag", pvInt)->
+            endNested()->
+        createStructure();
+    cout << doubleScalarHard->dump(cout) << endl;
+
+

The easiest way to produce the structure is:

+
+    StructureConstPtr stringArrayEasy =
+         getStandardField()->scalarArray(pvString,"alarm,timeStamp");
+    cout << stringArrayEasy->dump(cout) << "\n\n";
+
+These three ways produce: +
+hardest way
+structure
+    double value
+    structure timeStamp
+        long secsPastEpoch
+        int nanoseconds
+        int userTag
+
+hardway
+structure
+    double value
+    time_t timeStamp
+        long secsPastEpoch
+        int nanoseconds
+        int userTag
+
+easy way
+uri:ev4:nt/2012/pwd:NTScalarArray
+    string[] value
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+
+

An easy way to create a structure with a string array value field and an alarm and time stamp is +via standardField:

+
+    StructureConstPtr stringArrayEasy =
+        standardField->scalarArray(pvString,"alarm,timeStamp");
+    cout <<stringArrayEasy->dump(cout) << endl;
+
+It produces : +
+uri:ev4:nt/2012/pwd:NTScalarArray
+    string[] value
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+0x607188
+
+

enumerated structure

+

An enumerated structure is a structure with two sub-fields: +index, which is an int, and choices, which is an array of string. +The following examples create a structure which has a field names value, which +is an enumerated structure and additional fields. +A hard way to create an structure with an enumerated value field and a time stamp is:

+
+    StructureConstPtr enum_t =
+    fieldCreate->createFieldBuilder()->
+        setId("enum_t")->
+        add("index", pvInt)->
+        addArray("choices", pvString)->
+        createStructure();
+
+    StructureConstPtr ntEnumHard =
+    fieldCreate->createFieldBuilder()->
+        setId("uri:ev4:nt/2012/pwd/NTEnum")->
+        add("value", enum_t)->
+        addNestedStructure("timeStamp")->
+            setId("time_t")->
+            add("secondsPastEpoch", pvLong)->
+            add("nanoSeconds", pvInt)->
+            add("userTag", pvInt)->
+            endNested()->
+        createStructure();
+    cout <<ntEnumHard->dump(cout) << endl;
+
+It produces: +
+uri:ev4:nt/2012/pwd/NTEnum
+    enum_t value
+        int index
+        string[] choices
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+
+

The following is an easy way. Note that it has two additional +fields: alarm and timeStamp:

+
+    StructureConstPtr ntEnumEasy = getStandardField()->enumerated("alarm,timeStamp");
+    cout <<ntEnumEsay->dump(cout) << endl;
+
+It produces: +
+uri:ev4:nt/2012/pwd:NTEnum
+    enum_t value
+        int index
+        string[] choices
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+
+ +

union example

+

The following creates a union and a structure with a union value field:

+ +
+    UnionConstPtr ntunion =
+    fieldCreate->createFieldBuilder()->
+        add("doubleValue", pvDouble)->
+        add("intValue", pvInt)->
+        add("timeStamp",standardField->timeStamp())->
+        createUnion();
+    cout <<ntunion->dump(cout) << endl;
+
+    StructureConstPtr unionValue = standardField->regUnion(punion,"alarm,timeStamp");
+    cout <<unionValue->dump(cout) << endl;
+
+It produces: +
+union
+    double doubleValue
+    int intValue
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+
+structure with value field being a union
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        double doubleValue
+        int intValue
+        time_t timeStamp
+            long secondsPastEpoch
+            int nanoSeconds
+            int userTag
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+
+ +

union array example

+

The following:

+
+    UnionArrayConstPtr unionArray = fieldCreate->createUnionArray(
+        fieldCreate->createVariantUnion());
+    cout << unionArray->dump(cout) << "\n\n";
+
+

Produces

+
+any[]
+any
+0x607188
+
+

power supply

+

The following creates a more complex structure:

+
+    StructureConstPtr powerSupply =
+    fieldCreate->createFieldBuilder()->
+        add("alarm",standardField->alarm()) ->
+        add("timestamp",standardField->timeStamp()) ->
+        addNestedStructure("power") ->
+           add("value",pvDouble) ->
+           add("alarm",standardField->alarm()) ->
+           endNested()->
+        addNestedStructure("voltage") ->
+           add("value",pvDouble) ->
+           add("alarm",standardField->alarm()) ->
+           endNested()->
+        addNestedStructure("current") ->
+           add("value",pvDouble) ->
+           add("alarm",standardField->alarm()) ->
+           endNested()->
+        createStructure();
+    std::cout << powerSupply->dumpValue(cout) <<std::endl;
+
+It produces: +
+structure
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timestamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+    structure power
+        double value
+        alarm_t alarm
+            int severity
+            int status
+            string message
+    structure voltage
+        double value
+        alarm_t alarm
+            int severity
+            int status
+            string message
+    structure current
+        double value
+        alarm_t alarm
+            int severity
+            int status
+            string message
+
+

Data Examples

+

The examples all produce a top level structure.

+

scalar example

+
+    PVStructurePtr doubleValue = getPVDataCreate()->createPVStructure(
+        getStandardField()->scalar(pvDouble,"alarm,timeStamp"));
+    PVDoublePtr pvdouble =
+        doubleValue->getSubField<PVDouble>("value");
+    pvdouble->put(1e5);
+    cout << doubleValue->dumpValue(cout) << endl;
+    double value = doubleValue->getSubField<PVDouble>("value")->get();
+    cout << "from get " << value << "\n\n";
+
+This produces: +
+uri:ev4:nt/2012/pwd:NTScalar 
+    double value 100000
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message 
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x607268
+from get 100000
+
+

array example

+
+    PVStructurePtr doubleArrayValue = pvDataCreate->createPVStructure(
+        standardField->scalarArray(pvDouble,"alarm,timeStamp"));
+    PVDoubleArrayPtr pvDoubleArray =
+         doubleArrayValue->getSubField<PVDoubleArray>("value");
+    size_t len = 10;
+    shared_vector<double> xxx(len);
+    for(size_t i=0; i< len; ++i) xxx[i] = i;
+    shared_vector<const double> data(freeze(xxx));
+    pvDoubleArray->replace(data);
+    cout << doubleArrayValue->dumpValue(cout) << endl;
+    
+    shared_vector<const double>  getData = pvDoubleArray->view();
+    cout << "via getData";
+    for (size_t i=0; i< len; ++i) cout << " " << getData[i];
+    cout << endl;
+
+This produces: +
+uri:ev4:nt/2012/pwd:NTScalarArray 
+    double[] value [0,1,2,3,4,5,6,7,8,9]
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message 
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x607268
+via getData 0 1 2 3 4 5 6 7 8 9
+
+

enumerated example

+
+    PVStructurePtr pvntenum = pvDataCreate->createPVStructure(
+         standardField->enumerated("alarm,timeStamp"));
+    cout << pvntenum->dumpValue(cout) << "\n\n";
+
+This produces: +
+uri:ev4:nt/2012/pwd:NTEnum 
+    enum_t value
+        int index 0
+        string[] choices []
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message 
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x607268
+
+

power supply example

+
+    StructureConstPtr powerSupply =
+    fieldCreate->createFieldBuilder()->
+        add("alarm",alarm) ->
+        add("timestamp",timeStamp) ->
+        addNestedStructure("power") ->
+           add("value",pvDouble) ->
+           add("alarm",alarm) ->
+           endNested()->
+        addNestedStructure("voltage") ->
+           add("value",pvDouble) ->
+           add("alarm",alarm) ->
+           endNested()->
+        addNestedStructure("current") ->
+           add("value",pvDouble) ->
+           add("alarm",alarm) ->
+           endNested()->
+        createStructure();
+    PVStructurePtr pvpowerSupply = pvDataCreate->createPVStructure(powerSupply);
+    cout << pvpowerSupply->dumpValue(cout) << endl;
+
+This produces: +
+structure 
+    alarm_t alarm_t
+        int severity 0
+        int status 0
+        string message 
+    time_t timestamp_t
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+    structure power
+        double value 0
+        alarm_t alarm
+            int severity 0
+            int status 0
+            string message 
+    structure voltage
+        double value 0
+        alarm_t alarm
+            int severity 0
+            int status 0
+            string message 
+    structure current
+        double value 0
+        alarm_t alarm
+            int severity 0
+            int status 0
+            string message 
+0x607268
+
+

union example

+
+    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(
+        standardField->regUnion(
+            fieldCreate->createFieldBuilder()->
+                add("doubleValue", pvDouble)->
+                add("intValue", pvInt)->
+                add("timeStamp",standardField->timeStamp())->
+                createUnion(),
+            "alarm,timeStamp"));
+    PVStructurePtr pvTimeStamp =
+        pvStructure->getSubField<PVUnion>("value")->select<PVStructure>(2);
+    pvTimeStamp->getSubField<PVLong>("secondsPastEpoch")->put(1000);
+    cout << pvStructure->dumpValue(cout) << "\n";
+    pvStructure->getSubField<PVUnion>("value")->select<PVDouble>(0)->put(1e5);
+    cout << pvStructure->dumpValue(cout) << "\n\n";
+
+This produces: +
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        time_t
+            long secondsPastEpoch 1000
+            int nanoSeconds 0
+            int userTag 0
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        double  100000
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+
+ +

varient union example

+
+    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(
+        standardField->variantUnion("alarm,timeStamp"));
+    PVStructurePtr pvTimeStamp =
+        pvDataCreate->createPVStructure(standardField->timeStamp());
+    pvStructure->getSubField<PVUnion>("value")->set(pvTimeStamp);
+    pvTimeStamp->getSubField<PVLong>("secondsPastEpoch")->put(1000);
+    cout << pvStructure->dumpValue(cout) << "\n";
+    pvStructure->getSubField<PVUnion>("value")->set(
+        pvDataCreate->createPVScalar(pvDouble));
+    PVDoublePtr pvValue = static_pointer_cast<PVDouble>(
+        pvStructure->getSubField<PVUnion>("value")->get());
+    pvValue->put(1e5);
+    cout << pvStructure->dumpValue(cout) << "\n\n";
+
+This produces: +
+uri:ev4:nt/2012/pwd:NTUnion
+    any value
+        time_t
+            long secondsPastEpoch 1000
+            int nanoSeconds 0
+            int userTag 0
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+uri:ev4:nt/2012/pwd:NTUnion
+    any value
+        double  100000
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+
+ +

big union example

+
+    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(
+        standardField->regUnion(
+            fieldCreate->createFieldBuilder()->
+                add("doubleValue", pvDouble)->
+                addArray("doubleArrayValue",pvDouble)->
+                addNestedUnion("unionValue") ->
+                    add("doubleValue", pvDouble)->
+                    add("alarm",standardField->alarm()) ->
+                    endNested() ->
+                addNestedStructure("structValue") ->
+                    add("doubleValue", pvDouble)->
+                    addArray("doubleArrayValue",pvDouble)->
+                    endNested() ->
+                addNestedUnionArray("unionArrayValue") ->
+                    add("doubleValue", pvDouble)->
+                    add("alarm",standardField->alarm()) ->
+                    endNested() ->
+                addNestedStructureArray("structArrayValue") ->
+                    add("doubleValue", pvDouble)->
+                    addArray("doubleArrayValue",pvDouble)->
+                    endNested() ->
+                createUnion(),
+            "alarm,timeStamp"));
+    cout << "introspection\n";
+    cout <<pvStructure->getStructure()->dump(cout) << endl;
+    cout << "data\n";
+    cout << pvStructure->dumpValue(cout) << "\n";
+    PVUnionPtr pvUnion = pvStructure->getSubField<PVUnion>("value");;
+    pvUnion->select("doubleValue");
+    PVDoublePtr pvDouble = pvUnion->get<PVDouble>();
+    pvDouble->put(1.55);
+    cout << "select valueDouble\n";
+    cout << pvStructure->dumpValue(cout) << "\n";
+    cout << "value = " << pvDouble->get() << "\n";
+    pvUnion->select("structValue");
+    pvDouble = pvUnion->get<PVStructure>()->getSubField<PVDouble>("doubleValue");
+    pvDouble->put(1.65);
+    cout << "select structValue\n";
+    cout << pvStructure->dumpValue(cout) << "\n";
+    cout << "value = " << pvDouble->get() << "\n";
+
+This produces: +
+introspection
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        double doubleValue
+        double[] doubleArrayValue
+        union unionValue
+            double doubleValue
+            alarm_t alarm
+                int severity
+                int status
+                string message
+        structure structValue
+            double doubleValue
+            double[] doubleArrayValue
+        union[] unionArrayValue
+            union[]
+                union
+                    double doubleValue
+                    alarm_t alarm
+                        int severity
+                        int status
+                        string message
+        structure[] structArrayValue
+            structure[]
+                structure
+                    double doubleValue
+                    double[] doubleArrayValue
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    time_t timeStamp
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+0x60a2c8
+data
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        (none)
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+select valueDouble
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        double  1.55
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+value = 1.55
+select structValue
+uri:ev4:nt/2012/pwd:NTUnion
+    union value
+        structure
+            double doubleValue 1.65
+            double[] doubleArrayValue []
+    alarm_t alarm
+        int severity 0
+        int status 0
+        string message
+    time_t timeStamp
+        long secondsPastEpoch 0
+        int nanoSeconds 0
+        int userTag 0
+0x60a2c8
+value = 1.65
+
+ +

Namespace and Memory Management

+ +

Namespace

+ +

All code in project pvDataCPP appears in namespace:

+
namespace epics { namespace pvData {
+     // ...
+}}
+ +

Memory Managemment

+ +

Many pvDataCPP introspection and data objects are designed to be shared. They are +made availiable via std::tr1::shared_ptr. +The following naming convention is used +in typedefs:

+
+
Ptr
+
When Ptr appears it stands for std::tr1::shared_ptr. +
+
+

For example:

+
+typedef PVScalarValue<boolean> PVBoolean;
+typedef std::tr1::shared_ptr<PVBoolean> PVBooleanPtr;
+
+ +

pvDataApp/pv

+ +

Directory pvDataApp/pv has header files that completely describe pvData. +The implementation is provided in directory pvDataApp/factory. +Test programs appears in testApp/pv.

+ +

NOTES:

+
+
interface
+
The documention uses the word interface. + This is an analogy with how Java defines interface. + C++ does not have interfaces but directory pv defines classes + with public members that are similar to the Java interfaces. Most of the + implementation is in factory.
+
Naming Convertions
+
The naming convertions for variables, methods, and classes follow the + Java convertions, i. e. class name begin with an upper case letter, + variables and methods begin with a lower case letter.
+
+ +

A PVStructure is a field that contains an array of subfields. Each field has +code for accessing the field. The interface for each field is an interface that +extends PVField. Each field also has an introspection interface, which an +extension of Field. The next few sections describes the complete set of C++ +introspection and data interfaces for pvData.

+ +

Class FieldCreate creates introspection objects. Class PVDataCreate creates +data objects. Class Convert provides a rich set of methods for converting and +copying data between fields.

+ +

Directory pvDataApp/pv has the following header files:

+
+
pvType.h
+
C++ definitions for primitive types.
+
pvIntrospect.h
+
A complete description of the introspection interfaces.
+
pvData.h
+
A complete description of the data interfaces.
+
convert.h
+
A facility that converts between data fields.
+
pvSubArrayCopy.h
+
This provides a facility that performs array coping between + arrays that have the same type.
+
standardField.h
+
Provides access to introspection interfaces for standard structures + like timeStamp, alarm, etc.
+
standardPVField.h
+
Cteates data interfaces for standard data structures like timeStamp, + alarm, etc.
+
+ +

pvType.h

+ +

This provides C/C++ definitions for the pvData primitive types: boolean, +byte, short, int, long, ubyte,ushort, uint,u long,float, double, and string. +Because pvData is network data, the C++ implementation must implement the +proper semantics for the primitive types.

+ +

pvType.h provides the proper semantics.

+ +

It includes the definitions:

+
+typedef /*lots of stuff*/ boolean
+
+typedef int8_t   int8;
+typedef int16_t  int16;
+typedef int32_t  int32;
+typedef int64_t  int64;
+typedef uint8_t   uint8;
+typedef uint16_t  uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+// float and double are types
+
+typedef std::vector<std::string> StringArray;
+typedef std::tr1::shared_ptr<StringArray> StringArrayPtr;
+inline std::string * get(StringArray &value);
+inline std::string const * get(StringArray const &value);
+inline std::string * get(StringArrayPtr &value);
+inline std::string const * get(StringArrayPtr const &value);
+}
+inline StringArray & getVector(StringArrayPtr &value);
+inline StringArray const & getVector(StringArrayPtr const &value);
+typedef std::vector<std::string>::iterator StringArray_iterator;
+typedef std::vector<std::string>::const_iterator StringArray_const_iterator;
+
+ +

where

+
+
boolean
+
A c++ bool has the semantics required for boolean. Only the name is + different. C++ code can use either bool or boolean.
+
int8,...,uint64
+
Integers present a problem because short, int, and long are C++ + reserved words but do not have a well defined number of bits. Thus for + C++ the definitions above are used in C++ code. The above definitions + have worked on all C++ implementations tested at present. If they break + in a future implementation they should be changes via "#ifdef" + preprocessor statements.
+
std::string
+
pvData requires that a string be an immutable string that is transfered + over the network as a UTF8 encoded string. Since std::string implements + copy on write semantics, it can be used for support for immutable + strings. It can also be serialized/deserialized as a UTF8 encoded string. + Because it is not a C++ primitive the first letter is capitalized. This + is the same convention the Java implementation uses. + Note that string is treated like a primitive type.
+
StringArray definitions
+
typedefs are provided for an array of std::strings, + which is a std::vector<std::string>. + This is used by introspection. +
+
+

TBD +

+
boolean
+
Question for Michael. Why isn't the definition of boolean just +
+typedef uint8_t boolean;
+    
+
printer.h
+
Not documented. Is this needed? Nothing currently uses it.
+
+

+ +

pvIntrospect.h

+ +

This subsection describes pvIntrospect.h This file is quite big so rather +than showing the entire file, it will be described in parts.

+ +

The primary purpose for pvData is to support network access to structured data. +pvAccess transports top level pvStructures. In addition a pvAccess server holds +a set of pvnames, where each name is a unique name in the local network. +This is also refered to as the channel name. +

+ +

Given a pvname , it is possible to introspect the types of the associated data +access to data. The reflection and data interfaces are separate because the +data may not be available. For example when a pvAccess client connects to a pvname, +the client library can obtain the reflection information without obtaining any +data. Only when a client issues an I/O request will data be available. This +separation is especially important for arrays and structures so that a client +can discover the type without requiring that a large data array or structure be +transported over the network.

+ +

Type Description

+ +

Types are defined as:

+
enum Type {
+    scalar,
+    scalarArray,
+    structure,
+    structureArray,
+    union_,
+    unionArray
+};
+
+class TypeFunc {
+public:
+    epicsShareExtern const char* name(Type type);
+};
+
+enum ScalarType {
+    pvBoolean,
+    pvByte, pvShort, pvInt, pvLong,
+    pvUByte, pvUShort, pvUInt, pvULong,
+    pvFloat,pvDouble,
+    pvString;
+};
+
+namespace ScalarTypeFunc {
+public:
+    bool isInteger(ScalarType type);
+    bool isUInteger(ScalarType type);
+    bool isNumeric(ScalarType type);
+    bool isPrimitive(ScalarType type);
+    ScalarType getScalarType(std::string const &value);
+    const char* name(ScalarType);
+    size_t elementSize(ScalarType id);
+};
+std::ostream& operator<<(std::ostream& o, const ScalarType& scalarType);
+
+ +

Type is one of the following:

+
+
scalar
+
A scalar of one of the scalar types.
+
scalarArray
+
An array where every element has the same scalar type.
+
structure
+
A structure where each field has a name and a type. Within a structure + each field name must be unique but the types can be different.
+
structureArray
+
An array where each element is a structure. Each element has the same + structure introspection interface.
+
union_t
+
This is like a structure that has a single subfield. + The type for the subfield can either be any type, which is called a varient union, + of can be one od a specified set of types. + In the data interfaces the type can be changed dynamically. +'
+
unionArray
+
An array where each element is a union. Each element has the same + union introspection interface.
+
+ +

ScalarType is one of the following:

+
+
pvBoolean
+
Has the value false or true.
+
pvByte
+
A signed 8 bit integer.
+
pvShort
+
A signed 16 bit integer.
+
pvInt
+
A signed 32 bit integer.
+
pvLong
+
A signed 64 bit integer.
+
pvUByte
+
An unsigned 8 bit integer.
+
pvUShort
+
An unsigned 16 bit integer.
+
pvUInt
+
An unsigned 32 bit integer.
+
pvULong
+
An unsigned 64 bit integer.
+
pvFloat
+
A IEEE float.
+
pvDouble
+
A IEEE double,
+
pvString
+
An immutable string.
+
+ +

TypeFunction is a set of convenience methods for Type

+
+
name
+
Returns the name of the type.
+
+ +

ScalarTypeFunction is a set of convenience methods for ScalarType

+
+
isInteger
+
Is the scalarType an integer type, i.e. one of pvByte,...pvULong.
+
isUInteger
+
Is the scalarType an unsigned integer type, i.e. one of + pvUByte,...pvULong
+
isNumeric
+
Is the scalarType numeric, i.e. pvByte,...,pvDouble.
+
isPrimitive
+
Is the scalarType primitive, i.e. not pvString
+
name
+
Returns the name of the scalarType.
+
getScalarType
+
Given a string of the form std::string("boolean"),...,std::string("string") + return the scalarType.
+
elementSize
+
Returns the size in bytes of an instance of the scalarType.
+
+ +

Introspection Description

+ +

This section describes the reflection interfaces which provide the +following:

+
+
Field
+
A field: +
    +
  • Has a Type.
  • +
  • Can be converted to a string.
  • +
  • Can be shared. A reference count is kept. When it becomes 0 the + instance is automatically deleted.
  • +
+
+
Scalar
+
A scalar has a scalarType
+
ScalarArray
+
The element type is a scalarType
+
Structure
+
Has fields that can be any of the supported types.
+
StructureArray
+
The field holds an array of structures. Each element has the same + Structure interspection interface. A pvAccess client can only get/put + entire PVStructure elements NOT subfields of array elements.
+
Union
+
+ This has two flavors: a varient union or a union of a fixed set + of types. A PVUnion will have a single subfield. + If the union introspection interface is a varient union then + the single field can be of any type and has the name any. + If the union is not a varient type then the type can be one of + a fixed set of types and a name associated with the type. + The union introspection interface has a field array and a string + array that has the fixed set of types and associated names. +
+
UnionArray
+
+ This is an array of unions. A PVUnionArray is an array + of PVUnions. Each element has the same interspection interface + but the subfield of each element can have a different type. +
+
FieldBuilder
+
This is a convenience interface that makes it easier to create + top introspection interfaces. +
+
FieldCreate
+
This is an interface that provides methods to create introspection + interfaces. A factory is provides to create FieldCreate.
+
getFieldCreate
+
Gets a pointer to the single instance of FieldCreate.
+
+

Field,Scalar,ScalarArray,Structure,StructureArray,Union,UnionArray

+
class Field;
+class Scalar;
+class ScalarArray;
+class Structure;
+class StructureArray;
+class Union;
+class UnionArray;
+
+typedef std::tr1::shared_ptr<const Field> FieldConstPtr;
+typedef std::vector<FieldConstPtr> FieldConstPtrArray;
+typedef std::tr1::shared_ptr<const Scalar> ScalarConstPtr;
+typedef std::tr1::shared_ptr<const ScalarArray> ScalarArrayConstPtr;
+typedef std::tr1::shared_ptr<const Structure> StructureConstPtr;
+typedef std::tr1::shared_ptr<const StructureArray> StructureArrayConstPtr;
+typedef std::tr1::shared_ptr<const Union> UnionConstPtr;
+typedef std::tr1::shared_ptr<const UnionArray> UnionArrayConstPtr;
+
+
+class Field :
+    virtual public Serializable,
+    public std::tr1::enable_shared_from_this<Field>
+{
+public:
+    POINTER_DEFINITIONS(Field);
+    virtual ~Field();
+    Type getType() const{return m_type;}
+    virtual std::string getID() const = 0;
+    virtual std::ostream& dump(std::ostream& o) const = 0;
+ ...
+};
+std::ostream& operator<<(std::ostream& o, const Field& field);
+
+class Scalar : public Field{
+public:
+    POINTER_DEFINITIONS(Scalar);
+    virtual ~Scalar();
+    typedef Scalar& reference;
+    typedef const Scalar& const_reference;
+
+    ScalarType getScalarType() const {return scalarType;}
+    virtual std::string getID() const;
+    virtual std::ostream& dump(std::ostream& o) const;
+    virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
+    virtual void deserialize(ByteBuffer *buffer, DeserializableContol *control);
+ ...
+};
+
+class epicsShareClass Array : public Field{
+public:
+    POINTER_DEFINITIONS(Array);
+    virtual ~Array();
+    typedef Array& reference;
+    typedef const Array& const_reference;
+ ...
+};
+
+
+class ScalarArray : public Field{
+public:
+    POINTER_DEFINITIONS(ScalarArray);
+    typedef ScalarArray& reference;
+    typedef const ScalarArray& const_reference;
+
+    ScalarArray(ScalarType scalarType);
+    ScalarType  getElementType() const {return elementType;}
+    virtual std::string getID() const;
+    virtual std::ostream& dump(std::ostream& o) const;
+    virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
+    virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
+ ...
+};
+
+class StructureArray : public Field{
+public:
+    POINTER_DEFINITIONS(StructureArray);
+    typedef StructureArray& reference;
+    typedef const StructureArray& const_reference;
+
+    StructureConstPtr  getStructure() const {return pstructure;}
+    virtual std::string getID() const;
+    virtual std::ostream& dump(std::ostream& o) const;
+    virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
+    virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
+ ...
+};
+
+class epicsShareClass UnionArray : public Field{
+public:
+    POINTER_DEFINITIONS(UnionArray);
+    typedef UnionArray& reference;
+    typedef const UnionArray& const_reference;
+    UnionConstPtr  getUnion() const {return punion;}
+    virtual std::string getID() const;
+    virtual std::ostream& dump(std::ostream& o) const;
+    virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
+    virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
+};
+
+class Structure : public Field {
+public:
+    POINTER_DEFINITIONS(Structure);
+    typedef Structure& reference;
+    typedef const Structure& const_reference;
+
+    std::size_t getNumberFields() const {return numberFields;}
+    FieldConstPtr getField(std::string const & fieldName) const;
+    FieldConstPtr getField(std::size_t index) const;
+    std::size_t getFieldIndex(std::string const &fieldName) const;
+    FieldConstPtrArray const & getFields() const {return fields;}
+    StringArray const & getFieldNames() const;
+    std::string getFieldName(std::size_t fieldIndex) const;
+    virtual std::string getID() const;
+    virtual std::ostream& dump(std::ostream& o) const;
+    virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
+    virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
+ ...
+};
+
+class epicsShareClass Union : public Field {
+public:
+    POINTER_DEFINITIONS(Union);
+    static std::string DEFAULT_ID;
+    static std::string ANY_ID;
+    virtual ~Union();
+    typedef Union& reference;
+    typedef const Union& const_reference;
+
+    std::size_t getNumberFields() const;
+    FieldConstPtr getField(std::string const &fieldName) const;
+    FieldConstPtr getField(std::size_t index);
+    std::size_t getFieldIndex(std::string const &fieldName) const;
+    FieldConstPtrArray const & getFields() const;
+    StringArray const & getFieldNames() const;
+    std::string getFieldName(std::size_t fieldIndex) const;
+    bool isVariant() const;
+    virtual std::string getID() const;
+    virtual std::ostream& dump(std::ostream& o) const;
+    virtual void serialize(
+        ByteBuffer *buffer, SerializableControl *control) const;
+    virtual void deserialize(
+        ByteBuffer *buffer, DeserializableControl *control);
+    
+};
+
+
+
Constructors
+
Note that all constructors are protected or private. The only way to + create instances is via fieldBuilder or fieldCreate. The implementation manages all + storage via shared pointers.
+
dump
+
Many classes provide this. This is a stream method that prints using + the metadata syntax described in pvDataJava.html.
+
+ +

Field

+
+
getType
+
Get the field type.
+
getID
+
Get an ID for this introspection interface
+
+ +

Scalar

+
+
getScalarType
+
Get that scalar type.
+
getID
+
For each scalarType there is one instance of Scalar. The ID for each is + the metadata name for the type, i. e. one of "boolean" , ... , "string". +
+
+ +

ScalarArray

+
+
getElementType
+
Get the element type.
+
getID
+
For each elemnetType there is one instance of ScalarArray. The ID for + each is the metadata name for the type, i. e. one of "boolean[]" , ... , + "string[]".
+
+ +

StructureArray

+
+
getStructure
+
Get the introspection interface that each element shares,
+
getID
+
This returns the ID[] where ID is the value returned by + structure->getID().
+
+` +

UnionArray

+
+
getUnion
+
Get the union interface for each element.
+
+ +

Structure

+
+
getNumberFields
+
Get the number of immediate subfields.
+
getField
+
Given a name or an index get the introspection interface for the + field.
+
getFieldIndex
+
Given a name get the index, within the array returned by the next + method, of the field.
+
getFields
+
Get the array of introspection interfaces for the field,
+
getFieldNames
+
Get the array of field names for the subfields.
+
getFieldName
+
Get the field name for the specified index.
+
+

Union

+
+
getNumberFields
+
Get the number of possible field types. + Both getFields and getFieldNames will return an array with getNumberFields elements. + A value of 0 is returned for invarient arrays. +
+
getField
+
Given a name ot an index the type is returned. + NULL is returned if not found. +
+
getFieldIndex
+
Get the index for name. -1 is returned if not found. +
getFields
+
Get the array of types.
+
getFieldNames
+
Get the array of names.
+
getFieldName
+
Get the name for the specified index.
+
isVariant
+
returns true if this is varient array and false otherwise. +
+

fieldBuilder and createField

+
+class epicsShareClass FieldBuilder :
+    public std::tr1::enable_shared_from_this<FieldBuilder>
+{
+public:
+    FieldBuilderPtr setId(std::string const & id);
+    FieldBuilderPtr add(std::string const & name, ScalarType scalarType);
+    FieldBuilderPtr add(std::string const & name, FieldConstPtr const & field);
+    FieldBuilderPtr addArray(std::string const & name, ScalarType scalarType);
+    FieldBuilderPtr addArray(std::string const & name, FieldConstPtr const & element);
+    StructureConstPtr createStructure();
+    UnionConstPtr createUnion();
+    FieldBuilderPtr addNestedStructure(std::string const & name); 
+    FieldBuilderPtr addNestedUnion(std::string const & name);
+    FieldBuilderPtr addNestedStructureArray(std::string const & name); 
+    FieldBuilderPtr addNestedUnionArray(std::string const & name);
+    FieldBuilderPtr endNested();
+};
+
+class epicsShareClass FieldCreate {
+public:
+    static FieldCreatePtr getFieldCreate();
+    FieldBuilderPtr createFieldBuilder() const;
+    ScalarConstPtr createScalar(ScalarType scalarType) const;
+    ScalarArrayConstPtr createScalarArray(ScalarType elementType) const;
+    StructureArrayConstPtr createStructureArray(StructureConstPtr const & structure) const;
+    StructureConstPtr createStructure () const;
+    StructureConstPtr createStructure (
+        StringArray const & fieldNames,
+        FieldConstPtrArray const & fields) const;
+    StructureConstPtr createStructure (
+    	std::string const & id,
+        StringArray const & fieldNames,
+        FieldConstPtrArray const & fields) const;
+    UnionConstPtr createUnion (
+        StringArray const & fieldNames,
+        FieldConstPtrArray const & fields) const;
+    UnionConstPtr createUnion (
+    	std::string const & id,
+        StringArray const & fieldNames,
+        FieldConstPtrArray const & fields) const;
+    UnionConstPtr createVariantUnion() const;
+    UnionArrayConstPtr createVariantUnionArray() const;
+    UnionArrayConstPtr createUnionArray(UnionConstPtr const & punion) const;
+    StructureConstPtr appendField(
+        StructureConstPtr const & structure,
+        std::string const & fieldName, FieldConstPtr const & field) const;
+    StructureConstPtr appendFields(
+        StructureConstPtr const & structure,
+        StringArray const & fieldNames,
+        FieldConstPtrArray const & fields) const;
+    FieldConstPtr deserialize(ByteBuffer* buffer, DeserializableControl* control) const;
+        
+};
+
+epicsShareExtern FieldCreatePtr getFieldCreate();
+
+ +

FieldBuilder

+

This is a class that makes it easier to create introspection interfaces. +It is meant to be used via stream input syntax. See the examples that follow the +description of the methods. +

+
+
setID
+
This sets an ID for the field, which is the name for the field when it is a subfield. +
+
add
+
+ Add a scalar field. +
+
addArray
+
+ Add a scalarArray field. +
+
createStructure
+
+ Create a structure from the fields that are currently present. +
+
createUnion
+
+ Create a union from the fields that are currently present. +
+
addNestedStructure
+
+ Add a nested structure. This is followed by an arbitrary number of adds + followed by a an endNested. +
+
addNestedUnion
+
+ Add a nested union. This is followed by an arbitrary number of adds + followed by a an endNested. +
+
addNestedStructureArray
+
+ Add a nested structure array. This is followed by an arbitrary number of adds + followed by a an endNested. +
+
addNestedUnionArray
+
+ Add a nested union array. This is followed by an arbitrary number of adds + followed by a an endNested. +
+
endNested
+
+ End current nested structure or union. +
+
+

Examples of using fieldBuilder were given earlier in this manual.

+

FieldCreate

+
+
getFieldCreate
+
Get the single instance of FieldCreate.
+
createFieldBuilder
+
Create an instance of a FieldBuilder.
+
createScalar
+
Create a scalar introspection instance.
+
createScalarArray
+
Create a scalar array introspection instance.
+
createStructure
+
Create a structure introspection instance. Three methods are provided. + The first creates an empty structure, i. e. a structure with no fields. + The other two are similar. + The only difference is that one provides an ID and the other does + not. The one without will result in ID structure.
+
createUnion
+
Create a union. There are two methods. + Each has arguments for an array of types and an array of names. + One method has an id. The other results in id = union. +
+
createVariantUnion
+
+ Create a varient union. The id will be any. +
+
createUnionArray
+
Create a union array. punion is the introspection interface + for each element.
+
createVariantUnionArray
+
Create a union array where each element is a varient union.
+
createStructureArray
+
Create a structure array introspection instance.
+
appendField
+
Create a new structure that is like an existing structure but has + an extra field appended to it. +
+
appendFields
+
Create a new structure that is like an existing structure but has + extra fields appended to it. +
+
deserialize
+
Deserialize from given byte buffer.
+
+ +

standardField.h

+ +

The file standardField.h has a class description for creating or sharing +Field objects for standard fields. For each type of field a method is provided. +Each creates a structure that has a field named "value" and a set of properyt +fields, The property field is a comma separated string of property names of the +following: alarm, timeStamp, display, control, and valueAlarm. An example is +"alarm,timeStamp,valueAlarm". The method with properties creates a structure +with fields named value and each of the property names. Each property field is +a structure defining the property. The details about each property is given in +the section named "Property". For example the call:

+
+StructureConstPtr example = standardField->scalar(
+    pvDouble,
+    "value,alarm,timeStamp"
+    );
+
+ +

Will result in a Field definition that has the form:

+
structure example
+    double value
+    alarm_t alarm
+        int severity
+        int status
+        string message
+    timeStamp_t timeStamp
+        long secondsPastEpoch
+        int  nanoSeconds
+        int userTag
+ +

In addition there are methods that create each of the property structures, +i.e. the methods named: alarm, .... enumeratedAlarm."

+ +

standardField.h contains:

+
class StandardField;
+typedef std::tr1::shared_ptr<StandardField> StandardFieldPtr;
+
+class StandardField {
+public:
+    static StandardFieldPtr getStandardField();
+    ~StandardField();
+    StructureConstPtr scalar(ScalarType type,std::string const &properties);
+    StructureConstPtr regUnion(
+        UnionConstPtr const & punion,
+        std::string const & properties);
+    StructureConstPtr variantUnion(std::string const & properties);
+    StructureConstPtr scalarArray(
+        ScalarType elementType, std::string const &properties);
+    StructureConstPtr structureArray(
+        StructureConstPtr const & structure,std::string const &properties);
+    StructureConstPtr unionArray(UnionConstPtr const & punion,std::string const & properties);
+    StructureConstPtr enumerated();
+    StructureConstPtr enumerated(std::string const &properties);
+    StructureConstPtr alarm();
+    StructureConstPtr timeStamp();
+    StructureConstPtr display();
+    StructureConstPtr control();
+    StructureConstPtr booleanAlarm();
+    StructureConstPtr byteAlarm();
+    StructureConstPtr ubyteAlarm();
+    StructureConstPtr shortAlarm();
+    StructureConstPtr ushortAlarm();
+    StructureConstPtr intAlarm();
+    StructureConstPtr uintAlarm();
+    StructureConstPtr longAlarm();
+    StructureConstPtr ulongAlarm();
+    StructureConstPtr floatAlarm();
+    StructureConstPtr doubleAlarm();
+    StructureConstPtr enumeratedAlarm();
+ ...
+};
+
+
scalar
+
Create a scalar with the specified scalar type and name. A structure + will be created with the first element being a scalar with the specified + scalar type and name value. The other fields in the structure will be the + corresponding property structures.
+
regUnion
+
A structure + will be created with the first element being a union with the specified + scalar type and name value. The other fields in the structure will be the + corresponding property structures.
+
varient`Union
+
Create a varient union. A structure + will be created with the first element being a union with the specified + scalar type and name value. The other fields in the structure will be the + corresponding property structures.
+
scalarArray
+
Create a scalarArray with each element having the specified scalar type + and name. A structure will be created with the first element being a + scalarArray with name value. The other fields in the structure will be + the corresponding property structures.
+
structureArray
+
Create a structureArray with the specified structure interface and + name. A structure will be created with the first element being a + structureArray with the specified structure interface and name value. The + other fields in the structure will be the corresponding property + structures.
+
unionArray
+
Create a unionArray with the specified union interface and + name. A structure will be created with the first element being a + unionArray with the specified structure interface and name value. The + other fields in the structure will be the corresponding property + structures.
+
structure
+
Create a structure with the specified name and fields specified by + numFields and fields. A structure will be created with the first element + being a structure with the name value and fields specified by numFields + and fields. The other fields in the structure will be the corresponding + property structures.
+
enumerated
+
Create a structure with the specified name and fields for an enumerated + structure. If properties are specified then a structure will be created + with the first element being a structure with the name value and fields + for an enumerated structure. The other fields in the structure will be + the corresponding property structures.
+
alarm
+
timeStamp
+
display
+
control
+
booleanAlarm
+
byteAlarm
+
shortAlarm
+
intAlarm
+
longAlarm
+
floatAlarm
+
doubleAlarm
+
enumeratedAlarm
+
The above provide introspection interfaces for standard properties. See + the section on Properties for a description of how these are defined.
+
+

pvData.h

+ +

This section describes pvData.h This file is quite big so rather than +showing the entire file, it will be described in parts.

+ +

typedefs

+ +

These are typedefs for Array and Ptr for the various pvData class +definitions, i.e. typdefs for "std::vector" and "std::tr1::shared_ptr".

+
+class PVField;
+class PVScalar;
+class PVScalarArray;
+class PVStructure;
+class PVStructureArray;
+
+
+typedef std::tr1::shared_ptr<PVField> PVFieldPtr;
+typedef std::vector<PVFieldPtr> PVFieldPtrArray;
+typedef std::vector<PVFieldPtr>::iterator PVFieldPtrArray_iterator;
+typedef std::vector<PVFieldPtr>::const_iterator PVFieldPtrArray_const__iterator;
+
+typedef std::tr1::shared_ptr<PVScalar> PVScalarPtr;
+typedef std::tr1::shared_ptr<PVScalarArray> PVScalarArrayPtr;
+
+typedef std::tr1::shared_ptr<PVStructure> PVStructurePtr;
+typedef std::vector<PVStructurePtr> PVStructurePtrArray;
+typedef std::vector<PVStructurePtr>::iterator PVStructurePtrArray_iterator;
+typedef std::vector<PVStructurePtr>::const_iterator PVStructurePtrArray_const__iterator;
+
+typedef PVValueArray<PVStructurePtr> PVStructureArray;
+typedef std::tr1::shared_ptr<PVStructureArray> PVStructureArrayPtr;
+typedef std::vector<PVStructureArrayPtr> PVStructureArrayPtrArray;
+typedef std::tr1::shared_ptr<PVStructureArrayPtrArray> PVStructureArrayPtrArrayPtr;
+
+typedef std::tr1::shared_ptr<PVUnion> PVUnionPtr;
+typedef std::vector<PVUnionPtr> PVUnionPtrArray;
+typedef std::vector<PVUnionPtr>::iterator PVUnionPtrArray_iterator;
+typedef std::vector<PVUnionPtr>::const_iterator PVUnionPtrArray_const__iterator;
+
+typedef PVValueArray<PVUnionPtr> PVUnionArray;
+typedef std::tr1::shared_ptr<PVUnionArray> PVUnionArrayPtr;
+typedef std::vector<PVUnionArrayPtr> PVUnionArrayPtrArray;
+typedef std::tr1::shared_ptr<PVUnionArrayPtrArray> PVUnionArrayPtrArrayPtr;
+
+class PVDataCreate;
+typedef std::tr1::shared_ptr<PVDataCreate> PVDataCreatePtr;
+
+
+/**
+ * typedefs for the various possible scalar types.
+ */
+typedef PVScalarValue<boolean> PVBoolean;
+typedef PVScalarValue<int8> PVByte;
+typedef PVScalarValue<int16> PVShort;
+typedef PVScalarValue<int32> PVInt;
+typedef PVScalarValue<int64> PVLong;
+typedef PVScalarValue<uint8> PVUByte;
+typedef PVScalarValue<uint16> PVUShort;
+typedef PVScalarValue<uint32> PVUInt;
+typedef PVScalarValue<uint64> PVULong;
+typedef PVScalarValue<float> PVFloat;
+typedef PVScalarValue<double> PVDouble;
+class PVString;
+typedef std::tr1::shared_ptr<PVBoolean> PVBooleanPtr;
+typedef std::tr1::shared_ptr<PVByte> PVBytePtr;
+typedef std::tr1::shared_ptr<PVShort> PVShortPtr;
+typedef std::tr1::shared_ptr<PVInt> PVIntPtr;
+typedef std::tr1::shared_ptr<PVLong> PVLongPtr;
+typedef std::tr1::shared_ptr<PVUByte> PVUBytePtr;
+typedef std::tr1::shared_ptr<PVUShort> PVUShortPtr;
+typedef std::tr1::shared_ptr<PVUInt> PVUIntPtr;
+typedef std::tr1::shared_ptr<PVULong> PVULongPtr;
+typedef std::tr1::shared_ptr<PVFloat> PVFloatPtr;
+typedef std::tr1::shared_ptr<PVDouble> PVDoublePtr;
+typedef std::tr1::shared_ptr<PVString> PVStringPtr;
+
+/**
+ * Definitions for the various scalarArray types.
+ */
+typedef PVValueArray<boolean> PVBooleanArray;
+typedef PVValueArray<int8> PVByteArray;
+typedef PVValueArray<int16> PVShortArray;
+typedef PVValueArray<int32> PVIntArray;
+typedef PVValueArray<int64> PVLongArray;
+typedef PVValueArray<uint8> PVUByteArray;
+typedef PVValueArray<uint16> PVUShortArray;
+typedef PVValueArray<uint32> PVUIntArray;
+typedef PVValueArray<uint64> PVULongArray;
+typedef PVValueArray<float> PVFloatArray;
+typedef PVValueArray<double> PVDoubleArray;
+typedef PVValueArray<std::string> PVStringArray;
+
+typedef std::tr1::shared_ptr<PVBooleanArray> PVBooleanArrayPtr;
+typedef std::tr1::shared_ptr<PVByteArray> PVByteArrayPtr;
+typedef std::tr1::shared_ptr<PVShortArray> PVShortArrayPtr;
+typedef std::tr1::shared_ptr<PVIntArray> PVIntArrayPtr;
+typedef std::tr1::shared_ptr<PVLongArray> PVLongArrayPtr;
+typedef std::tr1::shared_ptr<PVUByteArray> PVUByteArrayPtr;
+typedef std::tr1::shared_ptr<PVUShortArray> PVUShortArrayPtr;
+typedef std::tr1::shared_ptr<PVUIntArray> PVUIntArrayPtr;
+typedef std::tr1::shared_ptr<PVULongArray> PVULongArrayPtr;
+typedef std::tr1::shared_ptr<PVFloatArray> PVFloatArrayPtr;
+typedef std::tr1::shared_ptr<PVDoubleArray> PVDoubleArrayPtr;
+typedef std::tr1::shared_ptr<PVStringArray> PVStringArrayPtr;
+
+ +

PVField

+ +

PVField is the base interface for accessing data. A data structure consists +of a top level PVStructure. Every field of every structure of every top level +structure has a PVField associated with it.

+
+class PostHandler
+{
+public:
+    POINTER_DEFINITIONS(PostHandler);
+    virtual ~PostHandler(){}
+    virtual void postPut() = 0;
+};
+
+class PVField
+: virtual public Serializable,
+  public std::tr1::enable_shared_from_this<PVField>
+{
+public:
+   POINTER_DEFINITIONS(PVField);
+   virtual ~PVField();
+   std::string getFieldName() const ;
+   std::string getFullName() const;
+   std::size_t getFieldOffset() const;
+   std::size_t getNextFieldOffset() const;
+   std::size_t getNumberFields() const;
+   bool isImmutable() const;
+   virtual void setImmutable();
+   const FieldConstPtr & getField() const ;
+   PVStructure * getParent() const 
+   void postPut();
+   void setPostHandler(PostHandlerPtr const &postHandler);
+   virtual bool equals(PVField &pv);
+   std::ostream& dumpValue(std::ostream& o) const;
+ ...
+}
+std::ostream& operator<<(std::ostream& o, const PVField& f);
+
+ +

The public methods for PVField are:

+
+
~PVField
+
Destructor. Since shared pointers are used it should never be called by + user code.
+
getFieldName
+
Get the field name. If the field is a top level structure the field + name will be an empty string.
+
getFullName
+
Fully expand the name of this field using the + names of its parent fields with a dot '.' seperating + each name. +
+
getFieldOffset
+
Get offset of the PVField field within top level structure. Every field + within the PVStructure has a unique offset. The top level structure has + an offset of 0. The first field within the structure has offset equal to + 1. The other offsets are determined by recursively traversing each + structure of the tree.
+
getNextFieldOffset
+
Get the next offset. If the field is a scalar or array field then this + is just offset + 1. If the field is a structure it is the offset of the + next field after this structure. Thus (nextOffset - offset) is always + equal to the total number of fields within the field.
+
getNumberFields
+
Get the total number of fields in this field. This is nextFieldOffset - + fieldOffset.
+
isImmutable
+
Is the field immutable?
+
setImmutable
+
Make the field immutable. Once a field is immutable it can never be + changed since there is no method to again make it mutable. This is an + important design decision since it allows immutable array fields to share + the internal primitive data array.
+
getField
+
Get the reflection interface for the data.
+
getParent
+
Get the interface for the parent or null if this is the top level + PVStructure.
+
postPut
+
If a postHandler is registered it is called otherwise no action is + taken.
+
setPostHandler
+
Set the postHandler for the record. Only a single handler can be + registered. + PostHandler is a class that must be implemented by any code that calls setPostHandler. + It's single virtual method. postPut is called whenever PVField::postPut is called. +
+
equals
+
Compare this field with another field. The result will be true only if + the fields have exactly the same field types and if the data values are + equal.
+
dumpValue
+
Method for streams I/O.
+
+ +

PVScalar

+ +

This is the base class for all scalar data.

+
class PVScalar : public PVField {
+public:
+    POINTER_DEFINITIONS(PVScalar);
+    virtual ~PVScalar();
+    typedef PVScalar &reference;
+    typedef const PVScalar& const_reference;
+    const ScalarConstPtr getScalar() const ;
+    template<typename T>
+    T getAs() const;
+ ...
+}
+ +

where

+
+
getScalar
+
Get the introspection interface for the PVScalar.
+
getAs
+
Convert and return the scalar value in the requested type. + Result type is determined from the function template argument + which must be one of the ScalarType enums. + For example: +
+uint32 val = pv->getAs();
+
+
+ +
+ +

PVScalarValue

+ +

The interfaces for primitive data types are:

+
template<typename T>
+class PVScalarValue : public PVScalar {
+public:
+    POINTER_DEFINITIONS(PVScalarValue);
+    typedef T value_type;
+    typedef T* pointer;
+    typedef const T* const_pointer;
+    static const ScalarType typeCode;
+
+    virtual ~PVScalarValue() {}
+    virtual T get() const = 0;
+    virtual void put(T value) = 0;
+    std::ostream& dumpValue(std::ostream& o) const;
+    void operator>>=(T& value) const;
+    void operator<<=(T value);
+    template<typename T1>
+    T1 getAs() const;
+    template<typename T1>
+    void putFrom(T1 val);
+ ...
+}
+
+typedef PVScalarValue<boolean> PVBoolean;
+typedef PVScalarValue<int8> PVByte;
+typedef PVScalarValue<int16> PVShort;
+typedef PVScalarValue<int32> PVInt;
+typedef PVScalarValue<int64> PVLong;
+typedef PVScalarValue<uint8> PVUByte;
+typedef PVScalarValue<uint16> PVUShort;
+typedef PVScalarValue<uint32> PVUInt;
+typedef PVScalarValue<uint64> PVULong;
+typedef PVScalarValue<float> PVFloat;
+typedef PVScalarValue<double> PVDouble;
+typedef std::tr1::shared_ptr<PVBoolean> PVBooleanPtr;
+typedef std::tr1::shared_ptr<PVByte> PVBytePtr;
+typedef std::tr1::shared_ptr<PVShort> PVShortPtr;
+typedef std::tr1::shared_ptr<PVInt> PVIntPtr;
+typedef std::tr1::shared_ptr<PVLong> PVLongPtr;
+typedef std::tr1::shared_ptr<PVUByte> PVUBytePtr;
+typedef std::tr1::shared_ptr<PVUShort> PVUShortPtr;
+typedef std::tr1::shared_ptr<PVUInt> PVUIntPtr;
+typedef std::tr1::shared_ptr<PVULong> PVULongPtr;
+typedef std::tr1::shared_ptr<PVFloat> PVFloatPtr;
+typedef std::tr1::shared_ptr<PVDouble> PVDoublePtr;
+
+
+// PVString is special case, since it implements SerializableArray
+class PVString : public PVScalarValue<std::string>, SerializableArray {
+public:
+    virtual ~PVString() {}
+ ...
+};
+
+
+
+ +

where

+
+
get
+
Get the value stored in the object.
+
put
+
Change the value stored in the object.
+
dumpValue
+
ostream method.
+
operator>>=
+
get operator. For example: +
+double value;
+PVDoublePtr pvDouble;
+...
+pvDouble>>=value;
+
+
+
operator<<=
+
put operator. For example: +
+double value;
+PVDoublePtr pvDouble;
+...
+pvDouble<<=value;
+
+
+
getAs
+
Convert and return the scalar value in the requested type. + Result type is determined from the function template argument + which must be one of the ScalarType enums. + For example: +
+int32 val = pv->getAs<pvInt>>();
+
+
+
putFrom
+
Convert the scalar value in the requested type + and put the value into this PVScalarValue. + The source type is determined from the function template argument + which must be one of the ScalarType enums. + For example: +
+int32 val;
+pv->putFrom<pvInt>(val);
+
+ +
+
+

PVUnion

+

A PVUnion has a single subfield. +The Union introspection interface determines the possible +field types for the subfield. +If it is a varient union then any type is allowed and the +subfield name is normally any. +If it is not a varient union that the Union interface determines +the possible field types and names.

+
+class PVUnion : public PVField
+{
+public:
+    POINTER_DEFINITIONS(PVUnion);
+    virtual ~PVUnion();
+    typedef PVUnion & reference;
+    typedef const PVUnion & const_reference;
+
+    UnionConstPtr getUnion() const;
+    PVFieldPtr get() const;
+    
+    template<typename PVT>
+    std::tr1::shared_ptr<PVT> get() const;
+
+   PVFieldPtr select(int32 index);
+
+    template<typename PVT>
+    std::tr1::shared_ptr<PVT> select(int32 index);
+
+    PVFieldPtr select(std::string const & fieldName);
+
+    template<typename PVT>
+    std::tr1::shared_ptr<PVT> select(std::string const & fieldName);
+
+    int32 getSelectedIndex() const;
+    std::string getSelectedFieldName() const;
+    void set(PVFieldPtr const & value);
+    void set(int32 index, PVFieldPtr const & value);
+    void set(std::string const & fieldName, PVFieldPtr const & value);
+    virtual void serialize(
+        ByteBuffer *pbuffer,SerializableControl *pflusher) const ;
+    PVUnion(UnionConstPtr const & punion);
+};
+
+
+
getUnion
+
Get the introspection interface.
+
get
+
Get the curent field. A template version does the conversion. + NULL is returned if no field is selected or if the caller +' asks for the wrong type. +
+
select
+
Select and get the field by index or name. + A templated version does the conversion. + If the index is out of bounds or the name is not valid this methods throws an exception. + The method set should be used for variant unions rather than select. +
+
getSelectedIndex
+
Get the index of the currently selected field.
+
getSelectedFieldName
+
Get the name of the currently selected field.
+
set
+
Set the field to the argument. If invalid type an exception is thrown. + This should always work for a variant union. +
+
+ +

PVArray

+ +

PVArray is the base interface for all the other PV Array interfaces. It +extends PVField and provides the additional methods:

+
+class PVArray : public PVField, public SerializableArray {
+public:
+    POINTER_DEFINITIONS(PVArray);
+    virtual ~PVArray();
+    virtual ArrayConstPtr getArray() const = 0;
+    virtual void setImmutable();
+    std::size_t getLength() const;
+    virtual void setLength(std::size_t length);
+    std::size_t getCapacity() const;
+    bool isCapacityMutable() const;
+    void setCapacityMutable(bool isMutable);
+    virtual void setCapacity(std::size_t capacity) = 0;
+    virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const;
+ ...
+};
+
+
getArray
+
Get the introspection interface.
+
setImmutable
+
Set the data immutable. Note that this is permanent since there is no + methods to make it mutable.
+
getLength
+
Get the current length. This is less than or equal to the capacity.
+
setLength
+
Set the length. If the PVField is not mutable then an exception is + thrown. If this is greater than the capacity setCapacity is called.
+
getCapacity
+
Get the capacity, i.e. this is the size of the underlying data + array.
+
setCapacity
+
Set the capacity. The semantics are implementation dependent but + typical semantics are as follows: If the capacity is not mutable an + exception is thrown. A new data array is created and data is copied from + the old array to the new array.
+
isCapacityMutable
+
Is the capacity mutable
+
setCapacityMutable
+
Specify if the capacity can be changed.
+
setCapacity
+
Set the capaciity.
+
dumpValue
+
ostream method
+
+

+ + +

PVScalarArray

+ +

PVScalarArray is the base class for scalar array data. PVValueArray is a +templete for the various scalar array data classes. There is a class for each +possible scalar type, i. e. PVBooleanArray, ..., PVStringArray.

+
+class PVScalarArray : public PVArray {
+public:
+    POINTER_DEFINITIONS(PVScalarArray);
+    virtual ~PVScalarArray();
+    typedef PVScalarArray &reference;
+    typedef const PVScalarArray& const_reference;
+
+    const ScalarArrayConstPtr getScalarArray() const ;
+
+    template<typename T>
+    void getAs(shared_vector<const T>& out) const
+
+    template<typename T>
+    void putFrom(const shared_vector<const T>& inp)
+
+    void assign(PVScalarArray& pv);
+ ...
+}
+
+ +

where

+
+
getScalarArray
+
Get the introspection interface.
+
getAs
+
Fetch the current value and convert to the requested type. + A copy is made if the requested type does not match the element type. + If the types do match then no copy is made. +
+
putFrom
+
Assign the given value after conversion. + A copy and element-wise conversion is performed unless the element type + of the PVScalarArray matches the type of the provided data. + If the types do match then a new refernce to the provided data is kept. +
+
assign
+
Assign the given PVScalarArray's value. + A copy and element-wise conversion is performed unless the element type + of the PVScalarArray matches the type of the provided data. + If the types do match then a new refernce to the provided data is kept. +
+
+ +

PVStructure

+ +

The interface for a structure is:

+
class PVStructure : public PVField,public BitSetSerializable {
+public:
+    POINTER_DEFINITIONS(PVStructure);
+    virtual ~PVStructure();
+    typedef PVStructure & reference;
+    typedef const PVStructure & const_reference;
+
+    virtual void setImmutable();
+    StructureConstPtr getStructure() const;
+    const PVFieldPtrArray & getPVFields() const;
+    PVFieldPtr getSubField(std::string const &fieldName) const;
+
+    template<typename PVT>
+    std::tr1::shared_ptr<PVT> getSubField(std::string const &fieldName) const
+
+    PVFieldPtr getSubField(std::size_t fieldOffset) const;
+
+    template<typename PVT>
+    std::tr1::shared_ptr<PVT> getSubField(std::size_t fieldOffset) const
+
+    virtual void serialize(
+        ByteBuffer *pbuffer,SerializableControl *pflusher) const ;
+    virtual void deserialize(
+        ByteBuffer *pbuffer,DeserializableControl *pflusher);
+    virtual void serialize(ByteBuffer *pbuffer,
+        SerializableControl *pflusher,BitSet *pbitSet) const;
+    virtual void deserialize(ByteBuffer *pbuffer,
+        DeserializableControl*pflusher,BitSet *pbitSet);
+    PVStructure(StructureConstPtr const & structure);
+    PVStructure(StructureConstPtr const & structure,PVFieldPtrArray const & pvFields);
+    virtual std::ostream& dumpValue(std::ostream& o) const;
+};
+ +

where

+
+
getStructure
+
Get the introspection interface for the structure.
+
getPVFields
+
Returns the array of subfields. The set of subfields must all have + different field names.
+
getSubField(std::string fieldName)
+
+ Get a subField of a field.d + A non-null result is + returned if fieldName is a field of the PVStructure. The fieldName can be + of the form name.name... + If the field does not exist the a Ptr to a NULL value is returned + without any error message being generated. +
+ Note that the template version replaces getBooleanField, etc.
+
+
getSubField(int fieldOffset)
+
Get the field located a fieldOffset, where fieldOffset is relative to + the top level structure. This returns null if the specified field is not + located within this PVStructure. +
+
dumpValue
+
Method for streams I/O.
+
+ + +

PVValueArray

+ +

This is a template class plus instances for PVBooleanArray, ..., +PVStringArray.

+
template<typename T>
+class PVValueArray : public detail::PVVectorStorage<T,PVScalarArray> 
+{
+public:
+    POINTER_DEFINITIONS(PVValueArray);
+    typedef T  value_type;
+    typedef T* pointer;
+    typedef const T* const_pointer;
+    typedef ::epics::pvData::shared_vector<T> svector;
+    typedef ::epics::pvData::shared_vector<const T> const_svector;
+    static const ScalarType typeCode;
+
+    virtual ~PVValueArray() {}
+    std::ostream& dumpValue(std::ostream& o) const;
+    std::ostream& dumpValue(std::ostream& o, size_t index) const;
+    // inherited from PVVectorStorage
+    const_svector view();
+    void swap(const_svector& other);
+    void replace(const const_svector& next);
+    svector reuse();
+    ...
+};
+
+ +

where

+
+
dumpValue
+
Method for streams I/O.
+
view
+
Fetch a read-only view of the current array data.
+
swap
+
+ Callers must ensure that postPut() is called after the last swap() operation. + Before you call this directly, consider using the reuse(), or replace() methods. +
+
replace
+
Discard current contents and replaced with the provided.
+
reuse
+
Remove and return the current array data or an unique copy if shared.
+
+

TBD +

+
Check for completeness
+
Michael should check that PVScalarArray and PVValueArray + have the correct set of methods and that the descriptions are correct.
+
+

+ +

PVStructureArray

+ +

The interface for an array of structures is:

+
+template<>
+class PVValueArray<PVStructurePtr> : public detail::PVVectorStorage<PVStructurePtr,PVArray>
+{
+public:
+    POINTER_DEFINITIONS(PVStructureArray);
+    typedef PVStructurePtr  value_type;
+    typedef PVStructurePtr* pointer;
+    typedef const PVStructurePtr* const_pointer;
+    typedef PVStructureArray &reference;
+    typedef const PVStructureArray& const_reference;
+    typedef ::epics::pvData::shared_vector<PVStructurePtr> svector;
+    typedef ::epics::pvData::shared_vector<const PVStructurePtr> const_svector;
+
+    virtual ~PVStructureArray() {}
+    virtual ArrayConstPtr getArray() const;
+    virtual size_t getLength();
+    virtual size_t getCapacity();
+    virtual void setCapacity(size_t capacity);
+    virtual void setLength(std::size_t length);
+    virtual StructureArrayConstPtr getStructureArray() const ;
+    virtual std::size_t append(std::size_t number);
+    virtual bool remove(std::size_t offset,std::size_t number);
+    virtual void compress();
+    virtual const_svector view() const;
+    virtual void swap(const_svector &other);
+    virtual void replace(const const_svector &other);
+    virtual void serialize(ByteBuffer *pbuffer,
+        SerializableControl *pflusher) const;
+    virtual void serialize(ByteBuffer *pbuffer,
+        SerializableControl *pflusher, std::size_t offset, std::size_t count) const ;
+    virtual void deserialize(ByteBuffer *buffer,
+        DeserializableControl *pflusher);
+    virtual std::ostream& dumpValue(std::ostream& o) const;
+    virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const;
+    // inherited from PVVectorStorage
+    const_svector view();
+    void swap(const_svector& other);
+    void replace(const const_svector& next);
+    svector reuse();
+ ...
+}
+

where

+
+
getStructureArray
+
Get the introspection interface shared by each element.
+
compress
+
This moves all null elements and then changes the array capacity. When + done there are no null elements.
+
+ +

The other methods are similar to the methods for other array types. + See PVArray above for details.

+ +

PVUnionArray

+

The interface for an array of unions is:

+
+template&ly;&gt;
+class epicsShareClass PVValueArray&ly;PVUnionPtr&gt; : public detail::PVVectorStorage&ly;PVUnionPtr,PVArray&gt;
+{
+    typedef detail::PVVectorStorage&ly;PVUnionPtr,PVArray&gt; base_t;
+public:
+    POINTER_DEFINITIONS(PVUnionArray);
+    typedef PVUnionPtr  value_type;
+    typedef PVUnionPtr* pointer;
+    typedef const PVUnionPtr* const_pointer;
+    typedef PVUnionArray &reference;
+    typedef const PVUnionArray& const_reference;
+
+    //TODO: full namespace can be removed along with local typedef 'shared_vector'
+    typedef ::epics::pvData::shared_vector&ly;PVUnionPtr&gt; svector;
+    typedef ::epics::pvData::shared_vector&ly;const PVUnionPtr&gt; const_svector;
+    
+    virtual ~PVValueArray() {}
+    virtual ArrayConstPtr getArray() const;
+    virtual size_t getLength() const;
+    virtual size_t getCapacity() const;
+    virtual void setCapacity(size_t capacity);
+    virtual void setLength(std::size_t length);
+    UnionArrayConstPtr getUnionArray() const;
+    virtual std::size_t append(std::size_t number);
+    virtual bool remove(std::size_t offset,std::size_t number);
+    virtual void compress();
+    virtual const_svector view() const;
+    virtual void swap(const_svector &other);
+    virtual void replace(const const_svector &other);
+    virtual void serialize(ByteBuffer *pbuffer,
+        SerializableControl *pflusher) const;
+    virtual void deserialize(ByteBuffer *buffer,
+        DeserializableControl *pflusher);
+    virtual void serialize(ByteBuffer *pbuffer,
+        SerializableControl *pflusher, std::size_t offset, std::size_t count) const ;
+    virtual std::ostream& dumpValue(std::ostream& o) const;
+    virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const;
+};
+
+

where

+
+
getUnionArray
+
Get the introspection interface shared by each element.
+
compress
+
This moves all null elements and then changes the array capacity. When + done there are no null elements.
+
+ +

The other methods are similar to the methods for other array types. + See PVArray above for details.

+ +

PVDataCreate

+ +

PVDataCreate is an interface that provides methods that create PVField +interfaces. A factory is provided that creates PVDataCreate.

+
class PVDataCreate {
+public:
+    static PVDataCreatePtr getPVDataCreate();
+
+    PVFieldPtr createPVField(FieldConstPtr const & field);
+    PVFieldPtr createPVField(PVFieldPtr const & fieldToClone);
+
+    PVScalarPtr createPVScalar(ScalarConstPtr const & scalar);
+    PVScalarPtr createPVScalar(ScalarType scalarType);
+    PVScalarPtr createPVScalar(PVScalarPtr const & scalarToClone);
+    template<typename PVT>
+    std::tr1::shared_ptr<PVT> createPVScalar();
+
+    PVStructurePtr createPVStructure(
+        StringArray const & fieldNames,PVFieldPtrArray const & pvFields);
+    PVStructurePtr createPVStructure(PVStructurePtr const & structToClone);
+    PVStructurePtr createPVStructure(StructureConstPtr const & structure);
+
+    PVUnionPtr createPVUnion(UnionConstPtr const & punion);
+    PVUnionPtr createPVUnion(PVUnionPtr const & unionToClone);
+    PVUnionPtr createPVVariantUnion();
+
+    PVScalarArrayPtr createPVScalarArray(ScalarArrayConstPtr const & scalarArray);
+    PVScalarArrayPtr createPVScalarArray(ScalarType elementType);
+    PVScalarArrayPtr createPVScalarArray(PVScalarArrayPtr const  & scalarArrayToClone);
+    template<typename PVAT>
+    std::tr1::shared_ptr<PVAT> createPVScalarArray();
+
+    PVStructureArrayPtr createPVStructureArray(StructureArrayConstPtr const & structureArray);
+    PVStructureArrayPtr createPVStructureArray(StructureConstPtr const ∓ structure);
+
+    PVUnionArrayPtr createPVUnionArray(UnionArrayConstPtr const & unionArray);
+    PVUnionArrayPtr createPVUnionArray(UnionConstPtr const & punion);
+    PVUnionArrayPtr createPVVariantUnionArray();
+ ...
+};
+
+extern PVDataCreatePtr getPVDataCreate();
+ +

where

+
+
getPVDataCreate
+
The returns the PVDataCreate implementation, which is a singleton.
+
createPVField
+
The PVField is created reusing the Field interface. Two methods are + provided. Each calls the corresponding createPVScalar, createPVArray, or + createPVStructure depending in the type of the last argument.
+
createPVScalar
+
Creates an instance of a PVScalar. Four versions are supplied. The + first is passed an introspection interface. + The second provides the scalarType. + The third provides a PVScalar to clone. + The last is a template version. PVAT must be a valid type. + For example: +
+PVDoublePtr pvDouble = getPVDataCreate()->createPVScalar<PVDouble>();
+
+
+
createPVStructure
+
Create an instance of a PVStructure. + Three methods are provided. + The first uses an array of field names and an array of PVFields to initialize the sub-fields. + The second initializes the subfields by cloning the fields contained in + structToClone. The newly created sub-fields will have the same values as the original. + If structToClone is null then the new structure is initialized to have 0 sub-fields. + The third method uses a previously created structure introspection interface. +
+
createPVUnion
+
Create an instance of a PVUnion. Two methods are provided. + The first uses a previously created union introspection interface. + The second clones an existing PVUnion. +
+
createPVVariantUnion
+
Creates an instance of a varient PVUnion. + This is a union which has a single field which can be any pvData supported type, +
+
createPVScalarArray
+
Create an instance of a PVArray. Four versions are supplied. + The first is passed an introspection interface. + The second provides the elementType. + The third provides a PVScalarArray to clone. + The last is a template version. PVAT must be a valid type. + For example: +
+PVDoubleArrayPtr pvDoubleArray = getPVDataCreate()->createPVScalarArray<PVDoubleArray>();
+
+
+
createPVStructureArray
+
Create a PVStructureArray. + Two versions are provided. + The first is passed a StructureArrayConstPtr. + The second is passed a StructureConstPtr which is used to create a StructureArrayConstPtr. + The argument provides the Structure interface for ALL elements of the PVStructureArray. +
+
createPVUnionArray
+
Create a PVUnionArray. + Two versions are provided. + The first is passed a UnionArrayConstPtr. + The second is passed a UnionConstPtr which is used to create a UnionArrayConstPtr. + The argument provides the Union interface for ALL elements of the PVUnionArray. +
+
createPVVariantUnionArray
+
Create a PVUnionArray. + No arguments are needed. +
+
+ +

standardPVField.h

+ +

A class StandardPVField has methods for creating standard data fields. Like +class StandardField it has two forms of the methods which create a field, one +without properties and one with properties. Again the properties is some +combination of alarm, timeStamp, control, display, and valueAlarm. And just +like StandardField there are methods to create the standard properties. The +methods are:

+
class StandardPVField;
+typedef std::tr1::shared_ptr<StandardPVField> StandardPVFieldPtr;
+
+class StandardPVField : private NoDefaultMethods {
+public:
+    static StandardPVFieldPtr getStandardPVField();
+    ~StandardPVField();
+    PVStructurePtr scalar(ScalarType type,std::string const &properties);
+    PVStructurePtr scalarArray(ScalarType elementType, std::string const &properties);
+    PVStructurePtr structureArray(StructureConstPtr const &structure,std::string const &properties);
+    PVStructurePtr unionArray(UnionConstPtr const &punion,std::string const &properties);
+    PVStructurePtr enumerated(StringArray const &choices);
+    PVStructurePtr enumerated(StringArray const &choices, std::string const &properties);
+ ...
+}
+
+extern StandardPVFieldPtr getStandardPVField();
+
+ +

Conversion

+

There are two facilities for converting between two different PVData +objects:

+
+
Convert
+
This preforms all conversions except for coping subarrays.
+
PVSubArray
+
This copies a subarray from one PVArray to another. + The two arrays must have the same element type.
+
+

convert.h

+ +

NOTE about copying immutable array fields. If an entire immutable array +field is copied to another array that has the same elementType, both offsets +are 0, and the length is the length of the source array, then the shareData +method of the target array is called and the target array is set immutable. +Thus the source and target share the same primitive array.

+ +

This section describes the supported conversions between data types.

+
    +
  • All supported types can be converted to a string. If you ask for a 100 + megabyte array to be converted to a string expect a lot of output.
  • +
  • Conversion from a string to a scalar type.
  • +
  • Conversion from an array of strings to an array of scalar types.
  • +
  • Copy between the following types of scalar PVs +
      +
    • Numeric type to another numeric type
    • +
    • Both have the same type.
    • +
    • Either is a string
    • +
    +
  • +
  • Copy between PVArrays that satisfy one of the following. +
      +
    • Both have the same type.
    • +
    • Either is a string.
    • +
    +
  • +
  • Conversions between numeric scalar types.
  • +
  • Conversion between compatible structures.
  • +
  • A utility method the returns the full field name of a field
  • +
+
+bool operator==(PVField&, PVField&);
+
+static bool operator!=(PVField& a, PVField& b);
+
+bool operator==(const Field&, const Field&);
+bool operator==(const Scalar&, const Scalar&);
+bool operator==(const ScalarArray&, const ScalarArray&);
+bool operator==(const Structure&, const Structure&);
+bool operator==(const StructureArray&, const StructureArray&);
+bool operator==(const Union&, const Union&);
+bool operator==(const UnionArray&, const UnionArray&);
+
+static inline bool operator!=(const Field& a, const Field& b);
+static inline bool operator!=(const Scalar& a, const Scalar& b);
+static inline bool operator!=(const ScalarArray& a, const ScalarArray& b);
+static inline bool operator!=(const Structure& a, const Structure& b);
+static inline bool operator!=(const StructureArray& a, const StructureArray& b);
+static inline bool operator!=(const Union& a, const Union& b);
+static inline bool operator!=(const UnionArray& a, const UnionArray& b);
+
+class Convert;
+typedef std::tr1::shared_ptr<Convert> ConvertPtr;
+
+class Convert {
+public:
+    static ConvertPtr getConvert();
+    ~Convert();
+    void getFullName(std::string * buf,PVFieldPtr const & pvField);
+    bool equals(PVFieldPtr const &a,PVFieldPtr const &b);
+    bool equals(PVField &a,PVField &b);
+    void getString(std::string * buf,PVFieldPtr const & pvField,int indentLevel);
+    void getString(std::string * buf,PVFieldPtr const & pvField);
+    void getString(std::string * buf,PVField const * pvField,int indentLevel);
+    void getString(std::string * buf,PVField const * pvField);
+    std::size_t fromString(
+        PVStructurePtr const &pv,
+        StringArray const & from,
+        std::size_t fromStartIndex = 0);
+    void fromString(PVScalarPtr const & pv, std::string const & from);
+    std::size_t fromString(PVScalarArrayPtr const & pv, std::string const &from);
+    std::size_t fromStringArray(
+        PVScalarArrayPtr const & pv,
+        std::size_t offset, std::size_t length,
+        StringArray const & from,
+        std::size_t fromOffset);
+    std::size_t toStringArray(PVScalarArrayPtr const & pv,
+        std::size_t offset,
+        std::size_t length,
+        StringArray & to,
+        std::size_t toOffset);
+    bool isCopyCompatible(FieldConstPtr const & from, FieldConstPtr const & to);
+    void copy(PVFieldPtr const & from, PVFieldPtr const & to);
+    bool isCopyScalarCompatible(
+        ScalarConstPtr const & from,
+        ScalarConstPtr const & to);
+    void copyScalar(PVScalarPtr const & from, PVScalarPtr const & to);
+    bool isCopyScalarArrayCompatible(
+        ScalarArrayConstPtr const & from,
+        ScalarArrayConstPtr const & to);
+    bool isCopyStructureCompatible(
+        StructureConstPtr const & from, StructureConstPtr const & to);
+    void copyStructure(PVStructurePtr const & from, PVStructurePtr const & to);
+    bool isCopyStructureArrayCompatible(
+        StructureArrayConstPtr const & from, StructureArrayConstPtr const & to);
+    void copyStructureArray(
+        PVStructureArrayPtr const & from, PVStructureArrayPtr const & to);
+    bool isCopyUnionCompatible(
+        UnionConstPtr const & from, UnionConstPtr const & to);
+    void copyUnion(
+        PVUnionPtr const & from, PVUnionPtr const & to);
+    bool isCopyUnionArrayCompatible(
+        UnionArrayConstPtr const & from, UnionArrayConstPtr const & to);
+    void copyUnionArray(
+        PVUnionArrayPtr const & from, PVUnionArrayPtr const & to);
+    int8 toByte(PVScalarPtr const & pv);
+    int16 toShort(PVScalarPtr const & pv);
+    int32 toInt(PVScalarPtr const & pv);
+    int64 toLong(PVScalarPtr const & pv);
+    uint8 toUByte(PVScalarPtr const & pv);
+    uint16 toUShort(PVScalarPtr const & pv);
+    uint32 toUInt(PVScalarPtr const & pv);
+    uint64 toULong(PVScalarPtr const & pv);
+    float toFloat(PVScalarPtr const & pv);
+    double toDouble(PVScalarPtr const & pv);
+    std::string toString(PVScalarPtr const & pv);
+    void fromByte(PVScalarPtr const & pv,int8 from);
+    void fromShort(PVScalarPtr const & pv,int16 from);
+    void fromInt(PVScalarPtr const & pv, int32 from);
+    void fromLong(PVScalarPtr const & pv, int64 from);
+    void fromUByte(PVScalarPtr const & pv,uint8 from);
+    void fromUShort(PVScalarPtr const & pv,uint16 from);
+    void fromUInt(PVScalarPtr const & pv, uint32 from);
+    void fromULong(PVScalarPtr const & pv, uint64 from);
+    void fromFloat(PVScalarPtr const & pv, float from);
+    void fromDouble(PVScalarPtr const & pv, double from);
+    void newLine(std::string * buf, int indentLevel);
+ ...
+}
+
+extern ConvertPtr getConvert();
+
+

newLine is a convenience method for code that implements toString It +generates a newline and inserts blanks at the beginning of the newline.

+ +

pvSubArrayCopy.h

+
+template<typename T>
+void copy(
+    PVValueArray<T> & pvFrom,
+    size_t fromOffset,
+    size_t fromStride,
+    PVValueArray<T> & pvTo,
+    size_t toOffset,
+    size_t toStride,
+    size_t count);
+
+void copy(
+    PVScalarArray & from,
+    size_t fromOffset,
+    size_t fromStride,
+    PVScalarArray & to,
+    size_t toOffset,
+    size_t toStride,
+    size_t count);
+
+void copy(
+    PVStructureArray & from,
+    size_t fromOffset,
+    size_t fromStride,
+    PVStructureArray & to,
+    size_t toOffset,
+    size_t toStride,
+    size_t count);
+
+void copy(
+    PVArray & from,
+    size_t fromOffset,
+    size_t fromStride,
+    PVArray & to,
+    size_t toOffset,
+    size_t toStride,
+    size_t count);
+
+

The last copy is the only one most client need to call. +It either throws an error of the element types do not match or calls the +other copy functions. The arguments are:

+
+
from
+
The source array.
+
fromOffset
+
The offset into the source array.
+
fromStride
+
The interval between source elements.
+
to
+
The destination array. The element type must be the same + as for the source array. If the element type is structure then + the introspection interface for the element types must be the same. +
+
toOffset
+
The offset into the destination array.
+
toStride
+
The interval between destination elements.
+
count
+
The number of elements to copy.
+
+

An exception is thrown if:

+
+
type mismatch
+
The element types for the source and destination differ.
+
immutable
+
The destination array is immutable. +
capacity immutable
+
The destination array needs to have it's capacity extentended + but the capacity is immutable.
+
+ +

pvDataApp/property

+ +

Definition of Property

+ +

Only fields named "value" have properties. A record can have multiple value +fields, which can appear in the top level structure of a record or in a +substructure. All other fields in the structure containing a value field are +considered properties of the value field. The fieldname is also the property +name. The value field can have any type, i.e. scalar, scalarArray, or +structure. Typical property fields are timeStamp, alarm, display, control, and +history. The timeStamp is a special case. If it appears anywhere in the +structure hieraracy above a value field it is a property of the value field.

+ +

For example the following top level structure has a single value field. The +value field has properties alarm, timeStamp, and display.

+
structure counterOutput
+    double value
+    alarm_t
+        int severity 0
+        int status 0
+        string message
+    timeStamp_t
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+    display_t
+        double limitLow 0.0
+        double limitHigh 10.0
+        string description "Sample Description"
+        string format "%f"
+        string units volts
+ +

The following example has three value fields each with properties alarm and +timeStamp. Voltage, Current, and Power each have a different alarms but all +share the timeStamp.

+
structure powerSupplyValue
+    double value
+    alarm_t
+        int severity 0
+        int status 0
+        string message
+
+structure powerSupplySimple
+    alarm_t
+        int severity 0
+        int status 0
+        string message
+    timeStamp_t
+        long secondsPastEpoch
+        int nanoSeconds
+        int userTag
+    powerSupplyValue_t voltage
+        double value
+        alarm_t
+            int severity 0
+            int status 0
+            string message
+    powerSupplyValue_t power
+        double value
+        alarm_t
+            int severity 0
+            int status 0
+            string message
+    powerSupplyValue_t current
+        double value
+        alarm_t
+            int severity 0
+            int status 0
+            string message
+ +

Standard Properties

+ +

The following field names have special meaning, i.e. support properties for +general purpose clients.

+
+
value
+
This is normally defined since most general purpose clients access this + field. All other fields in the structure support or describe the value + field. The type can any supported type but is usually one of the + following: +
+
scalar
+
Any of the scalar types.
+
scalarArray
+
An array with the elementType being a scalar type
+
enumerated structure
+
A structure that includes fields named index and choices. index + is an int that selects a choice. choices is an array of strings + that defines the complete set of choices.
+
other
+
Other structure or array types can also be defined if clients and + support code agree on the meaning. Some examples are: 1) A + structure defining a 2D matrix, 2) A structure defining an image, + 3) A structure that simulates a remote method, ...
+
+
+
timeStamp
+
The timeStamp. The type MUST be a timeStamp structure. Also if the + PVData structure does not have a timeStamp then a search up the parent + tree is made to find a timeStamp.
+
alarm
+
The alarm. The type MUST be an alarm structure.
+
display
+
A display structure as described below. It provides display + characteristics for the value field.
+
control
+
A control structure as described below. It provides control + characteristics for the value field.
+
history
+
Provides a history buffer for the value field. Note that currently + PVData does not define history suppoprt.
+
other
+
Other standard properties can be defined.
+
+ +

In addition a structure can have additional fields that support the value +field but are not recognized by most general purpose client tools. Typical +examples are:

+
+
input
+
A field with support that changes the value field. This can be + anything. It can be a channel access link. It can obtain a value from + hardware. Etc.
+
valueAlarm
+
A field with support that looks for alarm conditions based on the + value.
+
output
+
A field with support that reads the current value and sends it + somewhere else. This can be anything. It can be a channel access link. It + can write a value to hardware. Etc.
+
+ +

The model allows for device records. A device record has structure fields +that that support the PVData data model. For example a powerSupport record can +have fields power, voltage, current that each support the PVData data model. +

+ +

Overview of Property Support

+ +

Except for enumerated, each property has two files: a property.h and a +pvProperty.h . For example: timeStamp.h and pvTimeStamp.h In each case the +property.h file defined methods for manipulating the property data and the +pvProperty.h provides methods to transfer the property data to/from a pvData +structure.

+ +

All methods copy data via copy by value semantics, i.e. not by pointer or by +reference. No property class calls new or delete and all allow the compiler to +generate default methods. All allow a class instance to be generated on the +stack. For example the following is permitted:

+
void example(PVFieldPtr const &pvField) {
+    Alarm alarm;
+    PVAlarm pvAlarm;
+    bool result;
+    result = pvAlarm.attach(pvField);
+    assert(result);
+    Alarm al;
+    al.setMessage(std::string("testMessage"));
+    al.setSeverity(majorAlarm);
+    result = pvAlarm.set(al);
+    assert(result);
+    alarm = pvAlarm.get();
+     ...
+}
+ +

timeStamp

+ +

A timeStamp is represented by the following structure

+
structure timeStamp
+    long secondsPastEpoch
+    int nanoSeconds
+    int userTag
+ +

The Epoch is the posix epoch, i.e. Jan 1, 1970 00:00:00 UTC. Both the +seconds and nanoSeconds are signed integers and thus can be negative. Since the +seconds is kept as a 64 bit integer, it allows for a time much greater than the +present age of the universe. Since the nanoSeconds portion is kept as a 32 bit +integer it is subject to overflow if a value that corresponds to a value that +is greater than a little more than 2 seconds of less that about -2 seconds. The +support code always adjust seconds so that the nanoSecconds part is normlized, +i. e. it has is 0<=nanoSeconds<nanoSecPerSec..

+ +

Two header files are provided for manipulating time stamps:

+
+
timeStamp.h
+
Defines a time stamp independent of pvData, i.e. it is a generally + useful class for manipulating timeStamps.
+
pvTimeStamp.h
+
A class that can be attached to a time stamp pvData structure. It + provides get and set methods to get/set a TimeStamp as defined by + timeStamp.h
+
+ +

timeStamp.h

+ +

This provides

+
extern int32 milliSecPerSec;
+extern int32 microSecPerSec;
+extern int32 nanoSecPerSec;
+extern int64 posixEpochAtEpicsEpoch;
+
+class TimeStamp {
+public:
+    TimeStamp()
+    :secondsPastEpoch(0), nanoSeconds(0), userTag(0) {}
+    TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds = 0,int32 userTag = 0);
+    //default constructors and destructor are OK
+    //This class should not be extended
+    void normalize();
+    void fromTime_t(const time_t &);
+    void toTime_t(time_t &) const;
+    int64 getSecondsPastEpoch() const {return secondsPastEpoch;}
+    int64 getEpicsSecondsPastEpoch() const {
+        return secondsPastEpoch - posixEpochAtEpicsEpoch;
+    }
+    int32 getNanoSeconds() const  {return nanoSeconds;}
+    int32 getUserTag() const {return userTag;}
+    void setUserTag(int userTag) {this->userTag = userTag;}
+    void put(int64 secondsPastEpoch,int32 nanoSeconds = 0) {
+        this->secondsPastEpoch = secondsPastEpoch;
+        this->nanoSeconds = nanoSeconds;
+        normalize();
+    }
+    void put(int64 milliseconds);
+    void getCurrent();
+    double toSeconds() const ;
+    bool operator==(TimeStamp const &) const;
+    bool operator!=(TimeStamp const &) const;
+    bool operator<=(TimeStamp const &) const;
+    bool operator< (TimeStamp const &) const;
+    bool operator>=(TimeStamp const &) const;
+    bool operator> (TimeStamp const &) const;
+    static double diff(TimeStamp const & a,TimeStamp const & b);
+    TimeStamp & operator+=(int64 seconds);
+    TimeStamp & operator-=(int64 seconds);
+    TimeStamp & operator+=(double seconds);
+    TimeStamp & operator-=(double seconds);
+    int64 getMilliseconds(); // milliseconds since epoch
+ ...
+}
+ +

where

+
+
TimeStamp()
+
The defauly constuctor. Both seconds and nanoSeconds are set to 0.
+
TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds = 0)
+
A constructor that gives initial values to seconds and nanoseconds.
+
normalize
+
Adjust seconds and nanoSeconds so that + 0<=nanoSeconds<nanoSecPerSec.
+
fromTime_t
+
Set time from standard C time.
+
toTime_t
+
Convert timeStamp to standard C time.
+
getSecondsPastEpoch
+
Get the number of seconds since the epoch.
+
getEpicsSecondsPastEpoch
+
Get the number of EPICS seconds since the epoch. EPICS uses Jan 1, 1990 + 00:00:00 UTC as the epoch.
+
getNanoSeconds
+
Get the number of nanoSeconds. This is always normalized.
+
getUserTag
+
Get the userTag.
+
setUserTag
+
Set the userTag.
+
put(int64 secondsPastEpoch,int32 nanoSeconds = 0)
+
Set the timeStamp value. If necessary it will be normalized.
+
put(int64 milliseconds)
+
Set the timeStamp with a value the is the number of milliSeconds since + the epoch.
+
getCurrent()
+
Set the timeStamp to the current time.
+
toSeconds()
+
Convert the timeStamp to a value that is the number of seconds since + the epocj
+
operator =
+
operator!=
+
operator<=
+
operator<
+
operator>=
+
operator>
+
Standard C++ operators.
+
diff
+
diff = a - b
+
getMilliseconds
+
Get the number of milliseconds since the epoch.
+
+ +

The TimeStamp class provides arithmetic operations on time stamps. The +result is always kept in normalized form, which means that the nano second +portion is 0≤=nano<nanoSecPerSec. Note that it is OK to have timeStamps +for times previous to the epoch.

+ +

TimeStamp acts like a primitive. It can be allocated on the stack and the +compiler is free to generate default methods, i.e. copy constructor, assignment +constructor, and destructor.

+ +

One use for TimeStamp is to time how long a section of code takes to +execute. This is done as follows:

+
    TimeStamp startTime;
+    TimeStamp endTime;
+    ...
+    startTime.getCurrent();
+    // code to be measured for elapsed time
+    endTime.getCurrent();
+    double time = TimeStamp::diff(endTime,startTime);
+ +

pvTimeStamp.h

+
class PVTimeStamp {
+public:
+    PVTimeStamp();
+    //default constructors and destructor are OK
+    //This class should not be extended
+    //returns (false,true) if pvField(isNot, is valid timeStamp structure
+    bool attach(PVFieldPtr const &pvField);
+    void detach();
+    bool isAttached();
+    // following throw logic_error if not attached to PVField
+    // a set returns false if field is immutable
+    void get(TimeStamp &) const;
+    bool set(TimeStamp const & timeStamp);
+};
+ +

where

+
+
PVTimeStamp
+
The default constructor. Attach must be called before get or set can be + called.
+
attach
+
Attempts to attach to pvField It returns (false,true) if a timeStamp + structure is found. It looks first at pvField itself and if is not an + appropriate pvData structure but the field name is value it looks up the + parent structure tree.
+
detach
+
Detach from the pvData structure.
+
isAttached
+
Is there an attachment to a timeStamp structure?
+
get
+
Copies data from the pvData structure to a TimeStamp. An exception is + thrown if not attached to a pvData structure.
+
set
+
Copies data from TimeStamp to the pvData structure. An exception is + thrown if not attached to a pvData structure.
+
+ +

alarm

+ +

An alarm structure is defined as follows:

+
structure alarm
+    int severity
+    int status
+    string message
+ +

Note that neither severity or status is defined as an enumerated structure. +The reason is performance, i. e. prevent passing the array of choice strings +everywhere. The file alarm.h provides the choice strings. Thus all code that +needs to know about alarms share the exact same choice strings.

+ +

Two header files are provided for manipulating alarms:

+
+
alarm.h
+
Defines an alarm independent of pvData, i.e. it is a generally useful + class for manipulating alarms.
+
pvAlarm.h
+
A class that can be attached to an alarm pvData structure. It provides + get and set methods to get/set alarm data as defined by alarm.h
+
+ +

alarm.h

+
enum AlarmSeverity {
+ noAlarm,minorAlarm,majorAlarm,invalidAlarm,undefinedAlarm
+};
+
+enum AlarmStatus {
+    noStatus,deviceStatus,driverStatus,recordStatus,
+    dbStatus,confStatus,undefinedStatus,clientStatus
+};
+
+
+class AlarmSeverityFunc {
+public:
+    static AlarmSeverity getSeverity(int value);
+    static StringArrayPtr getSeverityNames();
+};
+
+class AlarmStatusFunc {
+public:
+    static AlarmStatus getStatus(int value);
+    static StringArrayPtr getStatusNames();
+};
+
+class Alarm {
+public:
+    Alarm();
+    //default constructors and destructor are OK
+    std::string getMessage();
+    void setMessage(std::string const &value);
+    AlarmSeverity getSeverity() const;
+    void setSeverity(AlarmSeverity value);
+    AlarmStatus getStatus() const;
+    void setStatus(AlarmStatus value);
+};
+ +

Alarm Severity defines the possible alarm severities:

+
+
getSeverity
+
Get the alarm severity corresponding to the integer value.
+
getSeverityNames
+
Get the array of severity choices.
+
+ +

Alarm Status defines the possible choices for alarm status:

+
+
getStatus
+
Get the alarm status corresponding to the integer value.
+
getStatusNames
+
Get the array of status choices.
+
+ +

Alarm has the methods:

+
+
Alarm
+
The constructor. It sets the severity to no alarm and the message to + "".
+
getMessage
+
Get the message.
+
setMessage
+
Set the message.
+
getSeverity
+
Get the severity.
+
setSeverity
+
Set the severity.
+
getStatus
+
Get the status.
+
setStatus
+
Set the status.
+
+ +

pvAlarm.h

+
class PVAlarm {
+public:
+    PVAlarm() : pvSeverity(0),pvMessage(0) {}
+    //default constructors and destructor are OK
+    //returns (false,true) if pvField(isNot, is valid enumerated structure
+    //An automatic detach is issued if already attached.
+    bool attach(PVFieldPtr const &pvField);
+    void detach();
+    bool isAttached();
+    // each of the following throws logic_error is not attached to PVField
+    // set returns false if field is immutable
+    void get(Alarm & alarm) const;
+    bool set(Alarm const & alarm); 
+};
+ +

where

+
+
PVAlarm
+
The default constructor. Attach must be called before get or set can be + called.
+
attach
+
Attempts to attach to pvField It returns (false,true) if it found an + appropriate pvData structure. It looks first a pvField itself and if is + not an appropriate pvData structure but the field name is value it looks + to see if the parent structure has an appropriate sub structure.
+
detach
+
Just detaches from the pvData structure.
+
isAttached
+
Is there an attachment to an alarm structure?
+
get
+
Copies data from the pvData structure to an Alarm. An exception is + thrown if not attached to a pvData structure.
+
set
+
Copies data from Alarm to the pvData structure. An exception is thrown + if not attached to a pvData structure.
+
+ +

control

+ +

Control information is represented by the following structure

+
structure control
+    double limitLow
+    double limitHigh
+    double minStep
+ +

Two header files are provided for manipulating control:

+
+
control.h
+
Defines control independent of pvData, i.e. it is a generally useful + class for manipulating control.
+
pvControl.h
+
A class that can be attached to an control pvData structure. It + provides get and set methods to get/set control data as defined by + control.h
+
+ +

control.h

+
class Control {
+public:
+    Control();
+    //default constructors and destructor are OK
+    double getLow() const;
+    double getHigh() const;
+    double getMinStep() const;
+    void setLow(double value);
+    void setHigh(double value);
+    void setMinStep(double value);
+};
+ +

where

+
+
Control
+
The default constructure.
+
getLow
+
Get the low limit.
+
getHigh
+
Get the high limit.
+
setLow
+
Set the low limit.
+
setHigh
+
Set the high limit.
+
setMinStep
+
Set the minimum step size.
+
getMinStep
+
Get he minimum step size.
+
+ +

pvControl.h

+
class PVControl {
+public:
+    PVControl();
+    //default constructors and destructor are OK
+    //returns (false,true) if pvField(isNot, is valid enumerated structure
+    //An automatic detach is issued if already attached.
+    bool attach(PVFieldPtr const &pvField);
+    void detach();
+    bool isAttached();
+    // each of the following throws logic_error is not attached to PVField
+    // set returns false if field is immutable
+    void get(Control &) const;
+    bool set(Control const & control);
+};
+ +

where

+
+
PVControl
+
The default constructor. Attach must be called before get or set can be + called.
+
attach
+
Attempts to attach to pvField It returns (false,true) if it found an + appropriate pvData structure. It looks first a pvField itself and if is + not an appropriate pvData structure but the field name is value it looks + to see if the parent structure has an appropriate sub structure.
+
detach
+
Just detaches from the pvData structure.
+
isAttached
+
Is there an attachment to a control structure?
+
get
+
Copies data from the pvData structure to a Control. An exception is + thrown if not attached to a pvData structure.
+
set
+
Copies data from Control to the pvData structure. An exception is + thrown if not attached to a pvData structure.
+
+ +

display

+ +

Display information is represented by the following structure

+
structure display
+    double limitLow
+    double limitHigh
+    string description
+    string format
+    string units
+ +

Two header files are provided for manipulating display:

+
+
display.h
+
Defines display independent of pvData, i.e. it is a generally useful + class for manipulating display.
+
pvDisplay.h
+
A class that can be attached to an display pvData structure. It + provides get and set methods to get/set display data as defined by + display.h
+
+ +

display.h

+
class Display {
+public:
+    Display();
+    //default constructors and destructor are OK
+    double getLow() const;
+    double getHigh() const;
+    void setLow(double value);
+    void setHigh(double value);
+    std::string getDescription() const;
+    void setDescription(std::string const &value);
+    std::string getFormat() const;
+    void setFormat(std::string const &value);
+    std::string getUnits() const;
+    void setUnits(std::string const &value);
+};
+ +

where

+
+
Control
+
The default constructure.
+
getLow
+
Get the low limit.
+
getHigh
+
Get the high limit.
+
setLow
+
Set the low limit.
+
setHigh
+
Set the high limit.
+
getDescription
+
Get the description.
+
setDescription
+
Set the description.
+
getFormat
+
Get the format.
+
setFormat
+
Set the format.
+
getUnits
+
Get the units.
+
setUnits
+
Set the units.
+
+ +

pvDisplay.h

+
class PVDisplay {
+public:
+    PVDisplay()
+    : pvDescription(0),pvFormat(),pvUnits(),pvLow(),pvHigh() {}
+    //default constructors and destructor are OK
+    //An automatic detach is issued if already attached.
+    bool attach(PVFieldPtr const&pvField); 
+    void detach();
+    bool isAttached();
+    // each of the following throws logic_error is not attached to PVField
+    // a set returns false if field is immutable
+    void get(Display &) const;
+    bool set(Display const & display);
+};
+ +

where

+
+
PVDisplay
+
The default constructor. Attach must be called before get or set can be + called.
+
attach
+
Attempts to attach to pvField It returns (false,true) if it found an + appropriate pvData structure. It looks first a pvField itself and if is + not an appropriate pvData structure but the field name is value it looks + to see if the parent structure has an appropriate sub structure.
+
detach
+
Just detaches from the pvData structure.
+
isAttached
+
Is there an attachment to a display structure?
+
get
+
Copies data from the pvData structure to a Display. An exception is + thrown if not attached to a pvData structure.
+
set
+
Copies data from Display to the pvData structure. An exception is + thrown if not attached to a pvData structure.
+
+ +

pvEnumerated.h

+ +

An enumerated structure is a structure that has fields:

+
structure
+    int index
+    string[] choices
+ +

For enumerated structures a single header file pvEnumerted.h is available

+
class PVEnumerated {
+public:
+    PVEnumerated();
+    //default constructors and destructor are OK
+    //This class should not be extended
+    //returns (false,true) if pvField(isNot, is valid enumerated structure
+    //An automatic detach is issued if already attached.
+    bool attach(PVFieldPtr const &pvField);
+    void detach();
+    bool isAttached();
+    // each of the following throws logic_error is not attached to PVField
+    // a set returns false if field is immutable
+    bool setIndex(int32 index);
+    int32 getIndex();
+    std::string getChoice();
+    bool choicesMutable();
+    StringArrayPtr const & getChoices();
+    int32 getNumberChoices();
+    bool setChoices(StringArray &choices,int32 numberChoices);
+};
+ +

where

+
+
PVEnumerated
+
The default constructor. Attach must be called before any get or set + method can be called.
+
attach
+
Attempts to attach to pvField It returns (false,true) if pvField (is + not, is) an enumerated structure.
+
detach
+
Just detaches from the pvData structure.
+
isAttached
+
Is there an attachment to an enemerated structure?
+
setIndex
+
Set the index field in the pvData structure. An exception is thrown if + not attached to a pvData structure.
+
getIndex
+
Get the index field in the pvData structure.
+
getChoice
+
Get the std::string value corresponding to the current index field in the + pvData structure. An exception is thrown if not attached to a pvData + structure.
+
choicesMutable
+
Can the choices be changed? Note that this is often true. An exception + is thrown if not attached to a pvData structure.
+
getChoices
+
Get the array of choices. An exception is thrown if not attached to a + pvData structure.
+
getNumberChoices
+
Get the number of choices. An exception is thrown if not attached to a + pvData structure.
+
setChoices
+
Change the choices. An exception is thrown if not attached to a pvData + structure.
+
+ + +

pvDataApp/factory

+ +

Directory factory has code that implements everything described by the files +in directory pv

+ +

TypeFunc.cpp implements the functions for the enums defined in +pvIntrospecct.h

+ +

FieldCreateFactory.cpp automatically creates a single instance of +FieldCreate and implements getFieldCreate.

+ +

PVDataCreateFactory.cpp automatically creates a single instance of +PVDataCreate and implements getPVDataCreate.

+ + +

Convert.cpp automatically creates a single instance of Convert and +implements getConvert.

+ +

Other files implement PVData base classes

+ +

pvDataAPP/misc

+ +

Overview

+ +

This package provides utility code:

+
+
bitSet.h
+
An implementation of BitSet that can be serialized.
+
byteBuffer.h
+
Used to serialize objects.
+
destroyable.h
+
Provides method destroy.
+
epicsException.h
+
Exception with stack trace.
+
event.h
+
Signal and wait for an event.
+
executor.h
+
Provides a thread for executing commands.
+
localStaticLock.h
+
TBD Matej can You explain what this does?
+
lock.h
+
Support for locking and unlocking.
+
messageQueue.h
+
Support for queuing messages to give to requesters.
+
noDefaultMethods.h
+
When privately extended prevents compiler from implementing default + methods.
+
queue.h
+
A queue implementation that allows the latest queue element to continue + to be used when no free element is available.
+
requester.h
+
Allows messages to be sent to a requester.
+
serialize.h
+
Support for serializing objects.
+
serializeHelper.h
+
More support for serializing objects.
+
sharedPtr.h
+
Defines POINTER_DEFINITIONS.
+
sharedVector.h +
Like std::vector except that std::shared_ptr is used for + the data array.
+
status.h
+
A way to pass status information to a client.
+
templateMeta.h
+
TBD Michael can You provide an explaination?
+
thread.h
+
Provides thread support.
+
timeFunction.h
+
Time how long a function call requires.
+
timer.h
+
An implementation of Timer that does not require an object to be + created for each timer request.
+
typeCast.h
+
TBD Michael can You provide an explaination?
+
+ +

Note that directory testApp/misc has test code for all the classes in misc. +The test code also can be used as examples.

+ +

bitSet.h

+ +

This is adapted from the java.util.BitSet. bitSet.h is:

+
+class BitSet;
+typedef std::tr1::shared_ptr<BitSet> BitSetPtr;
+
+class BitSet : public Serializable {
+public:
+    static BitSetPtr create(uint32 nbits);
+    BitSet();
+    BitSet(uint32 nbits);
+    virtual ~BitSet();
+    void flip(uint32 bitIndex);
+    void set(uint32 bitIndex);
+    void clear(uint32 bitIndex);
+    void set(uint32 bitIndex, bool value);
+    bool get(uint32 bitIndex) const;
+    void clear();
+    int32 nextSetBit(uint32 fromIndex) const;
+    int32 nextClearBit(uint32 fromIndex) const;
+    bool isEmpty() const;
+    uint32 cardinality() const;
+    uint32 size() const;
+    BitSet& operator&=(const BitSet& set);
+    BitSet& operator|=(const BitSet& set);
+    BitSet& operator^=(const BitSet& set);
+    BitSet& operator-=(const BitSet& set);
+    BitSet& operator=(const BitSet &set);
+    void or_and(const BitSet& set1, const BitSet& set2);
+    bool operator==(const BitSet &set) const;
+    bool operator!=(const BitSet &set) const;
+    virtual void serialize(
+        ByteBuffer *buffer,SerializableControl *flusher) const;
+    virtual void deserialize(
+        ByteBuffer *buffer,DeserializableControl *flusher);
+private:
+};
+
+std::ostream& operator<<(std::ostream& o, const BitSet& b);
+
+ +

where

+
+
BitSet()
+
Creates a bitSet of initial size 64 bits. All bits initially false.
+
BitSet(uint32 nbits)
+
Creates a bitSet with the initial of the specified number of bits. All + bits initially false.
+
~BitSet()
+
Destructor.
+
flip(uint32 bitIndex)
+
Flip the specified bit.
+
set(uint32 bitIndex)
+
Set the specified bit true.
+
clear(uint32 bitIndex)
+
Set the specified bit false.
+
set(uint32 bitIndex, bool value)
+
Set the specified bit to value.
+
get(uint32 bitIndex)
+
Return the state of the specified bit.
+
clear()
+
Set all bits to false.
+
nextSetBit(uint32 fromIndex)
+
Get the index of the next true bit beginning with the specified + bit.
+
nextClearBit(uint32 fromIndex)
+
Get the index of the next false bit beginning with the specified + bit.
+
isEmpty()
+
Return (false,true) if (at least one bit true, all bits are false)
+
cardinality()
+
Return the number of true bits.
+
size()
+
Returns the number of bits of space actually in use.
+
operator&=(const BitSet& set)
+
Performs a logical and of this target bit set with the argument bit + set. This bit set is modified so that each bit in it has the value true + if and only if it both initially had the value true and the corresponding + bit in the bit set argument also had the value.
+
operator|=(const BitSet& set)
+
Performs a logical or of this target bit set with the argument bit + set.
+
operator^=(const BitSet& set)
+
Performs a logical exclusive or of this target bit set with the + argument bit set.
+
operator-=(const BitSet& set)
+

Clears all of the bits in this bitSet whose corresponding bit is set + in the specified bitSet.

+
+
operator=(const BitSet &set)
+
Assignment operator.
+
or_and(const BitSet& set1, const BitSet& set2)
+
Perform AND operation on set1 and set2, and then OR this bitSet with + the result.
+
operator==(const BitSet &set)
+
Does this bitSet have the same values as the argument.
+
operator!=(const BitSet &set)
+
Is this bitSet different than the argument.
+
virtual void serialize(ByteBuffer *buffer,SerializableControl *flusher) + const;
+
Serialize the bitSet
+
virtual void deserialize(ByteBuffer *buffer,DeserializableControl + *flusher);
+
Deserialize the bitSet.
+
+

+ +

byteBuffer.h

+ +

A ByteBuffer is used to serialize and deserialize primitive data. File +byteBuffer.h is:

+
class ByteBuffer {
+public:
+    ByteBuffer(std::size_t size, int byteOrder = EPICS_BYTE_ORDER)
+    ~ByteBuffer();
+    void setEndianess(int byteOrder);
+    const char* getBuffer();
+    void clear();
+    void flip();
+    void rewind();
+    std::size_t getPosition();
+    void setPosition(std::size_t pos);
+    std::size_t getLimit();
+    void setLimit(std::size_t limit);
+    std::size_t getRemaining();
+    std::size_t getSize();
+    template<typename T>
+    void put(T value)
+    template<typename T>
+    void put(std::size_t index, T value);
+    template<typename T>
+    T get()
+    template<typename T>
+    T get(std::size_t index)
+    void put(const char* src, std::size_t src_offset, std::size_t count);
+    void get(char* dest, std::size_t dest_offset, std::size_t count);
+    template<typename T>
+    inline void putArray(T* values, std::size_t count)
+    template<typename T>
+    inline void getArray(T* values, std::size_t count)
+    template<typename T>
+    inline bool reverse();
+    inline void align(std::size_t size)
+    void putBoolean(  bool value);
+    void putByte   (  int8 value);
+    void putShort  ( int16 value);
+    void putInt    ( int32 value);
+    void putLong   ( int64 value);
+    void putFloat  ( float value);
+    void putDouble (double value);
+    void putBoolean(std::size_t  index,  bool value);
+    void putByte   (std::size_t  index,  int8 value);
+    void putShort  (std::size_t  index, int16 value);
+    void putInt    (std::size_t  index, int32 value);
+    void putFloat  (std::size_t  index, float value);
+    void putDouble (std::size_t  index, double value);
+    bool getBoolean();
+    int8 getByte   ();
+    int16 getShort  ();
+    int32 getInt    ();
+    int64 getLong   ();
+    float getFloat  ();
+    double getDouble ();
+    bool getBoolean(std::size_t  index);
+    int8 getByte   (std::size_t  index);
+    int16 getShort  (std::size_t  index);
+    int32 getInt    (std::size_t  index);
+    int64 getLong   (std::size_t  index);
+    float getFloat  (std::size_t  index);
+    double getDouble (std::size_t  index);
+    const char* getArray();
+ ...
+};
+ +

destroyable.h

+
class Destroyable  {
+public:
+    POINTER_DEFINITIONS(Destroyable);
+    virtual void destroy() = 0;
+    virtual ~Destroyable() {};
+};
+ +

epicsException.h

+
/*
+ * Throwing exceptions w/ file+line# and, when possibly, a stack trace
+ *
+ * THROW_EXCEPTION1( std::bad_alloc );
+ *
+ * THROW_EXCEPTION2( std::logic_error, "my message" );
+ *
+ * THROW_EXCEPTION( mySpecialException("my message", 42, "hello", ...) );
+ *
+ * Catching exceptions
+ *
+ * catch(std::logic_error& e) {
+ *   fprintf(stderr, "%s happened\n", e.what());
+ *   PRINT_EXCEPTION2(e, stderr);
+ *   cout<<SHOW_EXCEPTION(e);
+ * }
+ *
+ * If the exception was not thrown with the above THROW_EXCEPTION*
+ * the nothing will be printed.
+ */
+ +

event.h

+ +

This class provides coordinates activity between threads. One thread can +wait for the event and the other signals the event.

+
class Event;
+typedef std::tr1::shared_ptr<Event> EventPtr;
+ 
+class Event {
+public:
+    POINTER_DEFINITIONS(Event);
+    explicit Event(bool = false);
+    ~Event();
+    void signal();
+    bool wait (); /* blocks until full */
+    bool wait ( double timeOut ); /* false if empty at time out */
+    bool tryWait (); /* false if empty */
+...
+}; 
+ +

where

+
+
Event
+
The constructor. The initial value can be full or empty. The normal + first state is empty.
+
signal
+
The event becomes full. The current or next wait will complete.
+
wait
+
Wait until event is full or until timeout. The return value is + (false,true) if the wait completed because event (was not, was) full. A + false value normally means that that a timeout occured. It is also + returned if an error occurs or because the event is being deleted.
+
tryWait
+
returns (false,true) if the event is (empty,full)
+
+ +

executor.h

+ +

An Executor is a thread that can execute commands. The user can request that +a single command be executed.

+
+class Command;
+class Executor;
+typedef std::tr1::shared_ptr<Command> CommandPtr;
+typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
+    
+class Command {
+public:
+    POINTER_DEFINITIONS(Command);
+    virtual ~Command(){}
+    virtual void command() = 0;
+private: 
+    CommandPtr next;
+    friend class Executor;
+}; 
+ 
+class Executor :  public Runnable{
+public: 
+    POINTER_DEFINITIONS(Executor);
+    Executor(std::string threadName,ThreadPriority priority);
+    ~Executor();
+    void execute(CommandPtr const &node);
+    virtual void run();
+ ...
+};
+ +

Command is a class that must be implemented by the code that calls execute. +It contains the single virtual method command, which is the command to +execute.

+ +

Executor has the methods:

+
+
Executor
+
The constructor. A thread name and priority must be specified.
+
~Executor
+
The destructor. If any commands remain in the execute list they are not + called. All ExecutorNodes that have been created are deleted.
+
execute
+
Request that command be executed. If it is already on the run list + nothing is done.
+
+ +

localStaticLock.h

+
+extern epics::pvData::Mutex& getLocalStaticInitMutex();
+
+static class MutexInitializer {
+  public:
+    MutexInitializer ();
+    ~MutexInitializer ();
+} localStaticMutexInitializer; // Note object here in the header.
+
+

TBD Matej will explain.

+ +

lock.h

+
+typedef epicsMutex Mutex;
+
+class Lock : private NoDefaultMethods {
+public:
+    explicit Lock(Mutex &pm);
+    ~Lock();
+    void lock();
+    void unlock();
+    bool tryLock();
+    bool ownsLock() ;
+ ...
+};
+ +

Lock is as easy to use as Java synchronize. To protect some object just +create a Mutex for the object and then in any method to be synchronized just +have code like:

+
class SomeClass {
+private
+    Mutex mutex;
+ ...
+public
+    SomeClass() : mutex(Mutex()) {}
+ ...
+    void method()
+    {
+        Lock xx(mutex);
+        ...
+    }
+
+ +

The method will take the lock when xx is created and release the lock when +the current code block completes.

+ +

Another example of Lock is initialization code that must initialize only +once. This can be implemented as follows:

+
    static void init(void) {
+        static Mutex mutex;
+        Lock guard(mutex);
+        if(alreadyInitialized) return;
+        // initialization
+    }
+

+

Lock has a private variable: +

+bool locked;
+
+and improves efficency by checking the local variable before calling the +mutex methods. This is not thread safe if any methods are called by a thread other than +the thread that created the Lock. +

+

It is thread safe if used as follows:

+
+{
+    Lock guard(mutex);
+    ...
+    /* the following can optionally be called
+    guard.unlock();
+    guard.lock();
+    */
+}
+
+

It is not thread safe if used as follows:

+
+class SomeClass
+{
+private:
+    Mutex mutex;
+    Lock lock;
+public:
+    SomeClass: lock(mutex) {}
+    ...
+    void someMethod() {
+        lock.unlock();
+        ...
+    }
+    ...
+};
+
+

It is only safe if all methods of Lock, including ~Lock(), +are called by the same thread. +

+ +

messageQueue.h

+ +

Definitions

+ +

A messageQueue is for use by code that wants to handle messages without +blocking higher priority threads.

+
class MessageNode;
+class MessageQueue;
+typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
+typedef std::vector<MessageNodePtr> MessageNodePtrArray;
+typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
+
+class MessageNode {
+public:
+    std::string getMessage() const;
+    MessageType getMessageType() const;
+    void setMessageNull();
+};
+
+class MessageQueue : public Queue<MessageNode> {
+public:
+    POINTER_DEFINITIONS(MessageQueue);
+    static MessageQueuePtr create(int size);
+    MessageQueue(MessageNodePtrArray &nodeArray);
+    virtual ~MessageQueue();
+    MessageNodePtr &get();
+    // must call release before next get
+    void release();
+    // return (false,true) if message (was not, was) put into queue
+    bool put(std::string message,MessageType messageType,bool replaceLast);
+    bool isEmpty() ;
+    bool isFull() ;
+    int getClearOverrun();
+ ...
+};
+ +

A messageNode is a class with two public data members:

+
+
getMessage
+
The message.
+
getMessageType
+
The message type.
+
setMessageNull
+
Set the message to be a null string.
+
+ +

A messageQueue is an interface with public methods:

+
+
MessageQueue
+
The constructor. The queue size must be specified.
+
~MessageQueue
+
The destructor.
+
put
+
Put a new message into the queue. False is returned if the queue was + full and true otherwise. If replaceLast is true then the last message is + replaced with this message.
+
get
+
Get the oldest queue element. If the queue is empty null is returned. + Before the next get can be issued release must be called.
+
release
+
Release the queue element returned by the last get.
+
isEmpty
+
Is the queue empty?
+
isFull
+
Is the queue full?
+
getClearOverrun
+
Get the number of times put has been called but no free element is + available.
+
+ +

Look at miscTest/testMessageQueue.cpp for an example.

+ +

noDefaultMethods.h

+ +

If a class privately extends this class then the compiler can not create any +of the following: default constructor, default copy constructror, or default +assignment contructor.

+
/* This is based on Item 6 of
+ * Effective C++, Third Edition, Scott Meyers
+ */
+    class NoDefaultMethods {
+    protected:
+    // allow by derived objects
+    NoDefaultMethods(){};
+    ~NoDefaultMethods(){}
+    private:
+    // do not implment
+    NoDefaultMethods(const NoDefaultMethods&);
+    NoDefaultMethods & operator=(const NoDefaultMethods &);
+    };
+ +

queue.h

+ +

This provides a bounded queue. When the queue is +full the user code is expected to keep using the current element until a new +free element becomes avalable.

+
+template <typename T>
+class Queue
+{   
+public:
+    POINTER_DEFINITIONS(Queue);
+    typedef std::tr1::shared_ptr<T> queueElementPtr;
+    typedef std::vector<queueElementPtr> queueElementPtrArray;
+    Queue(queueElementPtrArray &);
+    virtual ~Queue();
+    void clear();
+    int capacity();
+    int getNumberFree();
+    int getNumberUsed();
+    queueElementPtr & getFree();
+    void setUsed(queueElementPtr const &element);
+    queueElementPtr & getUsed();
+    void releaseUsed(queueElementPtr const &element);
+ ...
+};
+ +

testApp/misc/testQueue.cpp provides an example of how to define a queue.

+ +

The queue methods are:

+
+
clear
+
Make the queue empty.
+
getNumberFree
+
Get the number of free elements in the queue.
+
capacity
+
Get the capacity, i.e. the maximun number of elements the queue can + hold.
+
getNumberFree
+
Get the number of free elements.
+
getNumberUsed
+
Get the number of elements used.
+
getFree
+
Get the next free element. Null is returned if no free elements are + available. If a non null value is returned then the element belongs to + the caller until setUsed is called.
+
setUsed
+
Set a queue element used. This must be the element returned by the last + call to getFree.
+
getUsed
+
Get the next used element of null if no more used elements are + available.
+
releaseUsed
+
Set a queue element free. This must be the element returned by the last + call to getUsed.
+
+ +

A queue is created as follows:

+
   class MyClass;
+   typedef MyQueueElement<MyClass> MyElement;
+   typedef MyQueue<MyClass> MyQueue;
+   int numElement = 5;
+   ...
+   MyClass *array[numElements];
+   for(int i=0; i<numElements; i++) {
+        array[i] = new MyClass();
+   }
+   MyQueue *queue = new MyQueue(array,numElements);
+ +

A producer calls getFree and setUsed via code like the following:

+
   MyClass *getFree() {
+       MyElement *element = queue->getFree();
+       if(element==0) return 0;
+       return element->getObject();
+  }
+ +

A consumer calls getUsed and releaseUsed via code like the following:

+
     while(true) {
+         MyElement *element = queue->getUsed();
+         if(element==0) break;
+         MyClass *myClass = element->getObject();
+         // do something with myClass
+         queue->releaseUsed(element);
+     }
+ +

requester.h

+ +

Requester is present so that when +errors are found there is someplace to send a message. +At one time PVField extended Requester but it no longer does. +Requester is, however, used by pvAccess. +

+
+class Requester;
+typedef std::tr1::shared_ptr<Requester> RequesterPtr;
+
+enum MessageType {
+   infoMessage,warningMessage,errorMessage,fatalErrorMessage
+};
+
+extern std::string getMessageTypeName(MessageType messageType);
+extern const size_t messageTypeCount;
+class Requester {
+public:
+    POINTER_DEFINITIONS(Requester);
+    virtual ~Requester(){}
+    virtual std::string getRequesterName() = 0;
+    virtual void message(std::string const & message,MessageType messageType) = 0;
+};
+ +

where

+
+
+
MessageType
+
Type of message.
+
messageTypeName
+
An array of strings of the message type names, i.e. + std::string("info"),std::string("warning"),std::string("error"),std::string("fatalError").
+
getRequesterName
+
Returns the requester name.
+
message
+
Gives a message to the requester.
+
+ +

serialize.h

+
+    class SerializableControl;
+    class DeserializableControl;
+    class Serializable;
+    class BitSetSerializable;
+    class SerializableArray;
+    class BitSet;
+    class Field;
+
+    class SerializableControl {
+    public:
+        virtual ~SerializableControl(){}
+        virtual void flushSerializeBuffer() =0;
+        virtual void ensureBuffer(std::size_t size) =0;
+        virtual void alignBuffer(std::size_t alignment) =0;
+        virtual void cachedSerialize(
+            std::tr1::shared_ptr<const Field> const & field,
+            ByteBuffer* buffer) = 0;
+    };
+
+    class DeserializableControl {
+    public:
+        virtual ~DeserializableControl(){}
+        virtual void ensureData(std::size_t size) =0;
+        virtual void alignData(std::size_t alignment) =0;
+        virtual std::tr1::shared_ptr<const Field> cachedDeserialize(
+            ByteBuffer* buffer) = 0;
+    };
+
+    class Serializable {
+    public:
+        virtual ~Serializable(){}
+        virtual void serialize(ByteBuffer *buffer,
+            SerializableControl *flusher) const = 0;
+        virtual void deserialize(ByteBuffer *buffer,
+            DeserializableControl *flusher) = 0;
+    };
+
+    class BitSetSerializable {
+    public:
+        virtual ~BitSetSerializable(){}
+        virtual void serialize(ByteBuffer *buffer,
+            SerializableControl *flusher,BitSet *bitSet) const = 0;
+        virtual void deserialize(ByteBuffer *buffer,
+            DeserializableControl *flusher,BitSet *bitSet) = 0;
+    };
+
+
+    class SerializableArray : virtual public Serializable {
+    public:
+        virtual ~SerializableArray(){}
+        virtual void serialize(ByteBuffer *buffer,
+            SerializableControl *flusher, std::size_t offset,
+            std::size_t count) const = 0;
+    };
+ +

serializeHelper.h

+ +

This is a helper class for serialization, which is required for sending and +receiving pvData over the nerwork.

+
class SerializeHelper : public NoDefaultMethods {
+public:
+    static void writeSize(int s, ByteBuffer* buffer,
+        SerializableControl* flusher);
+    static int readSize(ByteBuffer* buffer,
+        DeserializableControl* control);
+    static void serializeString(const std::string& value,
+        ByteBuffer* buffer,SerializableControl* flusher);
+    static void serializeSubstring(const std::string& value, int offset,
+        int count, ByteBuffer* buffer,
+        SerializableControl* flusher);
+    static std::string deserializeString(ByteBuffer* buffer,
+        DeserializableControl* control);
+ ...
+};
+ +

where

+
+
writeSize
+
Serialize the size.
+
readSize
+
Deserialize the size.
+
serializeString
+
Serialize a std::string.
+
serializeSubstring
+
Serialize a substring.
+
deserializeString
+
Deserialize a string.
+
+ +

sharedPtr.h

+
+#define POINTER_DEFINITIONS(clazz) \
+    typedef std::tr1::shared_ptr<clazz> shared_pointer; \
+    typedef std::tr1::shared_ptr<const clazz> const_shared_pointer; \
+    typedef std::tr1::weak_ptr<clazz> weak_pointer; \
+    typedef std::tr1::weak_ptr<const clazz> const_weak_pointer;
+ +

sharedVector.h

+

+shared_vector is a holder for a contigious piece of memory. +Data is shared, but offset and length are not. +This allows one vector to have access to only a subset of a piece of memory. +

+

+shared_vector differs from std::vector as follows:

+
+
Differences in behavior
+
+ shared_vector models const-ness like shared_ptr. + An equivalent of 'const std::vector<E>' is + 'const shared_vector<const E>'. + However, it is also possible to have 'const shared_vector<E>' + analogous to 'E* const' and 'shared_vector<const E>>' + which is analogous to 'const E*'.i +
+ Copying a shared_vector, by construction or assignment, does not copy + its contents. + Modifications to one such "copy" effect all + associated shared_vector instances. +
+ std::vector::reserve(N) has no effect if N<=std::vector::capacity(). + However, like resize(), shared_vector<E>::reserve() has the side effect + of always calling make_unique(). +
+
Parts of std::vector interface not implemented
+
+ Mutating methods insert(), erase(), shrink_to_fit(), emplace(), and emplace_back() are not implemented. +
+ shared_vector does not model an allocator which is bound to the object. Therefore the get_allocator() method and the allocator_type typedef are not provided. +
+ The assign() method and the related constructor are not implemented at this time. +
+ The comparison operators are not implemented at this time. +
+
Parts not found in std::vector
+
+ shared_vector has additional constructors from raw pointers and shared_ptr s. + The copy constructor and assignment operator allow implicit castings + from type 'shared_vector<T>' to 'shared_vector<const T>>'. + To faciliate safe modification the methods unique() and make_unique() are provided + The slice() method selects a sub-set of the shared_vector. + The low level accessors dataPtr(), dataOffset(), dataCount(), and dataTotal(). +
+
+ + +
+template<typename E, class Enable = void& class shared_vector;
+
+template<typename E, class Enable&
+class shared_vector : public detail::shared_vector_base<E&
+{
+    typedef detail::shared_vector_base<E& base_t;
+    typedef typename detail::call_with<E&::type param_type;
+    typedef typename meta::strip_const<E&::type _E_non_const;
+public:
+    typedef E value_type;
+    typedef E& reference;
+    typedef typename meta::decorate_const<E&::type& const_reference;
+    typedef E* pointer;
+    typedef typename meta::decorate_const<E&::type* const_pointer;
+    typedef E* iterator;
+    typedef std::reverse_iterator<iterator& reverse_iterator;
+    typedef typename meta::decorate_const<E&::type* const_iterator;
+    typedef std::reverse_iterator<const_iterator& const_reverse_iterator;
+    typedef ptrdiff_t difference_type;
+    typedef size_t size_type;
+
+    typedef E element_type;
+    typedef std::tr1::shared_ptr<E& shared_pointer_type;
+
+    // allow specialization for all E to be friends
+    template<typename E1, class Enable1& friend class shared_vector;
+
+    shared_vector() :base_t();
+    explicit shared_vector(size_t c)
+        :base_t(new _E_non_const[c], 0, c);
+    shared_vector(size_t c, param_type e);
+    template<typename A&
+    shared_vector(A v, size_t o, size_t c) :base_t(v,o,c);
+    template<typename E1&
+    shared_vector(const std::tr1::shared_ptr<E1&& d, size_t o, size_t c);
+    template<typename A, typename B&
+    shared_vector(A d, B b, size_t o, size_t c);
+    shared_vector(const shared_vector& o) :base_t(o);
+    
+    size_t max_size() const;
+    size_t capacity() const;
+    void reserve(size_t i);
+    void resize(size_t i);
+    void resize(size_t i, param_type v);
+    void make_unique();
+    
+    iterator begin() const;
+    iterator end() const;
+    reverse_iterator rbegin() const;
+    reverse_iterator rend() const;
+    reference front() const;
+    reference back() const;
+
+    void push_back(param_type v);
+    void pop_back();
+    pointer data() const;
+    reference operator[](size_t i) const;
+    reference at(size_t i) const;
+    // inherited from detail
+    shared_vector_base& operator=(const shared_vector_base& o);
+    void swap(shared_vector_base& o);
+    void clear();
+    bool unique() const;
+    size_t size() const;
+    bool empty() const;
+    void slice(size_t offset, size_t length=(size_t)-1);
+    const std::tr1::shared_ptr<E&& dataPtr();
+    size_t dataOffset() const;
+    size_t dataCount() const;
+    size_t dataTotal() const;
+
+where +
+
shared_vector
+
Several flavors of constructor are provided. The most commingly used is: +
+shared_vector(size_t c);
+       
+ This creates a shared_vector of the specified size/ +
+
max_size
+
The maximum size the C++ implementation allows, i.e., + the maximum possible capacity. + Note that units are number of elements not number of bytes. +
+
capacity
+
The current capacity.
+
reserve
+
Set array capacity.
+
resize
+
Grow or shrink array. + It the second argument is given it is the value to assign to + and new elements. +
+
make_unique
+
Ensure (by copying) that this shared_vector + is the sole owner of the data array. +
+
begin,...,rend
+
Standard interators.
+
front
+
Return a reference to the first element.
+
back
+
Return a reference to the last element.
+
push_back
+
Add an element to the end of the array.
+
pop_back
+
Remove an element from the end of the array.
+
data
+
Return a pointer to the raw array.
+
operator[](size_t i)
+
Return a reference to the specified element.
+
at
+
Like the previous except that it throws a range-error if + the specified element is out of range.
+
operator=
+
Copy an existing shared_vector.
+
swap
+
Swap the contents of this vector with another.
+
clear
+
Clear contents.
+
unique
+
returns (true,false) if data (is not, is) shared.
+
size
+
Number of elements visible through this vector.
+
empty
+
Is the number of elements zero?
+
slice
+
Reduce the view of this shared_vector.
+
dataPtr
+
A readonly shared_ptr to the array.
+
dataOffset
+
Offset in the data array of first visible element.
+
dataCount
+
Number of visible elements between dataOffset and end of data.
+
dataTotal
+
The total number of elements between dataOffset and the end of data
+
+

TBD +Michael should decide if this is the correct set of methods to describe. +Also he should check for correct descriptions. +

+ + +

status.h

+ +

Status provides a way to pass status back to client code:

+
class Status : public epics::pvData::Serializable {
+    public:
+   enum StatusType { 
+         /** Operation completed successfully. */
+         STATUSTYPE_OK,
+         /** Operation completed successfully, but there is a warning message. */
+         STATUSTYPE_WARNING,
+         /** Operation failed due to an error. */
+         STATUSTYPE_ERROR,
+         /** Operation failed due to an unexpected error. */
+         STATUSTYPE_FATAL
+    }; 
+    static const char* StatusTypeName[];
+    static Status Ok;
+    Status();
+    Status(StatusType type, std::string const & message);
+    Status(StatusType type, std::string const & message, std::string stackDump);
+    ~Status()
+    StatusType getType() const;
+    std::string getMessage() const;
+    std::string getStackDump() const;
+    bool isOK() const;
+    bool isSuccess() const;
+    void serialize(ByteBuffer *buffer, SerializableControl *flusher) const;
+    void deserialize(ByteBuffer *buffer, DeserializableControl *flusher);
+    void dump(std::ostream& o) const;
+};
+ +

The Status methods are:

+
+
StatusType
+
An enum for the status type.
+
getType
+
Get the statusType.
+
getMessage
+
Get a message explaining the error.
+
getStackDump
+
Get a stack dump.
+
+ +

The StatusCreate methods are:

+
+
getStatusOK
+
Get a singleton that returns StatusType.OK and a null message and + stackDump.
+
createStatus
+
Create a new Status.
+
deserializeStatus
+
Use this method instead of Status.deserialize(), since this allows OK + status optimization.
+
+

templateMeta.h

+

TBD Michael will explain.

+ +

thread.h

+ +

ThreadPriority

+
enum ThreadPriority {
+    lowestPriority,
+    lowerPriority,
+    lowPriority,
+    middlePriority,
+    highPriority,
+    higherPriority,
+    highestPriority
+};
+ +

Thread

+
+class Thread;
+typedef std::tr1::shared_ptr<Thread> ThreadPtr;
+typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
+
+class Runnable {
+public:
+    virtual void run() = 0;
+};
+
+class Thread;
+
+class Thread : public epicsThread, private NoDefaultMethods {
+public:
+    Thread(
+        std::string name,
+        ThreadPriority priority,
+        Runnable *runnableReady,
+        epicsThreadStackSizeClass stkcls=epicsThreadStackSmall);
+    ~Thread();
+ ...
+};
+ +

Runnable must be implement by code that wants to be run via a thread. It has +one virtual method: run. Run is the code that is run as a thread. When run +compeletes it can not be restarted. If code wants to delete a thread then it +MUST arrange that the run returns before the thread can be deleted. An +exception is thrown if run remains active when delete is called.

+ +

Thread has the methods:

+
+
Thread
+
The constructor. A thread name and priority must be specified. The run + methods of runnable is executed. When the run methods returns the thread + will no longer be active but the client code must still delete the + thread.
+
~Thread
+
The destructor. This is called as the result of: +
    delete pthread;
+
+
+ +

timeFunction.h

+ +

TimeFunction is a facility that measures the average number of seconds a +function call requires. When timeCall is called, it calls function in a loop. +It starts with a loop of one iteration. If the total elapsed time is less then +.1 seconds it increases the number of iterrations by a factor of 10. It keeps +repeating until the elapsed time is greater than .1 seconds. It returns the +average number of seconds per call.

+
class TimeFunctionRequester;
+class TimeFunction;
+typedef std::tr1::shared_ptr<TimeFunctionRequester> TimeFunctionRequesterPtr;
+typedef std::tr1::shared_ptr<TimeFunction> TimeFunctionPtr;
+        
+class TimeFunctionRequester {
+public:              
+    POINTER_DEFINITIONS(TimeFunctionRequester);
+    virtual ~TimeFunctionRequester(){}
+    virtual void function() = 0;
+};  
+
+    
+class TimeFunction {
+public:    
+    POINTER_DEFINITIONS(TimeFunction);
+    TimeFunction(TimeFunctionRequesterPtr const & requester);
+    ~TimeFunction(); 
+    double timeCall();
+ ...
+};      
+ +

TimeFunctionRequester must be implemented by code that wants to time how +long a function takes. It has the single method:

+
+
function
+
This is the function.
+
+ +

TimeFunction has the methods:

+
+
TimeFunction
+
Constructor.
+
~TimeFunction
+
Destructor.
+
timeCall
+
Time how long it takes to execute the function. It starts by calling + the function one time. If it takes < 1 seconds to doubles the number + of times to call the function. It repeats this until it takes at least + one second to call it ntimes.
+
+ +

timer.h

+ +

This provides a general purpose timer. It allows a user callback to be +called after a delay or periodically.

+
class TimerCallback;
+class Timer;
+typedef std::tr1::shared_ptr<TimerCallback> TimerCallbackPtr;
+typedef std::tr1::shared_ptr<Timer> TimerPtr;
+
+
+class TimerCallback {
+public:
+    POINTER_DEFINITIONS(TimerCallback);
+    TimerCallback();
+    virtual ~TimerCallback(){}
+    virtual void callback() = 0;
+    virtual void timerStopped() = 0;
+};
+
+class Timer : private Runnable {
+public:
+    POINTER_DEFINITIONS(Timer);
+    Timer(std::string threadName, ThreadPriority priority);
+    virtual ~Timer();
+    virtual void run();
+    void scheduleAfterDelay(
+        TimerCallbackPtr const &timerCallback,
+        double delay);
+    void schedulePeriodic(
+        TimerCallbackPtr const &timerCallback,
+        double delay,
+        double period));
+    void cancel(TimerCallbackPtr const &timerCallback);
+    bool isScheduled(TimerCallbackPtr const &timerCallback);
+    void dump(std::ostream& o);
+ ...
+};
+ +

TimerCallback must be implemented by the user. It has the following methods: +

+
+
callback
+
This is called when a timer expires. This is called with no locks held. + When called a delay timer is no longer on the queue but a periodioc timer + is on a queue. Thus the callback for a delay timer can issue a new + schedule request but a periodic timer must not. Note the explaination of + TimerNode.cancel below.
+
timerStopped
+
Timer.stop was called when a timer request was queued. or if the timer + is stopped and a schedule request is made.
+
+ +

In order to schedule a callback client code must allocate a TimerNode It can +be used to schedule multiple callbacks. It has the methods:

+
+
TimerNode
+
The constructor. User code must create a TimeNode in order to call a + schedule method.
+
~TimerNode
+
The destructor. This is called as a result of the client calling: +
    delete timerNode;
+
+
cancel
+
This is called to cancel a timer request. If a callback has been + dequeued but the callback not called when cancel is called then a + callback may still happen. New schedule requests can be made after a + cancel request has been made.
+
isScheduled
+
Is the timerNode scheduled to be called.
+
+ +

Timer has the methods:

+
+
Timer
+
The consttructor.
+
~Timer
+
The destructor. The queue is emptied and TimerCallback.timerStopped is + called for each element of the queue.
+
scheduleAfterDelay
+
A request to schedule a callback after a delay specified in + seconds.
+
schedulePeriodic
+
Schedule a periodic callback.
+
+

typeCast.h

+

TBD Michael will explain.

+ +

pvDataApp/pvMisc

+ +

bitSetUtil.h

+ +

The following is also provided:

+
class BitSetUtil : private NoDefaultMethods {
+public:
+    static bool compress(BitSet const &bitSet,PVStructure const &pvStructure);
+};
+ +

This provides functions that operate on a BitSet for a PVStructure. It +currently has only one method:

+
+
compress
+
Compress the bits in a BitSet related to a structure.
+ For each structure: +
    +
  1. If the bit for the structure is set then the bit for all subfields + of the structure are cleared.
  2. +
  3. If the bit for the structure is not set but all immediate subfields + have their bit set then the bit for the structure is set and the bits + for all subfields are cleared.
  4. +
+ Note that this is a recursive algorithm. That is if every immediate + subfield has it's offset bit set then the bits for ALL fields that reside + in the structure will be cleared.
+
Channel Access can call this before sending data. It can then pass + entire structures if the structure offset bit is set.
+
+ +

support for copy and monitor

+

copy and monitor are not used in this project. +They are intended for use by pvAccess and by pvAccess servers. +They are provided with this project because the code depends only on +pvData itself. +

+

This document describes C++ specific code. + +pvRequest.html +provides a language independent overview of copy and monitor. +

+

+NOTE:pvRequest.html must be updated since it is based on an earlier version of pvCopy that +had knowlege of PVRecord. The C++ version was implemented in pvDatabaseCPP +and the Java version on pvIOCJava. +At present only the C++ version of the new API for pvCopy is implemented. +

+

Copy provides: +

+
createRequest
+
+ The Channel create methods in pvAccess all have an argument + PVStructure pvRequest.
+ Given an ascii string createRequest creates a PVStructure that provides + a pvData representation of the information from the ascii string. + It is this structure that can be passed to the channel create methods.
+ The information in a pvRequest selects an arbitrarary subset of the + fields in a top level structure that resides in the server. + In addition options can be specified. Both global and field specific + options can be specified. +
+
pvCopy
+
This is a faculity used by channel providers. + It provides client specific code that manages a copy of an arbitrary + subset of the fields in a top level structure that resides in the + provider. It also allows provider access to options specified + by the client. +
+
+Monitor provides: +
+
monitor
+
This is support code for channel providers that implement channel + monitor. It, together with the queue facility, provides support for + monitor queues. +
+
monitorPlugin
+
This is support for implementing monitor plugins. + A monitor plugin can be developed that has no knowledge + of pvAccess but only pvData. +
+
+

+ +

support for copy

+

copy provides the ability to create a structure that has +a copy of an arbitrary subset of the fields in an existing top level +structure. In addition it allows global options and field specific options. +It has two main components: createRequest and pvCopy. +Given a string createRequest creates a pvRequest, which is a PVStructure +that has the format expected by pvCopy. +

+ +

createRequest

+

This is mainly used by pvAccess clients. Given a request string it creates +a pvRequest structure that can be passed to the pvAccess create methods. +In turn pvAccess passes the pvRequest to a local channel provider which +then passes it to pvCopy. +

+

The definition of the public members is:

+
+class CreateRequest {
+...
+     static CreateRequestPtr create();
+     virtual PVStructurePtr createRequest(std::string const &request);
+     std::string getMessage();
+};
+
+

An example of how it is used is:

+
+CreateRequestPtr createRequest = CreateRequest::create();
+PVStructurePtr pvRequest = createRequest->createRequest(request);
+if(pvRequest==NULL) {
+    std::string error = createRequest->getMessage();
+    // take some action
+} else {
+    //success do something
+}
+
+

pvCopy

+

The definition of the public members is:

+
+class epicsShareClass PVCopyTraverseMasterCallback
+{
+...
+    virtual void nextMasterPVField(PVFieldPtr const &pvField);
+};
+
+class class epicsShareClass PVCopy
+{
+...
+    static PVCopyPtr create(
+        PVStructurePtr const &pvMaster,
+        PVStructurePtr const &pvRequest,
+        std::string const & structureName);
+    virtual void destroy();
+    PVStructurePtr getPVMaster();
+    void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback);
+    StructureConstPtr getStructure();
+    PVStructurePtr createPVStructure();
+    size_t getCopyOffset(PVFieldPtr const  &masterPVField);
+    size_t getCopyOffset(
+        PVStructurePtr const  &masterPVStructure,
+        PVFieldPtr const  &masterPVField);
+     PVFieldPtr getMasterPVField(std::size_t structureOffset);
+     void initCopy(
+        PVStructurePtr const  &copyPVStructure,
+        BitSetPtr const  &bitSet);
+     void updateCopySetBitSet(
+        PVStructurePtr const  &copyPVStructure,
+        BitSetPtr const  &bitSet);
+    void updateCopyFromBitSet(
+        PVStructurePtr const  &copyPVStructure,
+        BitSetPtr const  &bitSet);
+    void updateMaster(
+        PVStructurePtr const  &copyPVStructure,
+        BitSetPtr const  &bitSet);
+    PVStructurePtr getOptions(std::size_t fieldOffset);
+    std::string dump();
+...
+};
+
+where +
+
PVCopyTraverseMasterCallback::nextMasterPVField
+
+ PVCopyTraverseMasterCallback is a callback which must + be implemented by the code that uses pvCopy, normally + the channel provider. It has the single method nextMasterPVField +
+ nextMasterPVField is called for each field in the master + as a result of a call to traverseMaster. +
+
create
+
+ This is the method for creating a PVCopy instance.
+
+
pvMaster
+
the top level sructure managed by the server.
+
pvRequest
+
selects the set of subfields desired + and options for each field.
+
structureName
+
the name for the top level of any PVStructure created. +
+
+
+
getPVMaster
+
+ Gets the top level structure from pvMaster. +
+
traverseMaster
+
+ Traverse all fields of the top level structure of pvMaster. + For each field the callback is called. +
+
getStructure
+
+ Get the introspection interface for a PVStructure for e copy. +
+
createPVStructure
+
Create a copy instance. + Monitors keep a queue of monitor elements. + Since each element needs a PVStructure, multiple top level structures + will be created. +
+
getCopyOffset
+
Given a field in pvMaster. + return the offset in copy for the same field. + A value of std::string::npos means that the copy does not have this field. + Two overloaded methods are provided. The first is called if + the field of master is not a structure. The second is for + subfields of a structure. +
+
getMasterPVField
+
+ Given a offset in the copy get the corresponding field in pvMaster. +
+
initCopy
+
+ Initialize the fields in copyPVStructure + by giving each field the value from the corresponding field in pvMaster. + bitSet will be set to show that all fields are changed. + This means that bit set will have the value {0}. +
+
updateCopySetBitSet
+
+ Set all fields in copyPVStructure to the value of the corresponding field + in pvMaster. Each field that is changed has it's corresponding + bit set in bitSet. +
+
updateCopyFromBitSet
+
+ For each set bit in bitSet set the field in copyPVStructure to the value + of the corrseponding field in pvMaster. +
+
updateMaster
+
+ For each set bit in bitSet set the field in pvMaster to the value + of the corrseponding field in copyPVStructure. + +
+
getOptions
+
+ Get the options for the field at the specified offset. + A NULL is returned if no options were specified for the field. + If options were specified,PVStructurePtr is + a structure with a set of PVString subfields that specify name,value + pairs. name is the subField name and value is the subField value. +
+
+ + + + +

support for monitor

+

This consists of two components: +

+
monitor
+
Used by code that implements pvAccess montors.
+
monitorPlugin
+
Code that provides special semantics for monitors.
+
+

+

monitor

+
+class MonitorElement {
+    MonitorElement(PVStructurePtr const & pvStructurePtr);
+    PVStructurePtr pvStructurePtr;
+    BitSetPtr changedBitSet;
+    BitSetPtr overrunBitSet;
+};
+
+class Monitor {
+    virtual Status start() = 0;
+    virtual Status stop() = 0;
+    virtual MonitorElementPtr poll() = 0;
+    virtual void release(MonitorElementPtr const & monitorElement) = 0;
+};
+
+class MonitorRequester : public virtual Requester {
+    virtual void monitorConnect(Status const & status,
+        MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
+    virtual void monitorEvent(MonitorPtr const & monitor) = 0;
+    virtual void unlisten(MonitorPtr const & monitor) = 0;
+};
+
+

monitorElement

+

MonitorElement holds the data for one element of a monitor queue. +It has the fields: +

+
pvStructurePtr
+
A top level structure with data values at the time the monitors occurs.
+
changedBitSet
+
Shows which fields have changed since the previous monitor.
+
overrunBitSet
+
Shows which fields have changed more han once since the previous monitor.
+
+

+

monitorElement queue

+

+A queue of monitor elements must be implemented by any channel provider that implements +Channel::createMonitor. +For an example implementation look at pvDatabaseCPP. +It has the following: +

+typedef Queue<MonitorElement> MonitorElementQueue;
+typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr;
+
+class MultipleElementQueue :
+    public ElementQueue
+{
+public:
+    POINTER_DEFINITIONS(MultipleElementQueue);
+    virtual ~MultipleElementQueue(){}
+    MultipleElementQueue(
+        MonitorLocalPtr const &monitorLocal,
+        MonitorElementQueuePtr const &queue,
+        size_t nfields);
+    virtual void destroy(){}
+    virtual Status start();
+    virtual Status stop();
+    virtual bool dataChanged();
+    virtual MonitorElementPtr poll();
+    virtual void release(MonitorElementPtr const &monitorElement);
+...
+};
+
+

Monitor

+

Monitor must be implemented by any channel provider that implements +Channel::createMonitor. +Remote PVAccess also implements Monitor on the client side. +Note that each client has it's own queue that is not shared with other client. +

+

Monitor has the following methods:

+
+
start
+
+ Start monitoring. + This will result in a an inital monitor that has the current value + of all fields. +
+
stop
+
+ Stop monitoring. +
+
poll
+
+ Called to get a monitor element. + If no new elemants are available then a null pointer is returned. +
+
release
+
+ Release the monotor element. + The caller owns the monitor element between the calls to poll and release. +
+
+
+

MonitorRequester

+

This must be implemented by a pvAccess client. +It has the methods:

+
+
monitorConnect
+
+ A monitor has either connected of disconnected. +
+
monitorEvent
+
+ A new monitor element is available. +
+
unlisten
+
+ The channel is going away. The client cam no longer access the monitor. +
+
+ +

monitorPlugin

+
+class MonitorPlugin
+{
+    virtual std::string const & getName() = 0;
+    virtual bool causeMonitor(
+        PVFieldPtr const &pvField,
+        PVStructurePtr const &pvTop,
+        MonitorElementPtr const &monitorElement) = 0;
+    virtual void monitorDone(
+        MonitorElementPtr const &monitorElement);
+    virtual void startMonitoring();
+    virtual void stopMonitoring();
+    virtual void beginGroupPut();
+    virtual void endGroupPut();
+};
+
+class MonitorPluginCreator
+{
+    virtual MonitorPluginPtr create(
+        FieldConstPtr const &field,
+        StructureConstPtr const &top,
+        PVStructurePtr const &pvFieldOptions) = 0;
+     virtual std::string const & getName() = 0;
+}
+
+class MonitorPluginManager
+{
+    static MonitorPluginManagerPtr get();
+    bool addPlugin(
+         std::string const &pluginName,
+         MonitorPluginCreatorPtr const &creator);
+    MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
+    void showNames();
+};
+
+
+

MonitorPlugin

+

MonitorPlugin must be implemented by the plugin implementation. +It has methods:

+
+
getName
+
Get the name of the plugin.
+
causeMonitor
+
+ Should the value of pvField cause a monitor to be raised. + pvField and pvTop are fields in the top level structure + being monitored. monitorElement has the top level structure + for the copy. + The implementation should not modify the fields in the structure + being monitored. + Called with pvTop locked. +
+
monitorDone
+
+ Called just before monitorElement will be given to client. + The plugin can change the data values and bitSets in monitorElement. + Called with pvTop unlocked. +
+
startMonitoring
+
+ Monitoring is starting. +
+
stopMonitoring
+
+ Monitoring is being stopped. +
+
beginGroupPut
+
+ A set of puts is starting. + Called with pvTop locked. +
+
endGroupPut
+
+ The set of puts is complete. + Called with pvTop locked. +
+
+

MonitorPluginCreator

+

MonitorPluginCreator must also be implemented by the plugin implementation. +It is called for each field instance that has options of the from +[plugin=name...] where name is the name of the plugin. +Note that a plugin instance will belong to a single client. +It has methods:

+
+
getName
+
Get the name of the plugin.
+
create
+
+ Create a new plugin instance. + If the arguments are not compatible with the plugin a NULL shared pointer is + returned.
+ pvFieldOptions is + a structure with a set of PVString subfields that specify name,value + pairs. name is the subField name and value is the subField value.
+ Note that a plugin will below to a single client. +
+
+

MonitorPluginManager

+

MonitorPluginManager has the methods:

+
+
get
+
+ MonitorPluginManager is a singleton. + The first call to get will create the single instance. + Further calls will rerurn the single instance. +
+
addPlugin
+
+ Add a new plugin. +
+
findPlugin
+
+ Find a plugin. A NULL shared pointer is reurned if it has not been added. +
+
showNames
+
+ Show the names of all puugins that have been added. +
+
+

NOTE: +Should the method causeMonitor +have arguments pvField and pvTop +be defined so that they can not be modfied. +This would be posssible if the following was defined: +

+typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr;
+typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;
+
+then the definition for causeMonitor could be: +
+virtual bool causeMonitor(
+        PVFieldConstPtr const &pvField,
+        PVStructureConstPtr const &pvTop,
+        MonitorElementPtr const &monitorElement) = 0;
+
+But just adding these definitions is not sufficent. +In addition all methods defined in pvDataCPP must be checked. +In particular many of the methods in Convert must have +their arguments modified. +Big job. +

+

monitorPlugin example

+

Example Plugin Overview

+

This section describes an example plugin that:

+
    +
  • Only raises monitors when a field changes value.
    + If no plugin is provided + the default is to raise a monitor when a put is issued to a field.
  • +
  • Optionally a change will not raise a monitor.
    + The change will, however, + appear if a put to another field raise a monitor.
  • +
+

As an example assume that a channel provided by pvAccess has a top level structure +that represents a power supply.

+
+structure powerSupply
+    structure alarm
+    structure timeStamp
+    structure power
+       double value
+       structure alarm
+       struvture display
+    structure voltage
+       double value
+       structure alarm
+       struvture display
+    structure current
+       double value
+       structure alarm
+       struvture display
+
+

A pvAccess client wants to create a monitor on the powerSupply as follows: +The client wants a top level structure that looks like: +

+structure powerSupply
+    structure alarm
+    structure timeStamp
+    structure power
+       double value
+    structure voltage
+       double value
+    structure current
+       double value
+
+In addition the client wants monitors to occur only when one of the monitored +fields changes value but not just because a put occured. +Also if only the timeStamp changes value then that should not cause a monitor. +

+

The example monitor plugin implements the semantics the +client wants. It can be attached to any field via the following options: +

+[plugin=onChange,raiseMonitor=value]
+
+This plugin will trigger a monitor for the field only if the field changes +value. In addition value equals false means do not raise a monotor +for changes to this field. +But if a change to another field does cause a monitor the change to this field +will be passed to the client. +

+

+Assume that the client has already connected to the channel. +The client can then issue the commands:

+
+std::string request("field(alarm[plugin=onChange]");
+request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
+request += ",power.value[plugin=onChange";
+request += ",voltage.value[plugin=onChange";
+request += ",current.value[plugin=onChange";
+
+PVStructurePtr pvRequest = createRequest->createRequest(request);
+
+MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);
+
+

Example Plugin Code

+

The header file to create the example has the definition:

+
+class ExampleMonitorPlugin{
+public:
+    static void create();
+};
+
+

The implementation is:

+
+class OnChangePlugin : public MonitorPlugin
+{
+public:
+    virtual ~OnChangePlugin(){}
+    OnChangePlugin() {}
+    bool init(
+        FieldConstPtr const &field,
+        StructureConstPtr const &top,
+        PVStructurePtr const &pvFieldOptions)
+   {
+        pvField = getPVDataCreate()->createPVField(field);
+        raiseMonitor = true;
+        if(pvFieldOptions!=NULL) {
+            PVStringPtr pvString =
+                pvFieldOptions->getSubField<PVString>("raiseMonitor");
+                if(pvString!=NULL) {
+                    std::string value = pvString->get();
+                    if(value.compare("false")==0) raiseMonitor = false;
+                }
+        }
+        return true;
+   }
+   virtual std::string &getName(){return pluginName;}
+   virtual bool causeMonitor(
+        PVFieldPtr const &pvNew,
+        PVStructurePtr const &pvTop,
+        MonitorElementPtr const &monitorElement)
+   {
+       bool isSame = convert->equals(pvNew,pvField);
+       if(isSame) return false;
+       convert->copy(pvNew,pvField);
+       return raiseMonitor;
+   }
+private:
+   PVFieldPtr pvField;
+   bool raiseMonitor;
+};
+class OnChangePluginCreator : public MonitorPluginCreator
+{
+public:
+    virtual std::string &getName(){return pluginName;}
+    virtual MonitorPluginPtr create(
+        FieldConstPtr const &field,
+        StructureConstPtr const &top,
+        PVStructurePtr const &pvFieldOptions)
+   {
+       OnChangePluginPtr plugin(new OnChangePlugin());
+       bool result = plugin->init(field,top,pvFieldOptions);
+       if(!result) return MonitorPluginPtr();
+       return plugin;
+   }
+
+};
+
+void ExampleMonitorPlugin::create()
+{
+    static OnChangePluginCreatorPtr plugin;
+    static Mutex mutex;
+    Lock xx(mutex);
+    if(plugin==NULL) {
+        plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
+        MonitorPluginManager::get()->addPlugin(pluginName,plugin);
+    }
+}
+
+ + +
+ + diff --git a/src/factory/Convert.cpp b/src/factory/Convert.cpp index b437187..ffeff40 100644 --- a/src/factory/Convert.cpp +++ b/src/factory/Convert.cpp @@ -18,7 +18,6 @@ #include #include #include -#include using std::tr1::static_pointer_cast; using std::size_t; diff --git a/src/pv/convert.h b/src/pv/convert.h index 2f04655..e6cdc40 100644 --- a/src/pv/convert.h +++ b/src/pv/convert.h @@ -63,9 +63,7 @@ static inline bool operator!=(const UnionArray& a, const UnionArray& b) * pvUByte, pvUShort, pvUInt, pvULong, * pvFloat, or pvDouble.

* - *

getString converts any supported type to a std::string. - * Code that implements a PVField interface should implement - * method toString by calling this method.

+ *

getString converts any supported type to a std::string.

* *

fromString converts a std::string to a scalar. * fromStringArray converts an array of std::strings @@ -478,7 +476,7 @@ public: inline void fromDouble(PVScalarPtr const & pv, double from) { pv->putFrom(from); } /** - * Convenience method for implementing toString. + * Convenience method for implementing dump. * It generates a newline and inserts blanks at the beginning of the newline. * @param builder The std::string * being constructed. * @param indentLevel Indent level, Each level is four spaces.