From 3d00eed0f001dc88fea6f631e4a6a8450bce9c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Br=C3=BCckner?= Date: Tue, 14 Apr 2020 16:13:26 +0200 Subject: [PATCH 01/10] Change SetTrimbits() and SaveAllTrimbits() to rely on top/bottom signal instead of TopAddressIsValid() for further cleanup. --- .../eigerDetectorServer/FebControl.c | 8 ++++---- .../eigerDetectorServer/FebControl.h | 4 ++-- .../bin/eigerDetectorServer_developer | Bin 349282 -> 349314 bytes .../slsDetectorFunctionList.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/slsDetectorServers/eigerDetectorServer/FebControl.c b/slsDetectorServers/eigerDetectorServer/FebControl.c index 91e3f8ab2..0ccfa98e6 100755 --- a/slsDetectorServers/eigerDetectorServer/FebControl.c +++ b/slsDetectorServers/eigerDetectorServer/FebControl.c @@ -898,7 +898,7 @@ int Feb_Control_SendDACValue(unsigned int dst_num, unsigned int ch, unsigned int -int Feb_Control_SetTrimbits(unsigned int module_num, unsigned int *trimbits) { +int Feb_Control_SetTrimbits(unsigned int module_num, unsigned int *trimbits, int top) { LOG(logINFO, ("Setting Trimbits\n")); //for (int iy=10000;iy<20020;++iy)//263681 @@ -963,7 +963,7 @@ int Feb_Control_SetTrimbits(unsigned int module_num, unsigned int *trimbits) { int i; for(i=0;i<8;i++) { // column loop i - if (Module_TopAddressIsValid(&modules[1])) { + if (top==1) { trimbits_to_load_l[offset+chip_sc] |= ( 0x7 & trimbits[row_set*16480+super_column_start_position_l+i])<<((7-i)*4);//low trimbits_to_load_l[offset+chip_sc+32] |= ((0x38 & trimbits[row_set*16480+super_column_start_position_l+i])>>3)<<((7-i)*4);//upper trimbits_to_load_r[offset+chip_sc] |= ( 0x7 & trimbits[row_set*16480+super_column_start_position_r+i])<<((7-i)*4);//low @@ -1572,12 +1572,12 @@ int Feb_Control_StopAcquisition() { -int Feb_Control_SaveAllTrimbitsTo(int value) { +int Feb_Control_SaveAllTrimbitsTo(int value, int top) { unsigned int chanregs[Feb_Control_trimbit_size]; int i; for(i=0;iKS~UW0p~oCfzvQn&_RkfduhctDb_)8Ii!6L16cov6Vf zl61WWngS#bZrUv&)(ku<`lcY=yz930f4IYrB*%~}3 zNpl3ePm-i;4Gxi{xf*Piq4ku2q!5ScvDFTV1Uuw^ETRq9&biYKOnF1%a9?GJ{5-9a%=un(u#ZT|HF5iW z^pF(6IqBO{H2qYHvp*xb^Zn-F{7!T5f;S(JSn$c?(fNJmV9&V$S5^N02G1vx$`^bx zJnY#!V7E6(84HH@H_+7U(ut4xO+gW~^tv0bc|6Xx`SCc~a@`(Jd1a^^Q8^)~qADGx zk<39>X`G4+4SIg1*JM#~*X*ne3aaoPl)@aQ>Ib2Hn84TS@Ec*pO9J1b!!HH?tiX5Z z@G~74L8nAPx2_-(46OoxeiXmwGVl!oKd8fh4t%AG9~Y*>zYlzgz*}_q*MKij@V80h zvULRqP>?Mc96J1Cz{d%Eu@1i-KEbUPTp0@Mgzi_Y;V}K+GWAYeOIA~R*+RMMBK4L{ zm&f^OUrh>qYgz>9puUUA)Z#Ny|MlY{E4lUSRO<^lb?wyWi(<#VA>-+}>nFvu^D1&I zXrw|~*TJ8yQe!2v{)R*bnW`qzHp<;J3DhTEkJaPnOnoFIgu4qr* z7MmnVz65EBBHleg9mYyC9)4YIhNL+DXfqy3Q=3t_gNDU8(_B#0F{Z43-!iCrG05jkQ~OQJ+fD+Tbke+ZP_E+X=hAQ;zhK_!AsGeVsI`owrAHx zYMQ(%rd@44$#zIG2Ij_39a80&&f%d@IuJW80V8D3<8{1yl$P%~QrDxv9RKUOv$Sn; zY$5`fWYbZo)Pe$%7SZy!NuJ$#8i_Zwu9sJe=*^=*F!z7&gu1h;BE0#Y%R1UV?gtJM zc^O{!d8IqHLVaG9<&c7xVtg&)I3X_@Mj#H7@V@dZJ;Fm@1UHN$mL$Bc0Ma9b3)c<2 z4%!s!3`V$pWGn5D&0IaUwFpOh-m5zcH~*Sf=GpUy3QzN&D?IQ1t)lW8C?`Gg)m3dE z_-I*N?A+I3iHT!XopL}`br+Ais;XqlURBdwAJC4td`~YtEQIT9mFju`b1&oYS-@uQ zAABOgATh&8XLN?E6-ME)Ke%!{j6A-sN8nUHcTaGmR>fXEz7g?wL?aJK|G3s-wCm_`w}yRR+m}N8#?#a7|@8 z5rYI#?)&j*7JeVv0%sR<=7cV7nDE-I{<=Pg=|Lq*sk23$)?C#U)#mo)S)jqz38yPf z^sXuKB9R8ormEozzuQ2EES6S+?e2{lXDl}N3cp5rTX)8RmF1@ZZ*9{|^ zzMplKvQyx*qb%|&WnGF4WlpQ>%(JXC2Un%lb&1J@H&3%V3YD@`qAXco29Grhyy5|( z*`3sz5ETPX$s|fmdM?_=OL^E8r99-0)JOXgqVn{m%76s*dn$E(v`vk0A=)pxH`gek zD1tgHNu-mOS#G%{%4Bs5 zi3`FPDK)9#p_4ii??x?ciAf}b_9Ui|9dsx$ffUiWsj*}(wN2d+a|xYbMmn{N^W|8E zht*cp4bax9NsBJq-13hcQuVqmLtOWQSP~zU&Br~hx^Y68}h6HHvDsU~x)G1}5kffgz3Mxa%ZVZ zz6L!Vf<(c0R4_Z0>+{`pD}ZixYRwo&me9ELq;Ni{qTw8I4*99ov>-hpa(CsAk}4{< z##Zn^xqHc4%u7F8M7`0}EH$y^Zm?pUE6!L&0ciRKsIaP1opf;9xJVGt8an=)6CU9>-Qp?ooz zW>_EEex7@-bGYv-kXbPJdFE?z4*xz6Mzc6fn~!*>AVBk6MGBq9ouG0Y{B2xar~*b$+GkImch^K!TyQ_wOdHKjwwni(7I`S0cvv{zmzxwA>s@j2OXB=2J- zHe%#~Aebj_-fQZ5kY$t`<{zm+nd5oG(JQrzFzLEvS~)jq-R0Z_^fT|t~!*$BD>`fdbIk>DwE4)-1bPZ4+`Rr&Hho%}4&@>L)AKCJqBcY_wIzT%5?=lsRw z0&Sn4wC-(1KF`1I;qpH|1fEjCQ|cW4=u7aFf@i%dpL{$j|6@J`%LHv1Xx|2H8E9ux ztL?U6C0k@J^d)`H)|aRaEM-C!!Mzg& zrw-(g@=#d_D&zhW;v7D+7rgMz^{%7A_$BsQ>O{>Mb2lug_l{`Zm--&NnfYxjrEg)SEw_Z?NPq zBd&V;GEzbv_#XiT|JTx9`v#AH>`vN56(@Gm98&aBK9>0+2pahvQqQ}(XP-kl^PcWm ziG5bd+>5bZtF0uwZ;?iaY3&&pQQ2!!bW~O*H?8JLY=~AMmL~9yHLF#Ft$hj!loA-S zEEBO^f$avvZo%q{^wOf~^3Q^4+)W8N2eg?-slh$b%wzxVX_%&OUW;D&e|%hVdvjNW zz2E2K3igJOoi{Bby|m+|l+X*nMLVSXU#I;y&0lxkAw6)8w@=p#2%!@@ox}gN!q|JK zpAyo7(R+N;kE^Y?2C|O}8jQ^7Jgr!qwDf$QGE64+sl()bZV0o`4cYx^o_JstRE1U@1)H*6!)FyW6Zc=F1uG}Z^&zx- zVL9U=(IsKO_r?O zMYwWSsdi9}6Os)$WhKOM^*6^N*NwY5cD=^JH+512SVDly7{Mv+mcoGJ{twGvZ*va+ zIuc4{Ln*%U$#xFEcPEs}c9>SYPklE>WqC=I(!e>Ksjzpc-7dp`4bjPDteHB9ngW z8>8iZwp4A#O|Q__)oZS;k3he-C_N?J&`QJBBqZ(-jO#^z@yLT<{pMld(O)-Bqy=j- zXP$@Xa8U4&$te&r-&_w0gv>?nR^^W_Z|l<0jFvT<;qPH<(_@|jj{~cZ0*CaU%D_Wt z!NkT6Q`g$4nD@A{>e}SxeSo6@$2p{zS5xoW+dUC+S~S7Xj;?~+pi=IVsiK=wox^W% zmrQk-?Ca1lZfC@-h|;U@Z2@0~;L8BtE8xojUp)BucC-VHf|5tS0v!+D)=av-@LCTx zE#6E7iibbmZiK%#wu2nuFaKkXb4YJ}Bb>4T&seOybrE8+j8kJo|LC;X@d4!Ng54YO zkcVKGztXo@kEv*mJDEScHcE}zgnnQK%i(Z3X3LkDl;>$|w3*ck2*hzqatd|H<%oZL%YMXNyLFG|4w zoyz}xMNYsO>tkcez=hG%KGVqclU>K%>xboOHEO@S#%EMrl75`esOXpZIE>tPkq#tf zd(aNJawHO;6R(Rli;c(Q!D^f5?-*@YWfiYa@brS`F+B5KJ|6ueoWrkv0Gu@0VEAHCv+*VHE(dQoKXc&BFu|+)=#q`oZ+-Nev3)WLL-IHeIdP&-@cb$d zIdKltyjMh@@bm(0nN$Q?9@zOdLl8tGT)i4YJ2x&)EJq1X0k}y_D1ocK!czd4WZq0@ zTt$=bNQ$flm6lS9{(ogRExsci6MM}a??kph9=E%0)@j>C-J7CTH0D@vAc@6iU8>^e zy*dl@@rYElNzJ7=O;u^#Xw39n|v`;3%2ikal>iquY4`|4p znP|7|&IRi(I81Yn!qz7EcM)oWt)nF8@G&0BxaV@eLA-uB4;Dqdp3OH^_-GgX5{0s3 z?}C>53#Wy@ygZS1-RVp_Dter2!$%I6oDAyKTvJfYch)Eb0D_{GZs>G;u^wDqn8QcX|al`?U!(2dsy-CEC3)8;w6 z?VD3bD|K&XIj2!W8b+~_i{Q*NRY*Py{6ql{LYs|{#2uS!2giQt2(^~HI4K%V+sfw^ z-GF%!qp)OtWEo0$V|An9#RJE4x11!AC!E7C@?PPd&j$*Gw4hh+{%GQ9KBmw<`1$a# za~PX(19wlB-?(@@tn_g?t!wNOFh<20E>lFGrk(dh#prG}qZkAn38iBHUc29rx&!wc*3gAzTd@l1tFhAyWll_B+1nq%zHHm} zgzz$j2ZO>pVSf!hyL}s}IqtakPcrcxx9)hF%x3B~gE&sb2o{^;45v4IxlWR~06m@# z-9KAq(bW3D^hqol+kb_KjYDncS|!??!#14=4BN@LPaC3q7!zF!_QGze0EY zW%7`&U@jP1Rfbq)e^%|QDLkKP5cp&reK7Eq0&g3|$A1aD==sa$4}{CuE`&aF?S4HZdqZ90rC%OaVSXSKE^D!1lH^>OO6Nv54FqJCFW zn&MIF`noWfX47nWmM4eRv$@ess!EtgJ>#yo(T;~RF@X9XP6$`bHiD0S1o-`HNmQZ( zc@-&~V!lz+wQ8$sVnOSm-bbP!qw$ePW+rp()}?u;Y)SKrbHQ(|JDJ)mr_Za8;}eHS zq?*2^ih{yWQN40jU7t*wDyPp@O)V^|M(G2RYHgJ)E(_h8tD zJW3y4MMWioz&yU)bHWw>5Y;T9-Mgc3taM>_Cb6>a6LJL2cr*#iE7zkRkyM)M$qXPb z@uY*?=UE%NgwG}1mVBunNr`t6S7nKJ4zV1s@Fo$kH&q3)_f{os>H_hb11`n5jBMfWNH z`#iBU^FI#W6dyM>I0>8~IK^2;hidpMqroZ8M$JEriYe5U@DwsfBN8DnKBT^e$B($0 zcd$X!SMz(~qMduw@qhnb5B~Q)xsAh5rW_ysk(~gv*Yd)$T4SN-i9%l3^PecJe{!Vk zc>j|@CLBu)&>y3;w&6+qZ`mJ3Pd0>8`@U6p^6gV`cL(!zn1!}wSZM413E?gA7Ii+c zN=>Eo)c)I$f7p8xX&#%D=hwU!<8}mwCpYWxBQWqj9o|PPSkmfRj>$vFx-;z>G1t|X zDOBo4M_Hj{zL6Wu5p;PP&&);a}2&0AK z*&?2cXmw*I+IFHbDO{lyL#maYZ(P8w^;9M)rLL#u$Z6NUd+I=|5K$Aoho)5bUUGDRN zbdO(^uH|{>q87d$(gk`KqLW$g_e#J~6HDa&e_kC}PP1R&hkoTRa3Ar%5Jl{?{RM8h zp%>Eee`-rYxT|sk0-+n{R@)LvXkiPtrLTpTb_A5x(hDt%VWW&A{QvSJvG~9C$SP2t zJ;M9J{35qt!Hd5q?KI=12as*}Us_8p(MvBmQEG4H9bekI3IBJsa{8fG9w<6q=5-Yw z<*@f?EDz^Lxi9w|<@7^GIeqcVoZbgYjsC*R>G3XQBp{^oc)jSra63+A+Z2~{TZ*am z7;j10F)pRy7?*PC7?+aTrq$Fy%iDNOEp42pyN%OWU*R+*uV^$~wDpxg@MEJ_UzHQ3 z<4cZvWZVZTI60ku@!Dh1wfrY@;MC1Oc|pN`2p_hzZ;mMv*@%~GlVwS4P_&W~J84sU z5;;XX+tbMq?Qc(stmOGKKRh2@V@@^E)YtFE*_H401oT(i>liBZEdDQ~AwS&)c;`>K z#r!|b#OUb;TuX<3nm`(8+#8v&e8C$d^^+R4{;BG?m8M>xU2i0je%k-WW|B-@9S`CE zt`5()Xi@wo@6X0JQ&4l~n_P>2{9j0;f5x?_{u$Sz?PpwzzMpYBM!%)t-%{{zDR{s& zG^~@K`&m0Zq=mM1E(7IY=Qh$tOWytme5Cm23*}^qHvOEtW#7+vSA@L71AzM--hu9S zV(H0uqM=ClJ6!P4JDeiOb`Yo*Ulfw4LuBi*)hOsyN8Vgr01r z)xXFD?TKI9hyRm*>BRr#zr33i)81bmL}yoY%^^E!Yu5(gL*7dvjnw*HxySdG#jSc* za+`DbXLZK)X2&_KuHzg*zj{m^uNIdr!Q%3IEJf-z)0w=nJ2uIuAcybp@Ld?bqQ)*v z$3DohKu#W)gNq&m4(SF0-W+}o#~1&pLWuz|hl4p;Fk>^IgKq;TgPDv5jZh4(U!|Z` zmA^{y%+!>?k~jl=#37AW?jZ_?#oGFY8izDti;m8!);A3S=FRn}kMnV5GqC;2H>NWj zLGP^)%1WYMoYRCfAQ_p_T!(P{!bGrqvan&ZFO)ao`>?7%wbSmXFu>yWyN82H*udB(GKPv zMC-I`bjlIxqGw32f>X0>L;j-0amaX^4t$V6KA`6Rj*9sftfL-ZS6h5}w7+;rIqxDd$zYq2m?JNw}Bm@b6VV8e5@zR&GXmI+4WT{i#v>8|RF4 zZ(Qc`I2}*;kc66X81VO=9E>UJnH2IEEjqJmx#(i40F8~tL30GoJ@_`%8BLeb06%Rf zHb-~BNV;viwl~2S;t2ZL7CLZd8zdM1CWWl0)xSy48X2=X360p4<=f0U3CG@7CE(^; z-)}ZSg8ie|M4mt>+n85N8nB`M9wl}5=jC{#d0Mxs=J_e^wJAGd?ti0XI4y+~nCIwgM)R~1)jY3b zUs$;YGx+UNeln4ELkbLbbcNAiFFmCi?Da)L3L@IK8SJ%P)b=~YV6C4U4fYa0&w;_( zKc*ESS9+zG|W_7U}tM9@$4uUAyil5;8GuRWJe zrqT9u=~-7fy_k;p`66$J&gEKoJK%CZSwtf~SrxAhF`skz>x=y2As@0lbYQQimU=&l z!pU6YCkx_xoWn-~hSv*Xc)f<<^~ow^Blh1rvufR;>RS(DrBw^pvM4guHC}CD=(z7| zbs)#DrQKg(GxxJ`;P3zavOHytsMcRQu-hWpAWzoQ>ty*0L#3bV%KWsvFV=H42__wt zTCQp{MgoQ)KfjAcAT%R16li={^BU!*iFaqzm8nkV_4Q+f(zs6p)%sTUdVfi%MbfH; zf&4IF5uR~0F?~QCbg*k_BZ2nU1_gD9c z;4WtRlwu*2_pa_7u$lKRg37Dt-B0HeKTZ8C0e$39{xAJ()x`3`(4hS~Ma!xEd{j<( zNvL5F2XtdEH@USW1D8I z3{{a-lr{5%Jl%Q8v-}_rL%MA%^}TIlu2|Vb+ddn|?IU;V+rsTLm$%Ta#|ptV=kRl9 zAXsOgXA>b9_Br+*tvw&B*hU>ms?Q0_rWS*fpVRO$E;{=VKMlngKZZNClDa3O`a7@P zt*?Eo-BO!XyFHf$nL4}OerPv@!f?mInU}j`*8sVp^$cw?kDBq?>3Dz7Ih#$Xzu5uKzAZ{kdFeakXrdDSfcQ#J?$$DRaUBjGR{KA>yW`$N8DSG=N}A) z46Ke?`57yz3}TZOky*5_ER6cog6Wrky4KTN5=!(6HN&RLCO3v#vaV9l$p$(oWzm;I zsbA?Drk7&|9y_Bm_$7ug0rS0NT>ZU$hDBZP9n=D!m>HiDf$yc)!DQotb)Cfh9&S3r zg%&=67V8}3z{hAY9OR`c+UA{4CtQq3Tzzr7pA{*bYiK-AlOfbTJC+Ys5m9 zvYkZJ;f6?B^yj6Ne6f({g{>h{*puvpmEXE`**7JowWj4VB=nh)s5!;XB0~YHSZQ;TU9Hh7efsh^n5X| z3tId6Vjlj}yn==dWUhdH8+owO#nt^hSRv3fYa!DrB(_3g2pV!uG)%rCmo6mcRaVBL#;>OF*LNUQSsLznuO7MH;4W_5JPY4_t*@wB zE}5#*@FfU0p10L|spYGvtka%qye}*KgTFZ89Ny2Pk1pKrwNbsjkm!aGT?7&gAzYF!`&%s5 z+BJX6^w>ei!;Fcmx4?!S-A z!sw~f;)1v}=M!<^zz5uU_?&<_(Z#FHvamSX`1b_9=a!tF6K+2+%%c`}tT>%x*#xdE z&*IV?OTmpaB<>$mZf0?6OIX=xOMb++QUitxZ;9_yv_v=do}7u6VC*#^yu?UI9kp>6a@XMwjy%uRm<^BhjO+vm@FeU))7n+NI5$jOg_X@+co6( zc=C-S58%goEy-Zk5b`^egoToPVmnz7O76p##W=-O_DMKdt&+{xP(Qj>Ln)lMv%7E; zUuB=ct~Ha;7~E)<&gpDj&NYTcMa*i3o!rc8Cb2MA6aR6ub~Cw~3^B_@av#L_CKCQS z@0h_m&BA&ol5|qWOxKg7yf)|XqcIv4`g2g%OTy8`F1&JswBE48&PuLFx7k?j^(6M& z_TC?jVdW^EX>$&j>)lg${yU3CD<^!t0XNI4W+69_%n_$QKdMte{Y5t@^?Pq1vBvui ztVJ#9zJWwV>JMzhsK`3ahHfAmg8hZ~&J`}EXER>}xh@79Gb%8~l1#!N@e9`0} zI|&ozkcV$Kmn{Jjyyz8GfO;qi43yS@#HON zUOPq6{KOQJ$%n!Y#Y7@Dn}z;?ZLyBFw2Ni)pEg#Uh-i17H6)Vn%h@3; zY$|ASS;bV6L|$V}Q_)ihqEq3LG0c{P4)d~-B*M_%p(Lff$;oPaw|%Lz-?z0lW3;{H ze1AyzW3t-bvM~*C4worDiuT$@$h$$#+q><}K=ZJnWb#YW!cI*?vrn;t=~wHRQbpo- z@0jh8BQ^U|R7JPO2iEKy-ugbr89Js9?w zXa?EiQICW~-l;myJQZiS$~L!*C(V2r>j>J4Tc<<}lDXA7{1b48OR9{yqxW)ywmt~_ zS%E*Hqu;2zXFIn`SFjWeJRQU+oO@P>$7>1NJ=?hhI(#JXl|p&bD8BSE@FfDTEJoD^ zlzt9;fr6hS&CAd+ypMux!C=?nUjv>enNVn6Aq&gEyj9CmGhoI><)&@sJrOKrCdr8J z-HRnFPaK{~#}*wDUK}Lfm}fBu<)qE)W#u!$*vo2_pOY;!NlFlMh;A!+U}guT>voE$ z63ufgBbRP3@NzZlt-rv^vq;kNK^p)=&zd-@(ke*}HLAf%f4Y-FWulUSdPJSy8Fm^+8;QPK0*&qoG62s##Ya!41)q84Jl`0yWF?3B|xQ*^zhjpWJCD<8a56RW5?~Qf9gd!N|vKH({da zV2wAClSolqi&2)#dKaUtgoQ0ZnU9q%L0LCTUP}H$&NZ{vr6{zqzNIKEVfLF*M>XrY zncNrLj4crh>-(Byu`jdbDCS#6ZV2vBkk)^2mSt#BFAH(NE_o(a0j9b#)>eSdXlKzY z$fnTtJd1f;PTH{&=3PNDr}FsT z!X8$=lB7>NFCGWgt(f2Iz9>{bT363Xk~wiqT`Xc1dZUC@tRhq8d=qP0g-N)Con8g4 zOIWIlB!u!LBFNgApUYe>l9X05dV2lIVIJi80L6wBZ$gmOpFc|81ab)Jbde~yPp?an z9I_e{N;}J5tyJ5%8VPqh>sd{1gUK@1AQ5a=h8MQ2)N5wiDlEU8x!0ft{Dd|--Xis) zx^Y$sM)a>CN#T8XRF(=joE&7`pd^F17d~k!l`!@LFNUks%Eb^WDHp>BPrBBUKk)%! zTL)jMVW-zYGaDaZliAc;$u2ac=~lHp>d*OG$&-l-aR=@QHv*qqu+kCNBjgIj82At= zYq$;VUBfzW!_e$tef;Tm7FL9(UY1dWCmSo`PnTE)o)T|`K*g#?fyAK-v`4U0oT-=% zaHec#UQbfOk?9A0E$m@qOV&fpAy&E`u0O=O)}uLvtoU|#V-ah(9mAr6b@8Xd{Tncj zDp}12P86M3l;A0e&Em&)rBrI0*jAS{Yt7d9ha_)ivmiZB-;~T;rN~|#tiP1JfEpXNlD|TY>U)%c+H()` zof1~I4W=pCUk1xmvg|Uf)%>ibj4TN4=C-i9>w8!~9!WR$n$+oMmD6VRnN<-$#yPnOa?r zAm?JaJMiOWmv&&(w6TKw$s-f1g}?C8$JOX!z4t@vc{X%EStBnAVXg<@HeIap0a!bg z4Lv}Pjqq@js5~7zF-JH~njb_GMM_V0f1i9rNZZMl3Nitqnp%Bvta}&iKFI73BhoK5 zv9gCD)?0sXB76N|k|i&VJK4`M&@Q@?Z17|&!2)q@^z*qlt2jemv(z>^%SVTg<~2)Q zqxh(=G5d?WW~nX@iaG~8pBXVDF7@i@-__+cOB-|rN5LTSnx!o|{663tgrE)`emC%y z0^hB}ZvnnU;Lq#uD}Yz?npM)$L0!RI6o|a$<}e*T9{4y_(9NSSD+EOayRlyfyvR8A z*Rsyta0kq1opKfnx5(pI$DCOsAP~>jN8E17v z`hbT#y=ED2@c$2TBkD%vp7oN&6O+Y2a4Bf%jSgn5!Z1OEs3vI>wmK_tk})o48}J=f zB-X=Ub<$0~G?{CKV57X;U6+ol@K}ZvhME>C+A0%t9rqDkl33@1EtP;J^K_p(4kqAdC) zfcrJ2_qQ%Q>T!s&KSt(+x}ivrZSOK4 zt9*>CO~dtYY=9Ytx^^$7+6v;#Ahgf2^#R|Je4|ApF=_Mh$W@3+t|dO*`0y8Z{Q9 z$752_yI~%$L$WvFOBoi$p25CQn#x>zL7d7;_mW3cf0-x4P2zq;Hcc~F*(!}%8F0U1 zhpo#mF#nH~YI=SIo2vdb4gRHnxl8pgbpavX77YrW$XtITaVJY3CvOCQr<#VCqmJZ% z7t&j@S!*3x_MJ$}SadzP?K_dSo@}T`>N`zM36xPmBQ$5LN~hczMK(qw_BW78lV~&s zi9#}ZI7Oq`z%3;Eq`QGE2tsc+G3?1YL)Z|*Qp6d-DjSiX*qP}mWhqe0e|#+BY5bgE zP5h^qCI1-deu0U#{uqy`EV~Jlrh`>CVfLv3R(xXgR(3u_y=smc$~0pJJ~bxy=eLGIVe{{SoT4(W)_VWV^0^# zsIq^J@fd;+f`uw(P%@@EAMJByGmDReF>^PjQ{R3+Ntx(_1fE;fDv~7xN1LA{Eb6iO zgD%)Qr67E3m9N4Jb%MTwE3UAzMnCc)A9cNyy8jTYPWOG`}*ipSw#py@2w4#-{n@Lp6sgYSt9>{|1 zOI)^9TG7XRym<7TH+ew0fuk0$3}KzkWYXl7aii>i{Q&F@qTI>`IQtS^@gWq8$#-QD zvm8e8Xi^~mbq9*G1-F;EP&{ok1(5%`C~#)wk{$A2KER9HtJ%53if8fZg}NrT^9AH}F4pz} zf=n^%dV!<|;|8Q`X5B3$DQ2fb?pL;q#Pr1{0PJP2LfYAafM#Xx7LpRv2&$1CZ@7&{ z)y({ys)?Nj6>`W+{Hcpsjv&TgU}Z63p8LZ?blr3ScFJb*u%+9_<7K;~dg%h)kR+2DLbHNN%Nmn^5ZbiMdthyBeFoU(X zVv5ON=A%gdo0;_}Nr|sj4g5!bQ5EfSwK?QJ@||1`x3ir`$pdMp#=6>S6=!fYR~IwC zjNa}Wb%sAyjk=m^h}mC;FReD~%2%WOlv10tI+>Mo_R)+}{$nDFg{!SDV18cg9_9Z0 zD-;V?TfLKY^Wqv^@!KdCuC}^q|1o%4o36C(>Yny_qT*>!9D}D_Qaml8jYMNXXKN!H zMrUH@mAfLHSGI0N6(PQh@z)*Mrr(mdNbQdg_%PC= zK^AQF(m3XLh0K~w)r(n8DxOc(xLD6|Z{S-2g`8HMm}p#{g>N^ksbDRykohq@zgZGc zjQxv_4mS7-Ne}mp7S;Lq*A@AWP06b`&uai99|(v5YZ?KoV@6uz2h4Tj_!hun0&dg( z@WcM&WRhpCG}a*m&fpMh+986g&&BcGc6F^Rt?2@+ju2^0H(*VbKERqP1AsME1_6(# zQc3H4DXfDlvN>d|A_8YnWG%km6w<9KvNi+oDFNF7w+px!aI1jJ0s94916cUrTG%I~ zM&M645Tz@{16mYPF5q6kB?3k(LW)$lFa&Ucfbo`1h+V)Iz`{)nvjOv!A}m(81aO>y z3jv!2Tmsm{VGoFQI;3%fq5x5AT)%*!>9{@tp8(t=;8TFR1Pn#TbqE-Wj%yR}5a4DN zUS|f}DBxtkwF0&Q_ISlZ0UmY=7^WLnD&Pjd#R6^v>=N*4z)J*t0kBPlZ#4nV5O4%w zi-38>5dzKz9HPRWTbJN7yhEbE4R}Dnm4MF+*ax^*z|DY93%DI{r+}f*H75iNg|2B) z;oG3lHBADJ2J91X2H@&kT#P_J-Dbx_g(xTlTqfY1fZYPF1zafL7QhYxcLL58a1UUs zfcpU_t8kG7I9kAAfWrhF?ZJa29#Zkz)+GU>Z-Orf*ai5kfZ^f6-2z581)madHQ;su z`vJEKxC5|Xz}7( zz}W(B2AnEjbW?DgfV%*j1>A>CgkY0+fJUZ46~5gBxL?5NGgF^{vjO)A7z&xX1Y8cd zL%=@3Z31os+$`X3z>NYP09>oU9%+La4_>ukg9Y$T0b2o=3fK;~Sil8^$BOiKh@ z0oW$s8o(I>ZUk%*a4X;l0iOaKqF%L=Ho$R$hD5;yzykt?ra|XbxENLm>J>1o5_DR? z8Gt(lyaezG0mD~Qr`((d>3jsF>xD@eUt`QHFD5w;$4{*7Fxk4oZZU z;M0H$1l$MMF5pXmvsHLg7~oU^Cj*WXFf1gSIgI$fiECsM1*O=Z8x$}UBK-n}LZnZ? zP>A#h7z&Xt0Yf3uA>i|X+XM^?k!BTkn*lcp7zz>PwH=tz4ULFb6hI-eQ@~uIQUP;? ziUr&V*d^drz)J+&3D_p!ZonA=hJ}blz_1XBP+`xVA$SN;AMUguNf;8a4e)?~3jm)N zup4l%fGYr>7H}=#P67J?pAc{d;1&V*0&Ws;KVYAT|944vs8$Q^iU3?8;8eh60)~Z@ zmpEXpyIg<^1s)dCvZA}ZfO7@D0kBoTEr62+%oU0jaBmnM!ocmz;l!8@VyNJ&nwg6KkgNHv%q^q#lO}5G#KwF z9mDT5g?JP6cui285r4KwRKPo5M}JnM=VsC2J?DIaA=Oy$Nu0oE81d&b1fJVkC+PFj z0-tTfUx*fXn-PD}tKe~em3N>{&|fqLyAl6Yst~lqi2t8U0`D;5|5hXL1xEbeI|bgw z@dyZ7-~VGs6mU<|DfEp^;EMwAT8oQ~_{*B2?f|^zD~Q;*ZUy}`&ZPWzifjYfQ^X6mK@yjFqVh#y}mDsI;Bo;+pTPtYoE(F#<2c$i>l z4Zybve47z(E)n<>0r)|IZ#Uv6dfG)nhq2&#tuH$R@O^^*lo5Z!PJ!<-;v+%?{&WDo zNyU4lZeu~DT`=?n;Li(uuMt0~Lg3FD@ll%P`U3ENL4RJsdq##|jK*-m$Pm*n82XKP ze2-5J$pc1wY>~iUGUDU4h7IcRo}9FJtz|>{f}AvqMHQsH6Q*<0slwF6WTa0{7W5$j z_(6dWGvcS62Ht~7g_w;6(?y_ACT(5CDW`;>Xd`{9=t~l3#HVSBT8#J^L*zBR*g2<1!=O?i2LoM*Je1!0!yew+noQiuY)<-%XXGpwh^&SgXKm#4ph- zRBgoL6j~XU%4?ZA-@3U>;A;)^p0s6})AMDc;}#~pvj0Y7!r8D z5x=67<2@LD&Bg+p38*b-G2&Ng_*Ns{r8S@}0DnQqIbp=F(HhVWyhmrEwdI1L!^p5! zYeAXGO_!4WSM>X|%V?pUz!EnKde^9eTzY+gXv7jF?;wwS~{!##5lQS5Q|KDd4 z3`0hS@&XmFy!Wf?vT`9vSy1ZmJB0JfAx1g7d_qo`z-#&c!vmtgY-D((N#G-l_)1Ms zv=M*5SJ1~9@egzfyv2z3?5q$4$;N^Q%LP8wh~J$o@EJz@qeTL54Zv%b$kyR;ch#%< zoF`qj1r!M9l5>stsuod!-H88zP2iUp@q6L~-VuP;

ncmj6Se$21eUj12!#ECdxA z@imnKUu4AZH4A*P5&t7G$;oaV-h+yNB!-Q$?V&jl@Q+suL8Uqd;A^!C$^!6b1%0^@ z|AZ!grxE|3Xn>OcD_8t<6+hW51XUUte5C^KHR5T8z*igb4TS<3VeeR&l&~3(TH!<@J&YiQ<|^%H9XJ%VbULKecY@SsQ4z$8C#6_XSBX= zHR2Cw^ldskBI)X#LeUdO{2GsDx^`Uw6gn6p7&-#*{Q}=<#QQ4*{*)1aNOQWb0Q_l{ z-Xotj7Ch?~g1U|PW|P4881c_(ec5ZoA9e`(vqpTg7P$Hp`~o%qM~e^l2!``UhUYcY zT`=Nbu(Rs_BHQw~tKi6g%OHM74*mF{rE~cFA%0mh&w|SjTTk}zziOrYv26Jlixg4( z{#M5SBbz5GBCOK)GBi@=|9{H;rFN9!05Gl__a6C&kobixe#IdJ{MeWJUhxP+c{fRz zR;@jXw>bHqP>RMMJ;7hWJoP$Q)cY1Ey77vNk6At-w{77};f1h9R{jm{ zEdR$xWO-ouUp^v7193m?CA$;(5q2ER8j^>RZQFWxx%yF!zsI;GNpfD~~R z+!=^_^z2yNm9qq&ZWyt){~YGAfWn<5Bx0bP?Pskr|f1&U9=yV?& z`aM~erM))FU+}b{qm{oagBozh{q9EG&B437f5o4z2`c4fIH!+cJNw9-L?1j5A2}TP z%4o8}BHi5#(<(!Xb@h>y&;kCKlLpTi>?89-ts{@NPjO4HhZTQHmd)f1!jLH~*La0D zhhdCF*!n^whkX11XjDJx{*=s;i@(|b8A+I{aL(y0ZF62JR$fxlB=7-^ccm2x{B;3H zh#8Sk_!-Fz=U*D&e}LTVymI>r=KG8+4&@glaZ#gmkevku3>5YUvUVx<`<0Dne(h!C z)&Zx?vTRk1ruS;p8N#(z>wM+Ckvg0IK%Ufh6(1*S&XbZ!_n5}^)IBD4`aF3Ee+W+b zLtkISbN_@W?H(z=PQ|a`NANZ{udIKCm3=~D&o=t z8)TL4@#Tr`6J3B$s=TZVPk$tti(V7A%($bQvjKe^w}W{#l`H z`7`;kHo*`3@2aCyy);KctRm07#=h+aTEE6Y&h*|~Mh&g4n#)&DUBasS^) z6pQ^E2_t)1Ta7%0-TXI05Jx%Q%i;dG=LN~U=a)PjCaq_EeEEm@+v&Z_{AzdK4L{bv9&$jRZ-2hg+qJY2lh>K`@-jl2l`zh=DCy7;Rc$;-x zBC&~n2sVrr)rtKNf?IXs!Am4P%`cJ%FBhteb>S*pKdZDoI#2UB-@nM@sRz}!R;4Fw z9M7{N9s?Zm!O^i%^EH_>jgQ!1{)GVir6lcMXCoJxW!Y}WubK$p#k0HNwvN9W(u3~5 zlLc25P%8wuM+D#{HGIs1)&3(eE11Nl4w7mAR!XH_%E3{uSJGFL58RsX4#s=b*mr0DUrxz9|zdSa3`^#mdLDSSUX~q@9 z2vp)j{S~qi!DQeHoc;snl^HS2JdFDAxr$-3Ik2yOIt*bSaAEfa6z#i1-V%Jsc_r;* zmA`G@fINo%ej;xV;A2;aY{;=>Sw9abD&7=iAW?At3^Ir+H_68WB`*w?Bk?+>l0ZHj zERV@sKMR((EZ;X0*p=-s9@wX)@hi;yk0_$k^f`bwlk?Hsg_svzwMiODvYGE1na><; z*T{yvdYqh2ma0npV4P8jad=Pk&*S8hx@ujy(^OkyvG$f3}rJ;bO9 z&w*Bk$`1zG>9bJzP$2Hv@vz-?Rqap48`Yj5AJk@&dq@17^-bW--ybH=zQywC*!Cpy zuLqf7)QE34>b>P?kGjrsBP8OMJgW>-jM@}7wkfm2p<<+};=SSWy1*uU7A_ac312eX zwel*ZENG7R-V@AstxRE@c*z6T%R6=L1MYF%qcR|#zQMqz;ATaP#r-@& zj*usRs@m;JgrRF)k#N@CYSFWifkjbl!X$$lN*&KkGStx)1$E~{8R&qcGq3VDzwR^q z%`a~PZlvo*(yC?jOIJ}GzJ?a|NEasfka0#=< zhc~^#SX`VuZwfWzrI2VXKXMK~b{R=Ube@H;i_5cFc^nMyU`=uI^r@q-ynr3ImiRj# zRUhlB3S;Nv46)S}FHe=Jnbnrdv)Hro#(s-Oe@vJn?_8&*{_gVPQBi?!-j(B?lXfYm zyc#Wu&a-16;@v&NTX*vJpMnfE_34=+r;lDN2O7cgDq`Gd=(civRv|&2#Z@gzkQc-& z=?v^>$GR%jIebs>sAe>SwI#?=@qBwn|Kgl#TJhRI{ksYDzLz3deq7u&pS#%=0qMa>Cl#}KT^3KHgj^O)Y zIol*N|7)vt_-m+~F*{d2*t_KPh^ zo-uXK$cOY)73zHWE?JlJ%B1y5HSQf5tU5`~$A<(?C5^4^mSp+0zyUI4+8EsUd&R;Qf27G?oiOhV`K0dAo^FV$c*OLaF8g&PO2m6H!w~Tl z+`^f1h>VrLW2SsOkbd$kd21+9J3iqXbSno_vS;qnz=;XP z!F&noy=G)FAaeN~EhtkL0|S{d&*clOVL4UL>9%d(?$ zqy~SM%$FM>v&YoHo|!GLn|o$NDwc0`DRoy{#PD?zm)bWXRrz|x`Z@9xh_0A3R`j*m z^46tTjmK35ig#W~tps^}^sDoD(HdkIK-*09Ga}7~d>DT|7izGu# zmgDCcBCAq<|2%mqc4yn?$xg9E60u1v26<@&bH) zt!Dn%%E#N}nW43MPQHg%Vw1;iXxgC03AQ8$vV6>&BPab^ZO)F!?7I=#+>5O3fcJu|K2JORE`KM%abRwdvle%wIUa- zXl5sJ4Jugjnp~f!FTK8(aH~g{U2$(OLNKYu{-esl9KZBFvPJigTGg-%zpA z&p*#c+lxmTxY-JMh*8ZJXE&(fx67GOuG@~O#775KX0z_!joq2KW05>&7y6S$^5MYv z@$60V6Zn*bb+I8{_!rApU)wiBD~zk1eAFuOVE+>N0cf*x$=DG!e(Bii{_85Z`kM`O z_GR)Lfm%*>7)TVWed3T;tP`*Gkuc}w_rk@VA>Vw#_?Kq7gMQKJkZ<|gdAaK=U0Dfq zzsi2;&h|WQ7=gp|GOZv`0OnX5XCB>Xxn*>t#d3>`tx+|D zBSn~!t(f$)>oED(Ve)rg{`K41*P?-o1}+Y`-K^pkS!_L8r3aHW>8$q_WeqTRi@Zr& zCq39OQd;U%N^6{Bdt*w0tZlRyqevlIgDCR-5H3ZDBBcdN^J@zXQo2`Q%1T-xH|shF zxCDGR12gkmI;hSj?2 z9BT}8-nE7$Q+FY6N};hSg=5|B#dY#(yc9aLPTrsy`}>8XX1Y~1)2*^|`Sy`XL#*^+ z)WFq!=eHd`!1y&>Dt8}dxlLI+I`E_X*>bf@;)>+h&~G4A4GB%0$j46IhCK4{cc@y- z6c3F?B?#mp&R!&^$NO;hb47fJL!)$!G}CuG!qI|@BXzeH$*U6ioC~LU$dALwR2*Vv zXnw67VzRjPiYw->H#m{fIsd(0j-Na74P`BRgv3CLY04PXp3D~Bu7q;O?E$(APkzXt zJ8$KQ+YJ#^QDWr=1af>(a09|}JnP;d$EsbNWqD{&X)8YxP?Cn3#mY=xPz;L^R*oO_ zlq<_pVaCXh*4F3zt$UmqnRyE3kZngtEY?|Uuz`Ie1cj^H(m&W}nD$YGv~Z)m1T%&8 z4rrFg%I?rKvq}}ClLS8U6f;4d`|(I69d|%#7VE>$h>+hk$c< zGO9Xr?VWN;_#$Lj_#is|0{i7JvRLPx^0pks$Fl6ZcEZQ3Xo<24#xlzcPWax#eEAwC z;ArlDBfeL1mokQG@4^&WhtGz{kx}??R^5y`Rix$NWTkS^!#vq6*o+n}XXW_OtoQH| z)M*Xq++?+Lvvi%i**R?SJmeJ0CxG_)IpO&vi-_JU%NaUN-16v@hnZqsd@xoT`WQd=I7?WdC1T*8^8YmHlV#%tJuX1A;u2;HjvnC=bPaDi#`*t(IuC zQBmP$8*P|mR;DCR!?L6m1iiFNNl7jhtybv6qNJi-E7qtqsjzUPUu;WB>_j}x_^~$u{#5A#vWWjAvn2Ik{iK{{FQK(nO=81(k z<>j3pb#Xfe$}E|6ySO@{2BAt)S?osMbJO-61BUIEH5LV9mqDm#gA#ZC%$~7nTDPAy$8f7#q@Gssp9a-mrWzCbUo`npFuF zJS%^tzCPa)?8ry)q;uNBRt(VQSt0J25q@s0?tgsaP*0(}gREOFwTn=t>`HMJX5xJA z`Adonxl_cAP~mfUZhy>UKh7C{k0qm0ApK4_D^KR#i7`?Aot6h2dy!*jel;+hQdlha z-YIVQ^|U%^rMM3J62&XU|QxwO{hlA$gxkUT*YI#V5Q&Mkti+@=e;#D z<}PuaJi7`Xd)j!HxIdt_(p4B$My(n!v4e)sbM3g>s(ZoRR^54bpI5j1?rsqQxz*=a z_vC7kEgp!Ky#smPdj`z<*04Y3Ua>?Ltr3Ie`g_GZw6f`5F&(E<_ljRXb&wnH6C=Q? zx=-94&;(1()HO)>TevT}Ht{$0N6rd~2uj^3J`MR$1TDo$*=j-~$i}_%E6uxM6e%KCg5O@9B zu{Ui*-5y!9@qF?551zlyJNzKTvt?8<=E|dLq(3){uU~ETjes$}vlt`Zg8dZ&$DdE;Jf@?a^aaLUQ-qZZtb}q!FG*D7CwSo z@2V5B0WiSjdmj}s;V1gbSA8PWA01F05;wrXVO!3R$8%c-#KTpBo{Np~k^viZpvFk! zu>ni%y^lfC+OPZdc2=n4F0>V+GIrnU{wQvVUfn;(7p=iujE&wReA#rX81Sx?dieD2 z*7I)>|5%EJVyUbw60C7T*Y6(4v@((GU*r^7iyUhqURY-Bv{sf0{^k*W zoiz&9nQqOSC|k?K%nR;U!#M&+ymlp-)u?W! zxV&MOXfCF|fbTWoZ6jJBME|0$VjD*Fw`JqD^TIBdmWvhm-c3`vh#ALsmzv9(ThAPL zT+OZO-3c~VT}RpPNkmo*%-!u`c<9;3A!j=s(VHk=s1R!dlEl4z;GNNS5fc((m%vUR zc3o%5D|ZYi(YnJ*1?-;=cy5Zib z^uLdss~c~}jgosF7k&7~W!;~|djX=}KZ~6Ry{k%Pv)Ys2uuRir(+~I%rt154e^m8_ zo}PHF^{{G^jC(>%L?>PF#QCOu^TdF=O3U1*pB&H&EZWqa81y!1a`n!Ey+$=Jvif}a zC#wf;`YO3;PYrx}r)WPsH9)T5=>c2J?p@+~u_0EL>>5yObq#vp?3-?#Te#&qXyNS8Ic#q{VS3(q_|@7y|vZ--^~?!bY^m-!cw zEHM3epS4z)N1nwbiRI^W7;JiF<#QIX`#Fo4^}M)Ne_%(Qm>@rTUL@&H?XSZesxJX$ zyuAJeF$JG|cmEaKJuiTJg?(-O71vYL^)CGhS+GaM%OC$L+<3jfy1rx&*bk`d#d?ab zX^&Wl-)xs*FNyn#3V-zqAG-TSo7Vr~9MCrOf3{OUaRz^EuNH`kUa6AJ`dYYtLVYd# z6w~`ZU#_3{2k7lgKNKL3kA)XmAAM4a`afT;pLhZV>bO8pK!Hu5SJ?E8W&nK!=!Hyo z2GFkoU9EdnqmlyXb3tEZ(HCnQ(*qdez)0l+SpoEL(ABMqQk2(E@A%Ka(G{QnY<*e0 zg`af6hsnx^{v2VZxXS8eYds#KrODKNBH59n{9T@T0N;PxCx&7~tKBCiJ5n)Z)|MCK zM#=7dVlZw+diP<@#Iwu>F+$GRFLp{aA+QNT<(|O!JAE z(K$uW3N>b{r+l^7Ok}~$K8&h8a<30_b*k+4iOExP9+=320UE8JPeB4#55{K+G<^0l zmrTzc52neC2GnF3a~~?~YOqGT-~%Ayn{uy+SebbMM%T-n12CmddT|$vBhP{GWroW{6ITx!#`Afl z4#?4?>dkTP9GePv90b>zAnZPSvv!*R{XAQEceGXOb4!>yq;rcAk2LaZF=paBmjzpl2j_N>F{l$q7mjWmJvdI` z=p!T8#1V#raG-_3E*yzC+&EHkczo^><1-U0)UYz+4e^#H-!DT2dGchL5ht2S4&4S= zDU-JWzHQ3I3TDZb>U2tORVTOHqnIDX%JIX3Lf8 zlqI*SQ-|E6m?xd`eFanHNp;GULn}Z%9V?S70Gs4ubxM*e)u~%;o;O>_MHoB-4m?t~hfB|qJ1JRDL958sA*V=u=+?A|4`k`5#?$hfJq9^?6-}PqWBfPHDtyEvt9*v@VpMY^8#a(__GFm7N7F`^D07aW$wxsKQ~GU1n?_r< z9wjgn<&Qoi(~(Bv;|%%zVIxM~++ZXH9YekC4YI7k*yq1qbin8-YKI;>LdU9AeO82y zl_;teq0jQwr4^yJbR}R#=-4TBX+`Lm6<8}mpD0J$5o*&SG|lk9(2F81=c`u0&@mIV zVQ6$1jwmp1kgvXiL~0<_<+5KjTKIBeqfs2x0gJn$WX=&IPJYm6ID?KmNqn9yPc|Cw z`LCN^GghL&9yFG!oc_8)E^abDv@d<* z-!Po|b?qo_d+)?@rOEQ%i8MvEy!Tb3y0pC4QL6+j?{&D;rRBXZlm&VXmizDsI^mF`o=XIqx7IIBv)6G@3Oaz4Z6caqPtbL{LOeh=p_8pgP-d9 zyYT^E`))gAT%?DjlK4IoBTKc)){Eu0?-*Mh7R|TeUE?xcB{s&dmiO$|osRYGYB6GU zM+J)2$mV|-O(Ce|R5}9ovr4H^vi7i%?pOd?ru_D>aWeEYEIQkyX_N0z4u3O(uE)## zaOEUU0VM7IZO#y{^~7OmzKYJ)77Pw zZT~A&vX;MkOJ&lB#{Lk@Kw6TfB|jpoju?mKfsc&A4vQw=`N((|31Vp7vhs*=oG;fN zHNFYKETq?JTFOtB=`#5kGQ^_E^kc?1j#@HAwJ*8N@aTM<{|SQNs3ybo`bs`kbo=_9 z&x}w##EYpJseNHm;K+($e3-2H!nn*~vE|7xjN5`7Q4npFIUPoFkUgWzvJNA|kqdIQ z{ItWE?r2dGzQ_~CX&0tD-(X?>L*RD{J~7Q!fqB69{2=o^p;!9u3pL*r7}0%~gqzpv zdY13WA?6GNcSmc6n;u_Sr1`S0yM0Z=%-`v1?!(1w-%Zh`Qx`{qeURC!V^H!DW%=N(u z=B;{2mN8bp$yn>Ekws(8IN!pF=2U?s@U5F{#+mrJog2^Q z_<#!Gg4uvAzS`O5)}4BV^n7Z@<5fN*2;Y+b)EtYS8!&=$WwE;SYR1D}S*I=`uo+*7 z|I{24v?WY4ux{zIuA^`rB@;eV^jKWS%0-_++=WY*T&*rWxWu5XBuaucqa;{1vqYIo zcC$p8M@F?nxIr_@>Sda`Ot&tVtIN&SWl6g^0k;74>hzgxSEo7BJdRVDOj4&SWR^N* z%R+T(ku~ZxUACxGkvw(W950Rym9d|zbG*#>9OOeXU!4+V#pmW{_@n{;8F&V!&IC88 zGjO;%W6|bSXW${~OmGO}p%%tsjxk1f@XakqaZ4V3(88DuEsQbD!Y*}&zG3Q2=I^#( zF0cqzK{DYl8%L`%nPo0@MuFk#4D6SWVcek3;5%(xp3ZoLjdyI8nH}cgF}9%?dz7K0 z{e}W7Lz97HW%>y-KB5#5OImFt&dbQ$Q6URXm{-l;VzrNp*ORfuG80(&;Z0Q>XAM}j z^RVij3JXocU7v<^ZIElnKh2v*dm&R}Q}s!t4Dza-J`I%VOhg*h7|-~V z%w>7X*b9Cx_`%FSPTF@JzjM`b-CD?ktEW+I-XM36Qr!5B_8lMT1zk$!SJjN?9y`logA+GRV5}r25*D=2w`ZfpX zdz$p)rNAZ3-%nQyeNh16n7tg?~DB%-Ro_Z#>lJfaM&hJnZ(he`U_L+}*Y3LSBz zXZ6XwUn737TZAz=m-G=yvIAs2e@7So@0Bi`mQ+JkC(Hbgw6pan!_EqdAnoTI@RMwQ z5SFWXO~46^=c<)Tzq5lbCH>4HSqinwTp)L|*lH3JSEE=TWBlbpV2pyck@lo=NjCF+ zr1d4CyuaFyNV~oPd=K-#CGG7rN&hnAQ@SA5WRnLal6GwYFk3%pCW=`9z{kHq3rYLN zl({Ij3}XHO4BA9yxf_@>ZqQZ|6Wu#_wVLXi?=Ee@0rV zTXy`*jGI5-L^+pD4;~NtDag4QK zS_9l{%ZFTqG)9*B^@S`Xz34WirOs{&$wGyl0Rq1#F}oO;4GMXX^e)vgx!;78!R-_v z|0HRMyQUX#g$GE#U+Lqd2ziI}ui$3EZVzdrVIq7l2=!dxU!)yNl-2(>^rx)EZD#6xf}iu;xr6a5eKk zB(2>G+`#yI(sFWSZZ}5DMPZ<;I|z&(gQt?Xpi`DYeM&Qo$YAmfB;3kKzgPij`_MK$ zm-$;r|D;WJpqO60<6CoT(Ka-}Z_MC6(synKX4i({ca=w@Qa#@;9X1BBQ%&R{Eo?lA z4Yk0f%%26mH-LXRi5jK9g!$Ja;|qZ~G+~7_OgmZ$0=G8o32b_3vgunhIU=}xU?&?<-jzQxQ0_H&^ z{BejS%2J3GDO0={4KZ_uqxW5kG%+}D!@Eddn+9KS-iLor`U*GXleo4s7(G3}?292W zC?5>W<%f(W{n{L4S%)n@1UFop4uN06_%hOpjzfW-zy#curk6sXmhr8m{Yzo4a7Yp6 zj}l-H^B*Q{dn_;qZwP!@-v*q{{Cd)4CU7_7_t0_sfc*x1LAU5H<%3Yo0^eZ$f)-md zTo{&uJ)|dB11_-nLj~!lp+HY#j17t#3c=4}Jds3!s-Tx~8fnj$Na`^!o|g@B6O$`R zn_Uge?Ha0jh%$qF$k5kFv*tZF%YTCYj(GJTg*)Zm9y2+m7{ZA*moBBCJt_>7>@ZR$ zv^o2=(T$|7sFUYai0Du0$Q07&sf?;;{$kSpoQN*N85nst z>5-mN7{D13S%P2`?D)})E6NE*IY0SJ3i2I6xh^|qk=Qp%>xG@MtoJ18-$So|hKTG# zaFks<3=JEMKVU?n`3JdSaim?>0sbPp{ICi3?wr>4~!t&3Nwk910Wio!7bhjIPKWyR^=%KG!J5GhXi9()Kb!JHlcOSFegn$un;A=t$KBv_-bDG(4%IHs@~A^3<`+Ufisg@DjXOdWXko%} z*rf`vW1>!w5D2*65mCKl?(}H5)3FVX9)bkO?w5}qJuouS7&4|f!Ed+qN2g&3Sp>Y7 z@l_;O{Kt%3hV1ZnhUoiA+^C$(zKTZYn2JXFgBgwB+*1I|!HIqrHY5SF&CzuOm&wuf z7(tffbwG>|sK)TvNfUhxzRv6qNi;U4Z*PYh51aM_Y46l%T4)*S)eX-l{de%BBaGEyw68$*{!k6yPNI5^ zY(=@E@-Ws1&kcWrw2PyFxsMOW4PIF~^HqJ=IQ#b zLgG=?01jUarh~;^;N@Jt7V<7&4p+>680-$f|HvZk<4h2^^TwPav91dEl&!!SMB3-* zTmDhb>4FCkIMKv>Y_;DC4;Wmb6RUgd9;El8uYv4eMmpD#$n1g|P9x`5(w8Rm=Y#Xl zq<_{1zL(X$06SBG*>2|>r04Vi=P)1lB6*#_94O~^r1dlcciQ|Bm`z1lgX~atctNkQ z6Jx}cxT9CiaoJsJ#7(6AK?R)ia0G567c`(b>8xiR=|^FwKY$~ON#9Zov;6$W{P)FV z-35;L6KR!ca8Eave;(_SIm!{-un{kj9#sm3O)Stx+7$}>6?T*M+Y(@ZLw-R5)B-C< zAOlB+pns}>vm-`gQMs)fm@{xBc4fAm0v3>WLOzWyF}EEF5cmy=2cghESdPp=%6rh_ z0^6XGw_x~J6|zT0t|I+688ZF9=y^$KM>UH*joaa1Spf39T-L74`-${phk&_*j2a4a zpxqzGQIknK-2}|qM_rEQRmfJT!SD0+;FO{o!%-hqM#9VmZm=sI^(<*k6)=Lk*QmEi zd!tpR{|`ZKfEagzQU66Ux%!L6hN-P;;6^(MVn>pQsfL<-TWu_!bv)4=Adhv$=~%e5 zhUGIz>|PB%H!2pLG+HGe*B5&i>Edvg>`-=OgU;)d*u7X-D7`#E#J)q?oxP|xmFxYO z^!JfW{v~(pzcA5xfg72Bn#Axmz+BG-gWycX=U87biS#QMAe3awUjVx!k2_I8x^4Ic zms8MAk1Xv&>`;%$X1S-KCrvhioKg*Sg-m`AOX6VQV#XgKHif{PdKY{}+VTQPXVA(l zjIGsnC2`2eNsresHYiPKFiNQ?dUE3AjhFgmNf+6Ye|fOw{@Pi z;_^uU8Tt&)__%ds7L`?V0pwiVL+HbcfZ45a7~C&+12^04i2DnP8MVMQj2m#D)`JQX z7{7}ifpGiBra0U$C8@CZ8-RP2whG8+*z)l@(rGnb8veyhE>aCmV){H1cOL>~!{e8b zFpl)=jlZ4rD-xi`#qyg-*W1D8z7YRB1-;q`%=b|7FOzp~LAVf_wu&46@@4v2-07*2h@0&y zB@vEdez&=vg7z@!J&SwkZ6N#O=lY2BRFB+?;zg;bghA@MmL}dCBQtsjDDZQ&Tilmmd7K)yG{jQ?D3T?VtIIKj6wRxuI){Z_$8}B z>V=jT^DvnKzZy^0g!~<&~Yx7|Y=9s2pw4D3h803LiB;C3rzNi(& z{9)hPR&HpV-frWu;plf^;B&QOV`02%3^#UcGWI;ufooVE zX;7l%wFsd$Fvl*&^0c`Um^iztQSg!FtkvtNhT#xRmr#6|vQf zt6)GHFpnQ&v2-l!W$Z+Sjfh5Y06whAD}^I+90cMPk$D&f1$ySUX zNBZ1mS)kOe0eKM@!}IE;su=gK@%NK{NgJwhF@Fc?Q&em@fyeJAG5!#6AM@W(Z|BGk zs4eOR*|tC=vL#M8OJ+%G0vjp^YOFvoVn z@3AC-e*dgI0ZY8yCE&BuCp?N-^>GiPmcR=C1Shx3QimgcbPwcKGx->as{?Xo!YR_1 z6w6kJBQA2E6WyQdPn<^LFX=mi*jc@H>BJRS2e$Utnz(_4x-szYq9>M<97LMcx35O=@NCXUALYBzALU3wy3eY?5=r85{Wz%6?P_&JPk zMno%Rk1`T1_is^5d;*Jm6vGH%jbsxso|dNpb7&^MM|!?eQ^ovaq+bud-^hvogy#;) z+(9t1UU~;PlIL|my1i53PzG2_En3@gm;N&1pRqZ9J%mn5uyqH@9KA!bSpX_0FSA;4joG6lOa4YD-^ z-9g2=(bhX954&g`C|Ac=*|kPeDBP~1!)~4OGKtO5=C6Lr+ob)l8NH>Qi+zn7+F08 zidpXdQt)%R0A{%ny}+4_|Abw6@cnV0T1T4tQyKrNZ|dLB&L&_^$EhEaSg+#2c`>z% zinRQ05UOp3$pV|7)xg<|T~N>g%nFjnV-i>noW=Y!(sDat5cmA#D{w1Z1%3kaugBJU z4saslRnUh9`V%pEZH9UY2MRiD7bT+~{at0iUM^5g`ll)+oT#uD(mRYhA&F9WI{Xtl>Ab?**dOMoych_`Ga-XT@)!1fDZ^hHjh$vLBhzUPp zNs$17dK;%A2g|yEYZ)hycCr)|vO%erVB0Yhd>(UCmyo7jLJ^#Uskf3o3I-X(}L{xECfH%#utX5i!_7Z$v76Xy9+pv@eI_V)X)#jLg!FOR9>C2obu!a@jC6Y=Q;D1ta;eOJ;1K;oM3*SIdMet)}X-b?yxU`KQ@ANOC6 zE(hPmxD@;Lk&7Tv!GxzV=WIrna2n3UvVU(H_}R>VpR^Svzl?b7o&k`ZX?S=l(StH<148IkH`Sv=ykUXq%900D259k6SOYDEi|>&Bj{2V$j+f5Ds7 z*}y#N&Iu!ZHSBg|+Wa{aNYCg6zn<}>q-AX&2@DJolap_YU@c@B8!k z{-N`_uk(7n&g;C+>%7kE{J59zy%7Gp=J1+$Qd*mE%e9XTN4q{}TSkv&E$T zlcXz}B{{g>$j%1~uO3{udC3mjh5}ey<30`sPk>M4;d1NHF-xBOSH&jrWdw8$+H?ut} zels@aJuu{OG)ieJ#t!eL$ycTl59>4qh0)S0uesup(ac*P8KuovKIAQ^2$91orUjK( zrouFmCCDyKQE?$b&)wiN*;L#Wg%v?T<-Vt+P^YPC3$zav_%a3ip>Z{}u_PTghK%9UrR zzihrd#aDMAiN2i}MjB~3fKEJ~tj`>f?L+ttE@y_LC=O~|%uI2sxjPb}0PwmhUL z#|EJ(4aL+G7PXWp6z+y%*Vx728n5KeM61x%Zr9kqHu~`FGj&Y?+2P>+87>{btq5$dX!38)~LSMb0AlxYstLJO?koHdz} zB-O-9d5U=V)IsG}X~z9W)MiLdV>2EIQJYb*k%mUPQtVLFIictS&x%GAaY;)$r)7W| zvF(91)E60%@_uHl*Aw zM!A9!XCGKc2cs5koZMQ(p@+`YcEHKM;+1(H`hB^#=}+a}_x@I1aRro<9{Aw0HV}NY zEIMlG5m;j8WL4+xS5@7|!>y_+nY&BXbl8(=<5aJU1oh5HAe zK`=-xFw*Nf!<7l6@Sq=DF%?FhTH7XYs-L^3IZ!K?7nJUv#=ThcZ>@tsw@PuK{Bc6t zv_hRgrPt7tia@(GtyrT2T$1Ck1ZCbat&DcWBzaXwbx-s0>g>uOx%V-+yA`f!(+v{D zxO*?6S@^wg4V+y}meZQGVZv*-`)fNvr^G0AnyA&9tGc4v+|C>uG}tw*rNTsSnHxji zr@pyMyq&JGzka3+Pc?dly2dV~tHbhM-W|zK({~pto`w!o+6oV?O|A83yJ2M0ca?7B zwd%^6z=xFMUd2HI%9<5A(8KfnIkp=t!Ides&0;Fy&C{%oLZz%plqKlP;IS5gS3E#8 z+fRM55s~1OOrq4J=c28=l!skW%0upWeY8I|B1d1U3`kI)QK@T3GSvuY)_&2w*?I|e zTf?Z+7Ek=N%y#|t(}rAQL!S!M%G@tqV}Dtwn)aT3nhfwGBML1JCR+7p*VHqz0!SdvMj=S7ilnmKP69LNj5^|T$wKOjze$!q#m{nj<>zy+JSuf#c2TA=r}X9->PUzR zZ`Eu7JKA|=>9)h=)RU0wJ)^}2Fsrpyi9(3t3q*l%_Iu>@ER_fEs7+OG=Jfhq=*(TjGy^F zlnSYM5{mARf`X^E)0%}#!V@R4OC9gi-h~ThAO37|(J2hR)Ns1+>ILMGqQ2J?$)`*V zqUn2S)7A0eNNqaeFqAAcPf0uk@{)SmcXcXRL(Pj;gg-l3=$l{C?yPvaqj=VgH@Ab; z4AxMLhw&&1t&z zfNw5>IL~LWyTaX~VhTKPO08c^%YzEe44k>v&*{9U{|4G6TZMZO1cxrJeY$P>0JWrl zf9vb0rOPRq*QjlpA)z*2doqtxs6E*=_8VS1=CoT5Aow8KU9lHhC<`>F6up$Y2Y>b+ zP%<)~Dt$&W9ZHWN+o^eR(##mOjw#2rI;dmuoMk&NsN#bAz#gqMpiv6ru-ZWL9vjJ7P!C6#n=F`qarOH#w@b8?Z(?6NA!JaQT2ytjnYf+a~yKT;)J z!9z2bfCm0%CK{M68kp@G>)Vb7W;;#p7gUW*^|WP4Y6__$^;% z$EuUFZ?_fw|LZSNty3uIbdCKg3)MQErkfvD8)H(}BQOVOxr+IMHXpQH#eC3irKV*o zNCtH*i{Fx^$VaN_`?exK3p@par@%G#$;03&aGJJ%pvn*0shJz{r-Qao&|;1F$yU%} zjkx_;+P-Wv*3F^I7reb|EeON`*kUo|H-T1DHS}WuCb4KzE=vKb*g;w$%On* z9tLfhpe+OK2cRtjZ47l}-WbdmOjhPW-_aA9{c+krQs!0>-u+-mRwfx~!K<__>kheg zgxao|x2kpo)o63*NRExCidT-RL-^zM5Nt-Z+;hyXv5$^G3cPabX9|xxag4Bn*T@nw zLc2@n(Yvk+A>GtJKaE;eOj#KUHmhswmya36^n4=3B>G|{t2Su3NK6{q)ZOhG8{2ny znw&{%R(yYH8YCrW`lTzO!~r3V+ZsxJ_>n5DC=Nrd2@;dh2<$;TIz;ocW5YMXe?$3T z1a%q)mE9>ljD=)&>hy#611cx+O4I>RvBKHLei zP|+huNj^9ZGOWzEe4QzcjX{$dXp+X;hH;{Hgvfnq)wN47;M=cVK^~@^wB?P8?9}2|OY&$D{x70_{9i@~9NWCU$vbQ_Rh-yi zb4u2we7N#85>)a#rJmPy&oxfzBW0M2awY89B}+RN90#=_?|YjxI8B@1gBcHUGb&0d z3zt`cFF>_7Qjrt7}5FR0QAzC!n1gsr&R%Ay>p2%l`U1^Q`Py*hr) z=^SO8%Gw z6(2LcVlgoB6n2`zw4CJ7UT0FImXITNnppE7M1-=Ttq9wT*4g3|WB!lXxMy81IfJ#s zax6o-Z_1+QuAe~{T^~x;)9mY`$P3i6ZCwzVS1HQoAgi-%yI%e zr7DCp4Be~xz#HuvJG}?Ix?u2=Q{Y7~SR2B5xLY`-L+hw}{T#XWJhI|=`qcX5rJ~|@ z&jnPRqE>v}y_!XIs-LghkU{M?%!*V@r?e+lGHb!nDHSi}V{!Uf9m`qjdocqRoPG6G zBWhEB!q>05dGsB=e#Jmsb0>ZguU<^Szb$NNm%NZZwjoY_F&*pY0_s0xqW%q6l0CF_ z!)EfVx~9%YMp$sw;@V-6EsqB(tl@fqv%nEU@oNj1cZO+w!4ga>T?Mh^bvjURcOs{@ zfx1-DT^$yh1l8WstO8oGF*c4*fi~AzXNb{oZ*5VVvFaG@*tls%?Jzp6S?MTg)7W%J}v!(OoDYUO1f5v-t$c1ka=q&1sw z^3EF7f(u5vwGv)~O1VcSi@=fW8hev_WU|xbSc8UfJHrFRoR{Id7kp`gFAaR}fG-Vv zIEIo60XxwsD0%!-(D7JqUqp8m&hTPq<5W6OJR0(LBO1QBALNLJxo0@eDZTx<@X35U zV^F;P3B<%1XUed%6SHIouR<65-dqTIh<&;HoKoj@$WjigFfF^W+wrTiaQb#y7+Fef zTb2{6lD2iOGC5#!Eelg8EH!hn<|vb1*svNC-n@vaxKr~$jy&wg>rfS(`r1QvHzd+8 zw=9gQ9bVzbaeXIm2$Yasnpj`1+f1h=&Y>H>^<3my`XkT z^G-~}gBmG6+2Sw`R87!|ZzT@Vf*Uiru`!fqIHIfTpu37XSD2|LeB8 z0OoIvip&QS6lv#)wT|gWM^5;*j>(Cos%c+eDoiWF?TJd&w9BzgaOeUZiqG&uQFvoG zQkk|-K+I#I*lIkH2x1Jb<$HK{WAepWLIocxe1hjZ?cjM9&tc%<6@^gnOF_CGfUaKUGb#Y#}s0 zK=W>n4=)4NMo@8Eh(Pn|QCe|xDyHh@o8JwuhCFU}Ud~lVKeQg9z8w+k%d>1af5ghH zHd*o2UY!LxVL?mf4mCaEG?k@>xviDY2?{-5*_mr?^!$$1A+0&^%B)@3*0j50SGMZP zl@j=C^nSPNm-|!K{c?YbRG^frZa;}OD+N+&+`AX@^{#eQCyVHgKA8<4XyyH>Yxm0+ zX!6cQXm`=h6A+4`in5r1hj4B9hBzcgm_W^EXWm!57tmM7PtoFA;@6`;U*T=w zdh-5-{g3lCJA5@=Idv8LK#I>{UaA)o;TNwEp)2n<=z4rTJ$p+mDWQY6B+Yz5=*DY< zZY^KYgj<*Jw%?jW4pQH(EUN`Iq##}>*#r(bd8Wm6?ErqJfO{czKO}L-=GwurUphkz zN?x2D0Vi%bB`OgE=A{s7xovrPK1z6Fb)(_MQzwdVJ4wRpU1KluUg4h4nc(@&=#|?) zp4r036xs(r9~*OxbtF3CP7v_$Y? z1&UJTK7!z#iP8*F>Z9GeBNFQ;c#}VcA4n(_aaFs?kh}*s88*?I%XX38x&sbszSD&% zsPN7Qh=X?D89O~+VNtS@!=-fe&fTQ+MCo0Bl!@m=!Ja3`;=?1tjLL>K2Cz8WIgHa9 zz5pi)T!0==qwiTPA0D9v_spMtcm!L04#!dDY*^E5gdV(S9;ax%XC5dZ=Mq|0sMCFf z4%{;z6elcu?<1swR^R)Ow_s+<}~Q=`%$0h!Z~d^{B6MZ zK@V&X%<0nM*X!=N%<0n=ECoZ0U>MTjV}KXk9A(nsgMqJ5>7$gx8ntUc(w#Q^C1m-lPvhn{8?Ro)R}2pdHg!74r5KXNhal4tu4S; zYDWX4rRjLnsBK?-ips4dMs=nDX*zGO3#DcITnjD2S5+Mpr>;}Cz)f?GaM_i)TFO-y zM$hkykF@Y0tWMs_%?hy>QD>pH`&ptj$M*P`dOSCgGgS(!si)j^R@!&}A`Gd}2V$oy z)*Q!YJ^(zkZ6>ql)g>c^B877;*Jv%Q+@;!HP$yIW0}+tY{=fqZOAnw zq|%4Aqcm*)Vi`Zq{i}t6l!D8ssCIyrw(egtU2z=YC<%0Ee*_Ma%nvRi7BS-P0C|-+738hn%^?YVdf~R@>-lgR=Ud5DDe)~K(uoFNJOO)0Wgz=V zWh~gO-&=zJ^S^g17xlgSBDpAYA?6f_Gx6Y(V}pb2s!KTGcqmDZG^5>@K$4(DutqW3 z+);zn_i%22erF$s=(_*l@>{)8bi;o|vFi%rx11F85w^bs%q_dmXy!!=1KVjp!7fbN65 z@Zdp1A&q@BhT6PSPQ3e=of8fJfD_psA168yS`%c#afGG*hbU;Ne;ofi4oA?B>!(xq zp$&LyJ*47p$4>3g7h%+af8B?tOs|fysdJ58YIM@`hi^n`(p_rhF`ywmBp_c&lWM@$6-*GampM7ww)>y8&3;^`Gu0@8g4#k(8WZatJBtp z!$K!!$U2zpADPttnwirZknY5{45n9;O5I#P%zdfIHuqUj;R4`r(I7hf+a_uaG`JWewZ{< z@hoT1*L-IcDO^kqt@vS7Vll6u>w;xku@Wm9wfh4xjS8g?lpQ}@Fg0dq{Bh`qx07O8 z)VK(DDXJTn0Pbj9j*&8~z{y8B>^{01`N-L$Nl<9`XewxIPo@$#&42P%z^zY4;Qy{C zdB0F#gRXoi|=ZUaq<`Q}CAkAo65$=R}Lt|2KQ7-lRupBqKV>cYBs)?Jkvngt(-53th znS)JlhS+m#piBW}>$8hEjYKq4!DH-n=-GG!O+3wgF7z^V#m@!Oy>wZ+uIF4UtNDs4 zb7J}`K(Mmt-Cqen@IwR_tPcLPF|eE#KF`nmYM$p#()oM@vC-b=x#gmpQ}MsEId;0e zVjAM28;4c9WAo_VW^T(?rL-@gw2YcxSPdKHEC1KL5Cyp9g$~*Bw62 z>7!rf^c632`c_bC^yXJmW9-UMK!oSvd*#uwb{xXCDn9A9Wz&LJcuNkx!lksm!li_@ zaw*PME``^$m)5lMnz~v!%}^_+DR`CBRK2RvG}E)M{vN}o^|jYzTmOlY6J8m2gvw6N zr&E9QFm$c?(Gqy|=^s6>U_XQpJKArJ%oIrqk3K=^wz$2DR#KdwcC^QnCfeVgO8RK% z8%g2CJjI6ox}AZz^VIpq?KrS%eIpkA)%^w^E2H>7gC_rYH{kjobBlHUco9a=5a2Qz z{bnp$ll|r*SibDd@%jl57^r`qI&h_VU3B2hcygA8cHBx#)YEYv{vYV@euEYjr+9z1 zpGrc_{inDVp>J_5GT-7_Gy!(f?zf;t-P_!ZnQtrTw-xl;9Nn3U_fgV1`B7g%rO)X=JT{s9lE_{p_$d_V2@37oR-UEUAL@AA>_dzbf~@7*Z+@w--NG4w7M z8-1EnJwLr0 z|L6V8h5u`Qc00+YBR_izJ>Af?gcQ=VUE6?9K9fYsX~7wf*d$7*agDvjw@-9S(GI?S z673B7Ilm5pJkwT?iGMz?tw`O0`e^(@>BL4uLKEch4HUjX!x!6F(0A|?VH@PEHm~<^(YF9ft%k2Tw$ckiE40=SgUh|K~~GbZtio ztK~HC5vMeDs}6@{_pEBCG;NO#XBYJiU~`pk&2c`iY#KIK`5|ALGw94lp)5ZF;Yl_Q zWz2p=A--?UeL4@fWKyxQY9{Lp(kfO7_LjG*f6C27Kp<^S_uzBKa^0 zw!0hA%kSQ))wl9V8K=hoIiJ4#{v!H9cOrf9zw+qS@5i4w_x_7Pk#0x~Rbi}C37H|Xep&t3eg{-mr_ zVN=uaZ(=(_^FN#`zq*f>|98^%JlELkufu3gVRR?<6$y;ybOya6_Nz$AWc{S`|Au}C zpkHkI_|cg0M5OMU)Y}F1VsV0B_F+;Cv}@zX4f#SlYzv*34(;-tL7lJDR?rpG?hhlD z>Qf;nPgg2)ZEEVGT2RT*43|KIIuFq39v)SAvgqlF<>S65VhcLE;{{#cr15sDPSEjA z#e-4hy5~QqK4W=YdT|JLywL)iUtv#EIVmJZ_>)MUzi3COUarpovZ%rPg01t%QkQ4mX;pWv6PQABTv&H6B2| zp}i-AsSi?Mwd2Q)R(pvbr@?A(98;x8Bi}6LMzMZ=NnksG(o2cXKsaMpM;lfm~aux(I;?y+L*n=z6Lv@)z*FDF+W;X)yW zHe@!I2_bcp1k?Whr~uVc#QQe}_RDj_7A-<8eoUVKjcWZ5GD||dHg&kE6{4-$>B{_W zb+A6i4-Iq?^4Sp8fnHN@dbyL}RL>m|Zo!=l?%wp7>K3m(rltbg)tRJhA(MNAdUANB z5{sI+OJG#Jww-!DUrv0~@w-^~kz4uS_qz=<3kpMm4r?t$hZWHN-$g7fCAGHx$~`su`*eG~7^#p4TeF5#jS`ZYhM%R?Sj;4&+T32JxLB)*Z?` z{RU_;+*^0j)^{?QCrZYkIX{KlM()jNI?`0f5SC5O2KRn&^B5pH``8(9BL=*(2i#3cjp~jv*TAjUV6yF!t5n-P zlLDDK+ud~RUI>NpUg6iBV2|WZR2&QpED~1jtdG+^{RwDwwmX&x zTG;O8_hGwi+Vk10l^&rEzlojA)y%P(wH+sqsI?!G)Azoun>i1lieY0#YKS*rg(#iwFF&$H0lh$Vm9>tQ^!CAQygDMqyL!d z^^}B|BwnRfonS?@_w$jUTW>wZM*`McEqo;8DLiUVdkRC81#h5?4#;>FGWc4Hcbd&L z_T)v#z*?*2S%d~s5k!Cb$MxQ-k`SWTp$c829CM<{3A%bfC$a<8G*!^UrZ0ycKUbbb zxlWD+c^4^9andy`Sf@zYx5Y>-YkXtlvy#)4RTy9{!PbtA=0hQr;6Gy&vJFhSk_A z0T2EU`_m5k@#m{(_@8d3AAYflT_9vW^=3|`4-d{E!PGw(pA|DnNYHa%PQuCIuks{8 z$zi$rau_bG4knQc)Y2A3bH5DpZq(idLG!{;KK`nSu-snnvv~FLr??=S1ZAE4g6tC? zz%;rP=SU2A;jGW8o-NP1sOceOG*4U`dg2gxb?!Q~;ZrcfU!OZb`@T%wa#Um0Y?JMG zBxH7CtxLW>6$@_2(hS$wk%ht~ zM1s!tK^M5hvvH!2#ZX>=Kz}44M`I#KYx;_M{&MSjOsEZ&kfs~xhu>FB8q~yZ`(ceP z&L-Tc*{EFx{YKF1g4W@qpofPwt)aG|MeB<|!Go19;vVL~3L)qjEtuL>1E(H@L<|M? zk!X*I`iJAx_B?}^>3$ne-yTYijKOVI<=E93^z_$Ux+<}0Y2Ag0+dQtZk$v2FIX1qR zTUS)MPBK-d;2RTez6)IEu6z{FyYW8xPRU$bDTS*0qNy$3D!igA{Dj}FaE%@2VM#X+ z9O7XKQSa%`X~l)rJpH`jLJAT3*ZB0ElhfF=5=fY7Y<&H8Rn@11k&&*BLE3zXhb`TR zraWw+`;I+JJO3J$-V6#Jdvyk++(Ti&V|S`u5|j@EYNr-uH1uz=G2P(e6P>OjDWB({ z`>`16{M+1X(%=pCUxGw81ZWmWFa-Rcs)FTC{>z7*QUM|z&I_;!|F=b6#MgQriufSl zei!W;tK*>vW5B;pa2IhdyTlr^Y})4v)Wb#K27wdP&NzUgXQH#y}QG1NCadp^^cM7g4jm^`$XqU+pjh>$R^Q=YDDy6`g}*VsRf z^0%AisHqjk*KY$p*z|93)&HZKJEVdGVkG0onK@M!{+DM3N*h^aGK^K6E4O3S?OF?-bsiTS1Nq|!=uV? zC(EB`(}Axq-=>;R&?ZafEY)!>uCd1x(I||k#`l#~;@fF6Y0p0#VHLJ%utF7Uii=Dh!6+=xrE1w zf2O7!b&dTV?+i;*`MX_wCl>EnZP!#p`XR#L!m~8&U*DD&j-06Y*CH}q>>Ns<{`ei* zKDs1jjgAL{m&^I&GIFlYpkWtpkJI?8_}j{m@vM$J5APbCqm36Ah1cq^LXWG9X#d3> zDHn7lqPE@;9ZsY6(9%nH;|R0&(h_~0k`Hx+>*!JEN9$?Y*wwQ?9=`xN(fF$esAudU z99RsGB@x@9XcA8{4mn8t(nP$$M_glnuGBdd(jqC;9mEaVqu)7BhA$u`T~vCgfy9R& z(^amO96Z!bR;Pc0oK4xn-fxrUZ%G-u#C=E|NVQG{R(ieGnWN-xi~KB);LWWLR!&F~ z>0y3CHjqpnJC+j%|C!HjkV)e7cX*9(`~uh5z+=oOlg%WD^~mIAdC@S-2qLc#6FVQI zFh!fl3R2FBOpx2g4w}f%R$8GIwy&+3{K!tbWLb;%&ACSDwx=e_`S*gLVdXAKsyv{U z;|mW3tUj2m#f6o=U=m9T*k~~M5KrA#kl*5|bqcu$KLs;L8Y?i9U!x=~gya(I$+8e~ zH$E`NDdw?D)5%7aY`KPdafXIcIB#O#yOLC@>9XU{a_@kLGePXYs@nqKv0qJ{dd<* zR8IJU0gjel!jfl^MdNONenO{!`pai2_4{X$DB}$W)}@vV%_0%u1O_{lOEBQx@#6}!gDQ+QoqQKvC%v9neyc?xn1B4Gp0sRT6xMMWg}A=$~SbI79c zz=*w$A1AUxphyO*nu7+t&syeSJY}%{IdGgJ78*sikqTBCMRt%d))fU38ykuOLk+V< zlL)ULmGGGzi?+p0S-Dc?`1>kB<;r&<)RTg}dh_U-vbQp@DXy`5l}QRgAaA0{_q?Yw zua5t!y)y#^u&!uwA8BEZ81fS7WrH!~ZK&QdS5duZE?L9}!XACpw+Q_M+hU(+i~8jg z-WKMFB@roH#L>W-U1Q%-Mj=#Jwki~IAIN!^>{%E{&RSy0_Yq4nY~)c=%sOoZ$G$8r z4vIFjLjF_3D&i39PP4W+@?Ck!085((nrPNA55fHu>zIeGLJ*w?hm2!*TL9NZSXDe> zXm50a(%!rTwY|H))Y)f!rD^EyD(|_0`iboJ?Bv}Rwem{-!D^fhAVAL^T#ib z1?|FxR3gU6(h?p1F^rvI)%TW8T-goUbuaLJ0$;DA->$oYyR=zXum%h*f}ul)#|sPE z72KuWI(#_r6@vc!1ithl@FfC2qQid%e7=HTA}tHm6}*pv43%M-O^4?@WYGej!P3$& zX_YZ&8q8SEFX3*uBaE$GNYY~34`6M{(}p9dSPCKE#YyosIY=3@QkJ!|nuXwNXD!Ol z$*zSYDF_+Fke%GKuo1%b+i(5q^Z1?IF8-|Mzja-#CY{8u>(!xpaXZPRJfm)GRC89$vRpn`)e&tdtCF^lK1#>E)84Xkr99IS`+ zFUCM9Wu;3ne*GuAm*9z1oh(|4qz4&I%QDcOJ=whs$p^_`B7RZ?+ zEG>t)f=hTN$2tizaGs2#q8x;r5;l-Sa7*-L{6V`+IP^RRXZTv#FTj|$4mOTv_3MZWDN_GBB#Y4ddYGY_^Q$XTV@+J z)hLfRg}h;Qo-<`L%T_RjG5c1MG#v?k5I@Z2_OP;&t79HwH}4;hhj&!ZLSaVdG=v zJ2A2vS>7#(EG6vpEf`rn%yKK7rIaP#il=tA_g3N!@2QZcWK`x#p4vFcoTCf`$1pRO zAXxZLmY0y#L6FjMJ7l)A(c8%l^H=sORG+UL=F2pEcI%9;gT(S>m5F&uk+deW&|Tzt z)Y!I*`~_+>-J!(N;X9D%V271ZCMBb_PfwuepYxFn(Sx(yWn=M ztnV)9(ZSO0#S-2z57{KI9$=n(;5E&x@g5l4%%b;_SH^j`MX($MPv2h54#_9&_ab>B&XYsmB_9(~ zbF!qu&pFg}}G!@V5b9BJihm`1Qc6In4%X zO|PzCDGEeRvu03-j{!bf6?EOi3kyLJ!PpFwfEVe;;WF00A0C1EtW!>B3vKcg*7qPr zjE4= zZDZwLvUX$7|ErY!CEJwmt?r-iDt88j?}wZzb~mJ3eB_Bu*Ny)FBR8yeT<)lkte$BS zW1&DnOK&!^f=Y}O#E2@AGHsWu9OnvCvUUUCS4pD0{H-V5^h-@#D})>6UGCacYW8CV zQW#>oR?$|OqHFhZ7`vomN3_-hTPhJBHvd1iYO2oYzV9hUZ}=X0G=zHt;yJWTdx)$E z$J;UzL5@<&mr*%3^wNQjXjbtM*^pAk+lws;rDKVH$zqY&kOm@El#K#`!#18Y63@nNz#1y{lYhq_b6>4&;| zZ4I;j2PTas=HNdqtd#$Bup0hznzi$vv#g*0^f2@H@q=mP`((bn_B<<6eutP(`5k4= zyx7dT`HzK-@E^PpR*m>$WBJuc%Oo~b4Wl+P^8qy;qst@B=-yB-Zn`)c@eK@{V$k54 z5Nc+g10XiD>I38f)nS&&=z;7<$c{vVneEbqnE{_w46$on7wddPsb=^Q7*%z!L^zoK z1uxaX)a8SCi8Lr=CiDD_M4zmFl)M@IZ`IVtN^42(ze9R`7&}`_*8W?h`7E=J-1u*i z9z5ArhwL{|jZSLjI;?pcPF#jG7zx=hv(=N?v+G7MOu{&RV(8S3P9{Q!$@z)Ix$Vb<7y48_J0jwnlk7XIU58BgG+o^|k_c9!=;r2DS(?CcNmXl8|t zm^hPJQzItuVwQarNl-a!Jc^mSm06zTD->4!Byw>N8+wwYW=Ysq3LW2?kb<^Ay`Xek z{s_hzKY*N)x%*_>_;;h_zh|LbjbVjPkxf_Ejfla=hpVP^VBetP^aER3e~|f_NJM1Q_{1g;r9(D%3rx-HJ6S6)o_O_59>Qf$?Pc!u z1FXM^%$~D;bb|daAE3NmWxv6~EYE^HL05bX#bW-wA(J_H@kCxA|78z~GX%Grc~G1< zkp;+qSs6IFa>-8l&mZ88?v3o?v*ZrajvWQ|_;chw`Gyf@exAgJhhU#4Op)o{z^95J zyKh4R%YL3Lk1NBY&b28I$oyG+lA*SO)jyBqE`xPHk0|3{1J9GxU|eXF@s4IQiH|ID z%7e;=5%(WBM7F5L%?g_l(Jai@Op+oiK($hr&}vlGtdmn!ut88ExQ4xeC+t+dfGB^K z9ee>x?QYin0$D6?7-6OtF?P#X{)7hClOjjaASxhJJ*vZrlSaRyH-XlC}8(b=67zVL@ibhhxcfbkV+0ID#%F71xw zZAiCm;wwTV2nZK_t>jv{ZV+!hCbFaF$W`Im-yrZ{pj$x}Z1>fTveH+{)r;$tSCCgW zs(8LGL44-8H}P6>A*U4^TcbHPzU8o~h;_Y6mPhgoCoiBFdl(&!%=#Kho$i?^s`aQJ zQuMH@*KnA(7mRk4A^>dS#|jAo#_X%;!0~wZMN36DDOa4BbD!j!6_@IET zfW1EPkcNl70*2|PlnQt+;9>#S04@-43*bBfcLB~6@Oi*#0v-lzQ{iv%io*mP2H32^ z-ftz~L%XA*zyWwjz{P;i3)lm=SHM+(&kDE!aHoKw&=qY0hC){~3m6Jr(J0^%z%?p- zV<_ONY+QmsKiz1ELcneTmjNyma5Z43fc=281>6SMF5t6(69n83*ec*b zz@Y*j@!~;JABxO)VQW~x=$qgH0owuh2^bz8+$~^qQ}Ag4mjG@Tun%yHfExk(1>6d_ zUclXes{wn_`XXo)T%i_hH39YrI2v$?fVmPy0?q@RFJN?2utUIm0cQxf3UIQ3(M`e8 z0&WIu5pXB=5Q0tO0UDV`1biOwpn%b5rhXN^DGYFrfT577OTYzyI|S?j+$!K2z)b>f z1>7LuZomf>*el&Mga@BmuuTHISHKp)r2@7AE*5Ya-~s`|5~e%>7Xi)`a4Fz40ha@| z3HTu3Fab9KHmmPnO55N#L8GFe3-FMDp=r>00mCXmy#j_+g3hXNaVX$U0Ve=%6EJ)w zs9C@TfExu|0=P!Ne5DptB_8SlmkYQRaG8M50(J|y7jU6~xk64A-VVzJWeXU76J!^# z9dLqx^8i}~TnIQcTk`VZx4jGxk|?M`(l#vMM!*9ChK1xl0mDMdoqW#Q2l%wW4+Cyj z;hSLT81a7x*T^IaoY3 z;M0Ix1q=(3CIJrtZct%26e8Y(;sF{FpMaqd*(+eKP^o~qLd60u2V5ZFgMjk{><64F z;8wtC0)~Z%O~9}a2~%OOdjJn+^izz)E@0xkl4R={O|I|b|m z+$P{gz|8_~2iz#&vw&+v{NLG+hbmDp47gl{Z!rTd6EG~Kyl?@hyTuN;P~c%9Ei1ak z4LDoi_X4&HxEgSRfVo0e0k?b>>{{6_rLVj;gt!+Ud-aX(F~xLGSu@u7o)p~Z-wUL)|WM!Y3Y z;M)T5y#n8E#Lx6Lh=LAd!Btvcb_U=(1^sCwepaEtcLm@F1pcfMA6B8_y;8TaAlxPx zdIIpL1-{papIs#IeF1pQa{Wepgip|)SMc8P;b+wt28;}mX9dHc5s$C*slGB~#7AWc z{IC%pqcvjTwTY&c> zRuhY{V7>?x%A~EUIH^epvKr}=MPHI=BR)k_)MmslC=hZIctWVFAk`uYl8p?hTE%Hb zd|HX1w+G-y1U|!vU)Uq?nd5k`GW@OLXI@9+qEj}gDpD)4&)@C^cAuHwDg?6;~|6jT@)R%;dbjQBjwLRCgQ4xyD{sk}m| z^R4Uh1^%Fc-kY*kbGjNsLCRXqS?Y~=r`Cc7Bi_|3Drhv~^ZNweZ^WGrdbgQHLH9%f$8XnK))Rnl67;=B{0^VM_Zji-DuM4e;!7+Q zUe(m+jRmD0f?>djzgM%wpb>vxwxAyhzz+!gun}Lb$r%aA|L;047)Ff@o-`G&ys)e5 zGLI0XEGTvOJ;Hfqvr*1IkB}28@LK+Vf43;G7#SX@5cn`7zCsgZHR8YH7WC0Z{5{PA zZ!_Y(dy7Otg0bM<0)bC9;`f^bKFx@KFjL^|0eH<489F@ft@>1-^QOv~0R_Uj?m`08STFABg934E~; ze?UxfvRjAuqT)xyuu-->G$#W7(Gnr3RL8*aS_NfB{J{=E?=j*Z)8y|p;(ve!DEYr! zZY+4bN(ibj;%l4&?=#|QsK8el@%0%3Uv0!6k_7%B-w@EX_|R!lP-A2`>=*cYBc7ED ze1j3+py3;h_#>LH_%%Gw|6$S}YJJ?K6{z?|%^91G_$Rf#Z!zMJYV@r-JR<4FLZN7z z5x>c+nXX+|0EM0$5DXng{8MKIzSD^J7YY370KDdOT}J$|7M0#BpEVXd?GS>xjreEI z3w%!iUhB(VBmUWBLEmS@H)(;ZU%{_X^MADX**3v&-pKHrX1W0*{&^c~`cJYuhdVot z`)}j*kDvJQ154M~dF92t92>4a>^j-T|9F-1C$r_Bu2e+v>s$H%MQ)v`h_FlF4%J9m z=YJ{JmwHi(bKvZ5+}`%PAh__<-Da8yA`;gk}q&)s;Bvti=1yxY}c@+#x08fHSl7WSfwhy#IFm z{qO2qalZd9zx|gf_}adSkBxqS7X;c)TK^lz=(vHD_aV7P{`Ls-eTa7##YuK2f3sSZ z2q9s<0XU82xN^$49tK-^Qvw!aZs$ij)_?_yO$X znUy}y{i{|!`oq@O~FN2$VjvUd^b)F;N*F6^e z23HUq{EyMG@chXKxx4aN^l!=UN!j6kbhw8__mj2h%8gJiM6iB1mVCP9bbg8S{| z{DTp=l!F8Lpi*9j1NszJ-%pmrdEkBcwBh(iMH3Vj>GmpERvAugpr0g#bo0lo6m+fi zQ?fk7GX7ZfDK6->v5HU0+J(GtFYp~g`>^iF#T$F^gR;f4Lwu`?Co2hqc)M@5wt98D5 zX1vbRzbB9DyNVBz@0=$kvv-}pOi!I>gXhV8_-k*?c!acRnp9bJR$(jA^0(SM>3@TrrR zb+PFWWYJ3PyNg^*;Wvn>zl@l5pjGXk_ebUaOc{RCg(dH3gC!F#W64m}lE-zH{Nv>D zbMpY%vL^9=YhSwUj(SvM(O08}{FX!G%8d-*y7wI;Ea7wVkgmg8KPTT;XM%Lw?l6T~ z33hmohWWlwoVn=>+z0Zo-Y>|8aZ0<9((@;Uvi?sBW!Im`4^>LLv^zfsmRE+#-a&<^ zVNkJB=O6}k8|xp$dnz?7`%AJSvdwjA)kW>w5Ec0QsUru(m zoBvF9jjMI}24wt2QLFSX|7!yhh7`)8A+k(%5>_*WYCWuVNYS`gDWu(H8LZ%Aa;46Q z%G7Yz1+t$#ezR;HZ+hE!)5HF%G(GdLWWi0l#oshN$FGc=zhMOMuobFI=a<&*;r)+L zW~szYN8ZocZAbJ0!dY3U8qUsbVQwbRX8-+HLkREu8;M{m{zgK{5!PKT&t>2Kn<0dw z9Pi|CZ+z%^$@0+8cr=XPcX*g21;h67>zU`DWbT5N_jzUMwz6nktIU|}XvcI>2&{1r z>+ZMJgB7nQ;O{x&{@FukB)jWU0y+FI5~Wn|DeD_1QE@)VHH;J0f&KS_Ssh*0uSjZ& zPh<@~E>juj!cDk(cByP6NAtGUe~~%!o>E^_l^(NlJWq%C_;<=rO^l1?ugDVR3t++g zT@(D>B<(h50~eWYyVHhWB?G_ zY3F46yNj3U+n7^Y-^LteHgxkh_3abs<8q7t^?&W#_{e}RJ+g6J+Tg$bk6qd_stoX+ z(a90`u8Sm6Skngs)LfjLw9UAL{AZ1tA6vaFv0 z6di8~GLR^^IVOXs8k77=pyZo_<#4i5ZTb_z@}!JazCylj-Rt8aUCha@ONq({`S@RO zN2lp$e{BxOSiZ9v@y=SgLyFeI4QstZ=5t2(6|y0no+77`+f*fwC8q`X<8((#D-&;7Y{|X3JzRe$g`7^w-XuZ2C(x4Rn@S@Dh5J zyjRyg;2zaIDg)xjvkYts?uM|*xZj1zVR97y01sbL%?&qnttTALdQ>g?$)utPmO9&@ zhEm5{vki50M?l@p5e7Qo=)%kV0kFFbe*nxEi;JjykKocYWl0!|H%VKqas)1tcUk4# zf$iNCDZdklTRcZjL@+CwBd^Af`&VRnFqRe`6YHHLrv?(wi^7VAXTrJ_^pjC?!3}lV z8y1;L*CQ@e2E1^>--E*|QPv69s2dWigR^i6i^hl7^9NaWw7hI?-4Nacv1+-IYwY2R zNGbT>!HU=u#%iKr_+-`*Ezh4_jFWhMS`yvk2>RShyzj3KsqV&r*p-4JW>$XBzU z#2C9Y2K|vbSKhltP5Rv)zNeuLK<%16*BDXFgX>M&T^vsdT4K#{U?Ad+J;S?p@^_Db zUQK+4=gO%Qm&w0Tj2jKz?ZJl>V&$v3s^ziriWnuCfgK%KQ6;;^?g*aHjE1uASUDm_ z3BgdrtZqb!_kP5y0@U_iEPec?aMrj*He|du`BqZJ+HCUX^hiFm;adFfK=D2s;jrpv z3og1Y{+)lva6ByWJdkC^p)VR(Nt_(Nv=>!dFu=oj^;x?m3;&aK!vI74g@i0FAzD=)fBQgk9a8?B;iU!BMc$l@@&rgp)_g-Tq2F_s zWz3T&?S}k#p1e=V#CT_&&#_IUjhTKa*C~HzDcIGHDT(C@gmu~bW7mOgyLYn`t)5fz7`O<{GMu*sj9L1^Q7DM_{VGLOqcm`BYE+p8rWNlJ6zlCRE?cdfxXJi0bey6aNh8VtvYe95?4 zsuDHz8fwDlIe1NJOXX|jJwvQ)sUfH;z53^+$T5_JcRpLN%n(?W^15a68tlpTE|Xni zfh0ncSPAl&0AqhAIR`^k*=KNP%5$b}pV*cuTQ0A_x7M1MPp*7Hro1quOwY-8@2WE8 z$vc@@P@|reWI>jP`LpEs|52L`P3GYhQ0Ar=prjfzLa&jpk-sy*oYzd&=I7Ty8?%m+ zE0eHdvNCV2fHE1(l>I;37C`P;Fl-F+& z=>-XOT|Bc;x9NhBFD-Nj{j}35U;h=#lny0i~UBsbgxO1&PJ|R(toQ<-k~j#?%g|HTJ2Iwn_ZK8#+hQ?B zkV3Qu5#-(hE=7qT)%i;ETk;K3hSp=UTD@Lw(sd4SvG`C1=H$0^ST(TSAb&auw}EHR z)c4|~Ekt*LVReni2)1aWTox$d#~UXv)W5UIuuxZ>W0Qf-zuB-_8Y<*XDKs{vaI)L| ze2csh?|?>sOWvj#`@0zvX8M+Drfd_VmhcNK1+RB{2WRG(q3(uXLa^~@IgY_31 zOyJ%QK{4uP^wZl7Gd_xt58f>2VV)?s8H%lE2XEFCv*U${9Bqi=t4@*A-?V?w*~(r?yoS1jX{o0#10|KE7_{Yc~#$aPJ%UysBH25!7-ECdgy>N{AdDfe&TXE~r&S z+V21V%DNu7sH*J$?tO0%5cGl|qY@7OMI{-ObW}_ccY?h+_0!nv8YT*#=;`S z6%@VfMoC326}wWALq$bJyKY)iVNy}y#{Qy~iZ<4$#r%Emd*{iF^7+i?z3(~ao_p>& z=l*@e8^f&JimzH@=G}+}T_ekIu|vB_?U>wWg?54!+SGv1-WcROZw?gUC{8)=;T+p} zsjf|`SJ{W?>9RiumSJhLhTlJ`*nZlIro}_be4@T}S~>n3e$8OyH*K6xk>BH6fxu6H z(p2ch1T1cXvSOKj6S`Z8%)SX0lt|xA*sp)dg0g2*gRMihDyat9cM}}dF5_>;{;XYQ z-^^$5O}(=0W+;ro3#s^3po(0ecA1{bm*P^Adv46f-GV_fO%~k3uZk*0AXI?tw;ki% za<}jusixy+HgC?xHXLUSOCC`(wX!Do8yZsw2e^t`)d07r?8Wi&i(9!fvEAB{Xl|Or zYf*`w>e{RpB-j$~bfT-8j-4Ed^O(&oCfT>sDNAnS(@+|tvTvy730MIjZ#Ex-4S173 z#M*A-(5f(W=if`i_#HilqD;8Mw!f7%Wu2jyfeey z;1e5%dhFyIVmo6Fqf64NIW36tiq&N3KZf{|ZY+c8%J9|F8d_BJgM-esa`K1VN&w3!6^43?# zKl?F*eEe=c3aqBP`5k9F>&lJLfwxU>3{u~G4;pvTJwfM@pxrF@Z{$Pej7@wY)D>(p zE3DlVRH17#mh$Z~b8}E#pvK8BH}gz%>$Jk)x(b8N56qU`dM_rjI$3`&zckED^sUo0 zya?zHoNKmA=azFF`sfzZq4itNJ@TAe1YNDtR}`Qt!q(MYbgr&-K2w*^cdoARt(Xh$ z?ZeBff`Vh({M}Z*;cV}2xes+aWy^i%ivRxpb60rZ-4Ah(Oe@AT`Cm24pP9e6sC9td zf7JMHY{PK(lA0T1X$sgHeijW>3lZH5{F0?lZA5N5m|I7M&i3N#~l0TH>{EUvnEa>+S%4I8?_?vG1PJ@T2d zFYqWY;~^Z+IAzsC*lj<;WIJGhyIZ&OxX2TO<*R;{-t9r9x| z=(souFAX}712s;L-x;*PZruq<^L*~?H7 z!FooW+}dxR%v9}5brtoPqFzjV0dHl(7mLIa&Ii|W4ZAUn}z!r>q`+cDuOe2FCQ6x`j2Ovj_6I1?^f_l0ZEd% zH+Vm^m&b*LStW3?hf~%$a_zpL621G(RKR&@(1TLcB|n#DNV+A~6nLpsWze98njleW zO2CpBIZ(-W1zR#Q{FjH$)QvB~jgd$H$OrH)%Oii{hXX{bALjcJ`iv@-&FTz(>uODt zzIMFuQuTbZH%e7s_)k;Lv>sMXl@~t3r(g)kedJuz!XFLVOPc1s`DjotFlkrr$AIVS zlok7fdyQ(|jOugcU#kv2`{6F&)j#;VtX>)Da_cV@pvG9BbgH5|^e8wb3JZlmQp5<5LL7}UTPnJJE z%ahp~e%#MwY=fdrlAHd*&u4RF=3l`-`WNtTvF^wJ757)D`zP36WZ3~eR*pEp-8f=3 z?{7Q+_D*%boGtWs9^gyyi|jJ_d46|M_E}%pLl?Twq7A+y2ehq&FX&_^PT~LR)h0yI zH>!Ly-^0mHsCR<*5qIgY?e- z1`n`!(P!@q{4G|h-e{ikbCi+lDle724R~x8FITJ%{8+80 zOOM}_%=YuyvFSxm^B7Zb0R*RzV67kHD&8UH$6SpH8u_&I)9;-^f&m)MA4ow8R}aMt z2sFIk(kl$lZTH8^92BvPxf>ORzGRMfNiTtjH^;rq@k+{IYlQE@hX3FHv6# zd|Cf8*3TWX|7Gmq;-%*mK0Xrv)xcvcn~R0aeFeQHURIsWSt5_T!tXZCbK2&quYjr7 zkioq!zxk|th+lj6`fBpx#v6GMGaj;SFbYZz}GPWv}s(ql*KQ_#^d-GwMCt@K*NMc-*Lfls%?iaYlV* z8&1U)J0L@ksaKp)*VvXVU9W>}P7zj5I93j})f2RB#WGW|D+g^mwi;~8`)zg7_c~Sy zd9vzt{wCknt6pY%fVutR4ZeIank!6zA?#rS07Q-Dt?J~j9>;nRjs&*@X)hQE4< z&chSj#_3aMe^MZn(uGf-zd1@To*#L~nu6Om=B~e^VBPAqYy4L%*Y9Sst5`qfe|v@g z&IQ;$mOZE+X8Hc?68-Ny77MNis`2R*`hzZEZ0}JY>Di%=MIvSG&}U;KRklOFe}1#j zhqT~x2%jVPwBpl-PX|6-`1Ih@ix1ius^JrgkH9AeAE!UFRR7e#DmA%We}li-DZejA z1xw`c-FgClt5>G%2HYr@E7;N{*DIJNcd1L8JfJQ@9#zZ_2ITkZ`eBzGz6aNjx@5{8 zuq)+q1v~oXdIddlm%60M1M1Q&k1FPO{qlPSW90A(T--9H0@RZOa=C)la=p4l$X)7k zL>^F=5_wczeDZsB@yg+QQNrkxDSP!@c+U3vUOh4V%}&AJLdIy5rLR+W`Ty`oeJ_{$ zEA^}pizQE0>bYS{Ak~XsF8}WtsV}+1B+9$0^qa%q>J_|Yqoz6jentRls`f*T#gf15 z*B=bq2&pc7i(^lq)Pg7U9EZt~wNL09LRz{6|3{jCY>nRTARtdYt^W?O%zj3%bmR*D zw+R2w&*)DuN1EX8waLfo^n-Mp{TIFWniv5~-eSpuzf+A{<301XR8e13x9=9K+ZRo$ zPy&7G_8%?k_8arIOx=F3ZV&e>*(XhEt9`f}>;syX zSgOovLzCNj!90J^RChYIi9CGeXDP)U6qz<7QyZ2z`7=$(j*5 z-WjE7x@yIR(WdiND`4oj$=V25)`L$UnAgcqUq&KT!nS_7@fH0D-CowD7l$;1dMHK~ zw&@A-hbG+_($O#Y=N>uyRsFDi-}$P(9z8zuH9g)DA^2x8a>Hx-$}o@MU!-Z;d2hiRN-TE(iLwB-kno!*G-igIZlbPQq;uY2O-oL8Ut?9ka5+z`I zuTxXEruV*38m#>OGGED>-s_Bz{x|jK9q<4~n{{uY))sZ^zqmyo!$Q77a&<@eZ+ctb z${bF?d+KG+-}Ki)zJq^yV`Tl`_4n!C|KK70LKYSy_z!N3EY&Jo7s$wW^j!{<=6~!R z{Su}U8{=2cVXJi~2mEgx(c_t;0L6-B*SmUi7;5QrAz(kNlo}%sy{Bh5l0b9I$fNrA z;U{6y>1s`zcAIken^EFge0v|RT&)x!)_yTK@W}UQtbdo${$$ z(_j5PO2G72e~!8}{nhVMw`R8eFI&l){_4jIT0hVahG7QMA~Y@KA=%WXzb8NbP#@|r zY4Yn2^>>gUx^_f1w&|bI?Vpb6--KZnVkMfEdeSsqEr*VB6RNaFK^d9 zjP6T6Mi3l6OvGM)-6x7}-GBY59?rtNpd+6941>1@6>M&G5Ro% z*UQ3AJtf4N)8%uW`eH{0$UgZ?r#{nBrzHF{PUt_mFx~m5apPT%Un%(6Fj_h00srA4 z#$nDj`enHB4#$Y@zcJFdma#PdYr~8SbnK8SM;ab~aF-8F(|XW%yn)BNdUjTnQq z_*aZKM(QdlZ<%D=iQg>se=*ru!@|<^@$7f{CRdHzKi)|2-!{dV&XENEho>0{27Z?3 zCZ<&eG_;rh}G-IibInwPhe&t5Lbk8vssj0qTjxiHc zeeE1$uA1t*<`}cl@#E*>f|mu)1r@?&3fB1#%{6xIX9Y6r6Jx9+UeiPH+W1e5@s14K zX2=?K>(%rJz4C~{ml3i@U2c#upBt0-N8ihI^Lkq5d=7H0ELE3K z+3>mXX;D0SBVOjGx(C*haD%!6N2)6pZC-T+9;U7W4k0|;#8}J`#t09-0q5Wk^5}ym z#$;$>jA07zP#47?p{~OC{SGG)Vk`pug+Bs|G=m;b~StC0Y84d^}A6O+t7s$(k$oz#~nbBn= zM8Db}On<$~pWkJ?&Qh^X1IG-%z7T#k^9>|{q*!?tUW5E{O7H&}V~gNdmWEis{kjy! z?j{s5U@Edie-dJ5_*|x^^m_0!zz-$fxg@TeUA(FSw3(n@ehitI7<#79JkiPS`R7wLf{kteA~bukvq!3 zY8Z&zaTtP)uZ5?CrO=Te*s2cM`W4~_$MOgwdj%U6v9H@0i-TB~y0ps-Tvqs?s*7a) zC$!TQD3fI2kSL-3j6~9s31hvQTMayc@O-sY8FY5Y#e$uxmGw}&8d9u>#IOXJvI>L& z!rOym6tY`rk1CgB5#KMgfe4hhtNl=D*H(h>A^x{Qdplmbzc$9EBJ`};A`eLt+SOiQ z3iXiLC}RGBkG~;Hh4yui^r6`5OyE)~whiNu0H%x^vPQs5cp9-x^?EA(@ShTPU#atFSbP~vI5OWjG5PZJkR}#Kn@aSgnD+sSc z-e8#f24P$RGDSc1pwJdo$}*6PU^H7o(&z?@5pAuH@K@+2N*gsQ^gF>-cVaQbKP~v( z-GlOBSSe3cP9a~0O%QxnF>s3|A9f+EL6X_}!j=kFbThJ&k(a_&p~98`ftv)Mn*&S+ zh21aMSN)KuZWC4x`Q8BeM}>9}n5p$Mx=q+ig59I^QBH*YL$H6r%bdI(){g&Fi7W%5 zfeL&rwBtfH|JxY55V>M^zwlJS9aX?NmRR_F!4`A@lb-Oag|<5%^<)$OHrR#XzXxiR ze|$u)0a=BC{22~&PK5$j6CXR+j$GhI!aoQtJ6-yEFj}h7LsL5hG;8Q|!Ov@v^-!N$ z11lC2`8vVf%1T=-UuZ9)am-8n9fEz_xbIu^TAy@%YfLXHM;mNoh7Jg}UsX=NjTk1F zs|Tg?tkMzV5IogJ8qy*r3BI)ixQzI7!1o65FBSX=rN5N;*COL{fGIW+g<^zu%m)Is zHsTSX7I&TB8Yxk3kjY6A9fFrON!NF1EV?st%N&v;;{0>nJlzl4d4>1y2!=w zHGbs`#S#wm@Ay%|eX~S@cbwDJ#-^EQB4k>dZy9L`64`)#3 zNB$t#S^@cFs_hhpPxK7i8N=exlaQ!(<_}90?CNynS*ImGEKTrjwcu9}zC>t6pSe)M z1R`V!o{?i}N!eQvQmlpzii2tC_Dh7Zldc)w%hDP8F;x`CQx`BHL9~OLL z2e56x7x?M9=a+#{O#8=IC^C_g{e#-X^+H=)D&0R|#)<)%rhoAuowwCmxg{{H zl&e&wR2TaNA6*Gdz7fv~cFPj9-AUEHCbVz5Wi`~MsWlHZOwAp?AJ zQPdMcTh%Y~|AUmSfRP(X3`5(>Hl!eBSQMt|WsNA-X7Qtk!t;H=S z=(KJtx+79V5%U&6pqK=f3Z7I5+)jA4(Dt+f+Y0YSg$k2rM!?}qTYxuoQph=juyKt95YO4UFdVHg76d=fS;!9LqwNA ztkrT<%u1pCt_y0Yp)tP~{F+h}BegMwf={die}L5N6zn>bXC;IsMr34!oj5Umv;%># z(>&&o;ES>$A4BrTu*w~E1Oi8h@EPn<1;{ZmCj{q+xb29Teqro)YS`;o2FDIV26zVL zV-tfT6N@n;xgY!vOMh%S#*k#-dcyb-q@DjYqF1+rM!g~S9>LM8&kSU-=pED1NIRIZ z2+p0Ez!aR=r(r_`Fxebi7rao8ZNLz+c;88k5U54>SUD4WTxd7A2O|>Oh0f6gEtCSW z{}I|hiZv~~#(C2?iIW`OZK8%(e577=fsZb4WTVCfT@p<#160AMSKh;BXRP3 zOD`}5a3lh>I}4Zw!njdFyLTCynQqk^heW-;8+^*3xQj6 zvZ@*aEu>%r45+69Ffa~d+3x-T{E*-esRmG{$6-2Ho)4TuM(};4_AjtA2AHzU`G#QGt-#sD$7UqA1(*Wm z{7z`SHNagKe-vg@US7HHXPDsyxx&heQCA3~G9B1ub*NFlLmKu0QyPxKHgZWNT9ZL) zHVbwPX4>%^RV>(!99U)ZciQ`6_!PUqQGXIzWjwsoL*<{vvSeN^+DYvi^}Jv)Dn895 z&@Qyge89HC9-;lV5ZG?VuZVvMFeUuxFu~4O@g_fv#-eif5nxKd(Kwdb-3H9jKqr=B z>Ea?|w;Ez>fy?oq>riM9m7}wf@kwZLzGcwp8!`B+3dtj*Zx`(H3DWxuIv(7` zf{*ByWgsuiAnoWMqfZKUycU@H$C%+T2WHq|95YR5KPf(GA9E?1S0H<#247nh|HJ&R z8bd)JQ!coX0o-U+I_7DiH5b4L>Rn^r7TO#2(u-n62%6mo#{7a@0 zDn3Q>ys3g+fyi-T$)5+aqdWRhL55}bd6$Zi{Z3gAyQ1I#W` z7@3ID_N0E^hX_qJFlF9({}NhGrgWb|E7LHtR$G-MAR$u@L5y0R0Ovkd2TWEc;ABTV z&Cv2l{sO4&1*RA$phuQh2H>j&9|vbMnz9md1^XHLbV~Sy&B7=u_fY|)T*3oFyEPe@ z+?s%){ZjNsdjw7Rv)~t$0M}UUOlZV5trZo}$dT}l;P)Zg_Q;fg?NYJ|i`@Y1RN7V2 zEw;+zxwozr;9JAL8Yx9hkV_`|Lc#A)sZEBDT_L#MFsOIzErMNUK#z;$w+Y5ndFl#d zpA{jmQ~~>}p=s<3g8j~mu*H!45y6%zJMH>D7e@5HTo9BevE>@ui?~j}i(U{{o9x6$ zH%sv8J%bK)ErP?7s2Kd`x=LtmtE3mj67xZ(X1mG+k3ccoZLY^qtVdR###Xw#lZq?# z9}1S{l&vUU6oXo*HHkRzEA9eru&hrUi!egmR-ZUsXgdqhI;t)aXIM^ET_?$3EO=OG zfIQA+?o;JyTufXq_yl;A-$xdR99F z1p5*3z)HwGdl-i)ra1>Kr+zmMdBBrpCUcA}sz)(ftR1_5K)b=?{w??=%Ya)*%}FfV z&^Xp%;qj4J(e!{%)sByc@v1S@*zqYi@^AsykUY|0hmzN#xSF}fFUQ(cec8e8sN*rp zEiVD4#qN06y$StBt0M(l1pBS>LpR|v!OB#`3J6!hfOueK0K7LI>&EhS!iePfCgAP> z{GKM)a!2$+WQFD>9eB2l8RJaWj6a3b>waKmG}KPODp5U~4lT6?mkA^Az_4F7b4SXG zYDiJrCoDx(eE~R2uuJL&N5l#Fn9NjDC>baC1YgzxOun8_jFZtrGDAm*+CW}qSvTQj z!D8LOKtDw2Yd9yiI@;7z+X)KR2(s=oTeT=@d_Nc zMgUVvOuSJTi^`Q<)UJsIIGs}p>B@wv%tdilh_z$mx@@!h0$Yd}{3E z?U+^n=tR^ekitK~$@Q`xb|tn#u7JqL1-~jFXD0UvwjxLNIvfen4gKi;RDaS8!SNPd zR|q+)-zuJj!?4Sm2dhomD!AGguq5K|5iBklIEFA57hfG3G&|`7!Rqnc4Qla3p&plo z2)ymIDTzWeSBbS&=_ww;uBt@o#e|n&!(IUSY{EAnq8p_c0wd9M`-Ec3BUsy`7={RQ zD4T)-b&pC6ip`Y6g5@bSRm49o*tOu>zMb+t|EPdd!c6^G zXvZR?7sXUwVX@F&4* zz_;T*y-sKaNx=2Q{~Ov_4NU1c{UgD*sCZCbOz##&THX;5s;vSkTxkD>h4v$s6c-dU z1CxT3Ntgu|1Fs@^oCAAXU=R&hDVJd~qC)bCP`p+dJ55f~g)B*-yt?6{v%cJx~H zx+kNvcK}m6+!qLbKKdbZ5+5tPg--B&gwdN$E|Wf}#S2%eaZ#sK%pmT|g1aEh$qR0* z+83zW`iTFP^(9Or(`uBvS8!h__~iCfU4*<|3XC)1(?8%FN}+g^pC?_Bj)X<6-Kb}= zr6<(`Z>zMWdQ#Etw{v-a_Q31;4mN)fR!YK*U)(AER2I+#zV_T7>%G-Fa7-8)0gaT?&T8&`N4B0yz z{zH1PP%8JWU?ZKtF&4fcOlVhzO1HpVq@v)o7L%?MY0iqKvtLA|8Lvq-S( zQLR1Jc&-*9O{y9?5%Amq_oM++?s;w(d|emhmsnWllKsDZi0{bEaUf zTirXTV9rdT{i_-{$ueNhm6%GpQD6yS>^Y5oU|S&~w_pNrGx4!14o3o=;g<{TAp-{W z6Mw&8%TeAw*_-o|sJ?H$u+rOmj&M0c&uGtsO--b@P-Z{}>jW0d3GdH)ZXx2jbD diff --git a/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.c index c462a17e9..24c7300dc 100755 --- a/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.c @@ -890,7 +890,7 @@ int setModule(sls_detector_module myMod, char* mess) { } //set trimbits - if (!Feb_Control_SetTrimbits(Feb_Control_GetModuleNumber(), tt)) { + if (!Feb_Control_SetTrimbits(Feb_Control_GetModuleNumber(), tt,top)) { sprintf(mess, "Could not set module. Could not set trimbits\n"); LOG(logERROR, (mess)); setSettings(UNDEFINED); @@ -1671,7 +1671,7 @@ void setExternalGating(int enable[]) { int setAllTrimbits(int val) { #ifndef VIRTUAL - if (!Feb_Control_SaveAllTrimbitsTo(val)) { + if (!Feb_Control_SaveAllTrimbitsTo(val,top)) { LOG(logERROR, ("Could not set all trimbits\n")); return FAIL; } From 97ba81d923171eca9d9598dc7360c32347eac77c Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Tue, 14 Apr 2020 16:58:37 +0200 Subject: [PATCH 02/10] fixed test --- slsSupportLib/tests/test-ToString.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsSupportLib/tests/test-ToString.cpp b/slsSupportLib/tests/test-ToString.cpp index 7fc6b4bcc..8bbbeb034 100644 --- a/slsSupportLib/tests/test-ToString.cpp +++ b/slsSupportLib/tests/test-ToString.cpp @@ -109,7 +109,7 @@ TEST_CASE("run status"){ using defs = slsDetectorDefs; REQUIRE(ToString(defs::runStatus::ERROR) == "error"); REQUIRE(ToString(defs::runStatus::WAITING) == "waiting"); - REQUIRE(ToString(defs::runStatus::TRANSMITTING) == "data"); //?? + REQUIRE(ToString(defs::runStatus::TRANSMITTING) == "transmitting"); REQUIRE(ToString(defs::runStatus::RUN_FINISHED) == "finished"); REQUIRE(ToString(defs::runStatus::STOPPED) == "stopped"); REQUIRE(ToString(defs::runStatus::IDLE) == "idle"); From 655a410d4373e596590e12a410207c9812561698 Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Thu, 16 Apr 2020 09:45:44 +0200 Subject: [PATCH 03/10] cleaned up UdpRxSocket --- slsReceiverSoftware/src/Listener.cpp | 7 +- slsSupportLib/CMakeLists.txt | 1 + slsSupportLib/include/UdpRxSocket.h | 143 +++-------------------- slsSupportLib/src/UdpRxSocket.cpp | 96 +++++++++++++++ slsSupportLib/tests/test-UdpRxSocket.cpp | 88 +++++--------- 5 files changed, 148 insertions(+), 187 deletions(-) create mode 100644 slsSupportLib/src/UdpRxSocket.cpp diff --git a/slsReceiverSoftware/src/Listener.cpp b/slsReceiverSoftware/src/Listener.cpp index 6c0b054ff..0416e90d0 100755 --- a/slsReceiverSoftware/src/Listener.cpp +++ b/slsReceiverSoftware/src/Listener.cpp @@ -12,6 +12,7 @@ #include "container_utils.h" // For sls::make_unique<> #include "sls_detector_exceptions.h" #include "UdpRxSocket.h" +#include "network_utils.h" #include #include @@ -177,7 +178,7 @@ void Listener::CreateUDPSockets() { sem_init(&semaphore_socket,1,0); // doubled due to kernel bookkeeping (could also be less due to permissions) - *actualUDPSocketBufferSize = udpSocket->getActualUDPSocketBufferSize(); + *actualUDPSocketBufferSize = udpSocket->getBufferSize(); } @@ -185,7 +186,7 @@ void Listener::CreateUDPSockets() { void Listener::ShutDownUDPSocket() { if(udpSocket){ udpSocketAlive = false; - udpSocket->ShutDownSocket(); + udpSocket->Shutdown(); LOG(logINFO) << "Shut down of UDP port " << *udpPortNumber; fflush(stdout); // wait only if the threads have started as it is the threads that @@ -220,7 +221,7 @@ void Listener::CreateDummySocketForUDPSocketBufferSize(int64_t s) { *udpSocketBufferSize); // doubled due to kernel bookkeeping (could also be less due to permissions) - *actualUDPSocketBufferSize = g.getActualUDPSocketBufferSize(); + *actualUDPSocketBufferSize = g.getBufferSize(); if (*actualUDPSocketBufferSize == -1) { *udpSocketBufferSize = temp; } else { diff --git a/slsSupportLib/CMakeLists.txt b/slsSupportLib/CMakeLists.txt index 98904031e..67cc82d19 100755 --- a/slsSupportLib/CMakeLists.txt +++ b/slsSupportLib/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES src/ToString.cpp src/network_utils.cpp src/ZmqSocket.cpp + src/UdpRxSocket.cpp ) set(HEADERS diff --git a/slsSupportLib/include/UdpRxSocket.h b/slsSupportLib/include/UdpRxSocket.h index a6ffffbb3..3a4ee5685 100644 --- a/slsSupportLib/include/UdpRxSocket.h +++ b/slsSupportLib/include/UdpRxSocket.h @@ -1,141 +1,30 @@ +#pragma once /* -UdpRxSocket provies socket control to receive -data on a udp socket. - -It provides a drop in replacement for -genericSocket. But please be careful since -this might be deprecated in the future - +UDP socket class to receive data. The intended use is in the +receiver listener loop. Should be used RAII style... */ -#include "network_utils.h" -#include "sls_detector_exceptions.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include //ssize_t namespace sls { class UdpRxSocket { - const ssize_t packet_size; - char *buff; - int fd = -1; + const ssize_t packet_size_; + int sockfd_{-1}; public: UdpRxSocket(int port, ssize_t packet_size, const char *hostname = nullptr, - ssize_t buffer_size = 0) - : packet_size(packet_size) { - /* hostname = nullptr -> wildcard */ + size_t kernel_buffer_size = 0); + ~UdpRxSocket(); + bool ReceivePacket(char *dst) noexcept; + size_t getBufferSize() const; + void setBufferSize(ssize_t size); + ssize_t getPacketSize() const noexcept; + void Shutdown(); - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - struct addrinfo *res = 0; - - const std::string portname = std::to_string(port); - if (getaddrinfo(hostname, portname.c_str(), &hints, &res)) { - throw RuntimeError("Failed at getaddrinfo with " + - std::string(hostname)); - } - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd == -1) { - throw RuntimeError("Failed to create UDP RX socket"); - } - if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) { - throw RuntimeError("Failed to bind UDP RX socket"); - } - freeaddrinfo(res); - - // If we get a specified buffer size that is larger than the set one - // we set it. Otherwise we leave it there since it could have been - // set by the rx_udpsocksize command - if (buffer_size) { - auto current = getBufferSize() / 2; - if (current < buffer_size) { - setBufferSize(buffer_size); - if (getBufferSize() / 2 < buffer_size) { - LOG(logWARNING) - << "Could not set buffer size. Got: " - << getBufferSize() / 2 << " instead of " << buffer_size; - } - } - } - // Allocate at the end to avoid memory leak if we throw - buff = new char[packet_size]; - } - - ~UdpRxSocket() { - delete[] buff; - Shutdown(); - } - - const char *LastPacket() const noexcept { return buff; } - ssize_t getPacketSize() const noexcept { return packet_size; } - - bool ReceivePacket() noexcept { return ReceivePacket(buff); } - - bool ReceivePacket(char *dst, int flags = 0) noexcept { - auto bytes_received = - recvfrom(fd, dst, packet_size, flags, nullptr, nullptr); - return bytes_received == packet_size; - } - - bool PeekPacket() noexcept{ - return ReceivePacket(buff, MSG_PEEK); - } - - // Only for backwards compatibility this function will be removed during - // refactoring of the receiver - ssize_t ReceiveDataOnly(char *dst) { - auto r = recvfrom(fd, dst, packet_size, 0, nullptr, nullptr); - constexpr ssize_t eiger_header_packet = - 40; // only detector that has this - if (r == eiger_header_packet) { - LOG(logWARNING) << "Got header pkg"; - r = recvfrom(fd, dst, packet_size, 0, nullptr, nullptr); - } - return r; - } - - ssize_t getBufferSize() const { - uint64_t ret_size = 0; - socklen_t optlen = sizeof(uint64_t); - if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &ret_size, &optlen) == -1) - return -1; - else - return ret_size; - } - - // Only for backwards compatibility will be removed - ssize_t getActualUDPSocketBufferSize() const { return getBufferSize(); } - - // Only for backwards compatibility will be removed - void ShutDownSocket() { Shutdown(); } - - void setBufferSize(ssize_t size) { - socklen_t optlen = sizeof(size); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, optlen)) { - throw RuntimeError("Could not set socket buffer size"); - } - } - - void Shutdown() { - shutdown(fd, SHUT_RDWR); - if (fd >= 0) { - close(fd); - fd = -1; - } - } + // Only for backwards compatibility, this drops the EIGER small pkt, may be + // removed + ssize_t ReceiveDataOnly(char *dst) noexcept; }; } // namespace sls diff --git a/slsSupportLib/src/UdpRxSocket.cpp b/slsSupportLib/src/UdpRxSocket.cpp new file mode 100644 index 000000000..558d9ee7e --- /dev/null +++ b/slsSupportLib/src/UdpRxSocket.cpp @@ -0,0 +1,96 @@ +#include "UdpRxSocket.h" +#include "network_utils.h" +#include "sls_detector_exceptions.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sls { + +UdpRxSocket::UdpRxSocket(int port, ssize_t packet_size, const char *hostname, + size_t kernel_buffer_size) + : packet_size_(packet_size) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + struct addrinfo *res = 0; + + const std::string portname = std::to_string(port); + if (getaddrinfo(hostname, portname.c_str(), &hints, &res)) { + throw RuntimeError("Failed at getaddrinfo with " + + std::string(hostname)); + } + sockfd_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sockfd_ == -1) { + throw RuntimeError("Failed to create UDP RX socket"); + } + if (bind(sockfd_, res->ai_addr, res->ai_addrlen) == -1) { + throw RuntimeError("Failed to bind UDP RX socket"); + } + freeaddrinfo(res); + + // If we get a specified buffer size that is larger than the set one + // we set it. Otherwise we leave it there since it could have been + // set by the rx_udpsocksize command + if (kernel_buffer_size) { + auto current = getBufferSize() / 2; + if (current < kernel_buffer_size) { + setBufferSize(kernel_buffer_size); + if (getBufferSize() / 2 < kernel_buffer_size) { + LOG(logWARNING) + << "Could not set buffer size. Got: " << getBufferSize() / 2 + << " instead of " << kernel_buffer_size; + } + } + } +} + +UdpRxSocket::~UdpRxSocket() { Shutdown(); } +ssize_t UdpRxSocket::getPacketSize() const noexcept { return packet_size_; } + +bool UdpRxSocket::ReceivePacket(char *dst) noexcept{ + auto bytes_received = + recvfrom(sockfd_, dst, packet_size_, 0, nullptr, nullptr); + return bytes_received == packet_size_; +} + +ssize_t UdpRxSocket::ReceiveDataOnly(char *dst) noexcept { + auto r = recvfrom(sockfd_, dst, packet_size_, 0, nullptr, nullptr); + constexpr ssize_t eiger_header_packet = + 40; // only detector that has this + if (r == eiger_header_packet) { + LOG(logWARNING) << "Got header pkg"; + r = recvfrom(sockfd_, dst, packet_size_, 0, nullptr, nullptr); + } + return r; + } + +size_t UdpRxSocket::getBufferSize() const { + size_t ret = 0; + socklen_t optlen = sizeof(ret); + if (getsockopt(sockfd_, SOL_SOCKET, SO_RCVBUF, &ret, &optlen) == -1) + throw RuntimeError("Could not get socket buffer size"); + return ret; +} + +void UdpRxSocket::setBufferSize(ssize_t size) { + if (setsockopt(sockfd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size))) + throw RuntimeError("Could not set socket buffer size"); +} + +void UdpRxSocket::Shutdown() { + shutdown(sockfd_, SHUT_RDWR); + if (sockfd_ >= 0) { + close(sockfd_); + sockfd_ = -1; + } + } +} // namespace sls \ No newline at end of file diff --git a/slsSupportLib/tests/test-UdpRxSocket.cpp b/slsSupportLib/tests/test-UdpRxSocket.cpp index d922b5e5c..07c7e115c 100644 --- a/slsSupportLib/tests/test-UdpRxSocket.cpp +++ b/slsSupportLib/tests/test-UdpRxSocket.cpp @@ -4,6 +4,14 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include constexpr int default_port = 50001; @@ -29,46 +37,49 @@ int open_socket(int port) { throw sls::RuntimeError("Failed to create UDP RX socket"); } - if (connect(fd, res->ai_addr, res->ai_addrlen)){ + if (connect(fd, res->ai_addr, res->ai_addrlen)) { throw sls::RuntimeError("Failed to connect socket"); } freeaddrinfo(res); return fd; } -TEST_CASE("Receive data on localhost") { +TEST_CASE("Get packet size returns the packet size we set in the constructor"){ + constexpr int port = 50001; + constexpr ssize_t packet_size = 8000; + sls::UdpRxSocket s{port, packet_size}; + CHECK(s.getPacketSize() == packet_size); +} + +TEST_CASE("Receive data from a vector") { constexpr int port = 50001; std::vector data_to_send{4, 5, 3, 2, 5, 7, 2, 3}; + std::vector data_received(data_to_send.size()); ssize_t packet_size = sizeof(decltype(data_to_send)::value_type) * data_to_send.size(); + sls::UdpRxSocket udpsock{port, packet_size}; - int fd = open_socket(port); auto n = write(fd, data_to_send.data(), packet_size); CHECK(n == packet_size); - CHECK(udpsock.ReceivePacket()); + + CHECK(udpsock.ReceivePacket((char*)data_received.data())); close(fd); - // Copy data from buffer and compare values - std::vector data_received(data_to_send.size()); - memcpy(data_received.data(), udpsock.LastPacket(), udpsock.getPacketSize()); - CHECK(data_received.size() == data_to_send.size()); // sanity check - for (size_t i = 0; i != data_to_send.size(); ++i) { - CHECK(data_to_send[i] == data_received[i]); - } + CHECK(data_to_send == data_received); + } TEST_CASE("Shutdown socket without hanging when waiting for data") { constexpr int port = 50001; constexpr ssize_t packet_size = 8000; sls::UdpRxSocket s{port, packet_size}; + char buff[packet_size]; // Start a thread and wait for package // if the socket is left open we would block std::future ret = - std::async(static_cast( - &sls::UdpRxSocket::ReceivePacket), - &s); + std::async(&sls::UdpRxSocket::ReceivePacket, &s, (char *)&buff); s.Shutdown(); auto r = ret.get(); @@ -76,60 +87,23 @@ TEST_CASE("Shutdown socket without hanging when waiting for data") { CHECK(r == false); // since we didn't get the packet } -TEST_CASE("Too small packet"){ +TEST_CASE("Too small packet") { constexpr int port = 50001; - sls::UdpRxSocket s(port, 2*sizeof(uint32_t)); + sls::UdpRxSocket s(port, 2 * sizeof(uint32_t)); auto fd = open_socket(port); uint32_t val = 10; write(fd, &val, sizeof(val)); - CHECK(s.ReceivePacket() == false); + uint32_t buff[2]; + CHECK(s.ReceivePacket((char *)&buff) == false); close(fd); } - -TEST_CASE("Receive an int to internal buffer"){ +TEST_CASE("Receive an int to an external buffer") { int to_send = 5; int received = -1; auto fd = open_socket(default_port); sls::UdpRxSocket s(default_port, sizeof(int)); write(fd, &to_send, sizeof(to_send)); - CHECK(s.ReceivePacket()); - memcpy(&received, s.LastPacket(), sizeof(int)); + CHECK(s.ReceivePacket(reinterpret_cast(&received))); CHECK(received == to_send); } - -TEST_CASE("Receive an int to an external buffer"){ - int to_send = 5; - int received = -1; - auto fd = open_socket(default_port); - sls::UdpRxSocket s(default_port, sizeof(int)); - write(fd, &to_send, sizeof(to_send)); - CHECK(s.ReceivePacket(reinterpret_cast(&received))); - CHECK(received == to_send); -} - - -TEST_CASE("PEEK data"){ - int to_send = 5; - int to_send2 = 12; - int received = -1; - auto fd = open_socket(default_port); - sls::UdpRxSocket s(default_port, sizeof(int)); - write(fd, &to_send, sizeof(to_send)); - write(fd, &to_send2, sizeof(to_send)); - CHECK(s.PeekPacket()); - memcpy(&received, s.LastPacket(), sizeof(int)); - CHECK(received == to_send); - - CHECK(s.PeekPacket()); - memcpy(&received, s.LastPacket(), sizeof(int)); - CHECK(received == to_send); - - CHECK(s.ReceivePacket()); - memcpy(&received, s.LastPacket(), sizeof(int)); - CHECK(received == to_send); - - CHECK(s.ReceivePacket()); - memcpy(&received, s.LastPacket(), sizeof(int)); - CHECK(received == to_send2); -} From 815b6a37aa9916b333d8208fa200cb1da6805b68 Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Thu, 16 Apr 2020 13:22:51 +0200 Subject: [PATCH 04/10] moved flag to base class --- slsReceiverSoftware/src/DataProcessor.cpp | 30 +--------- slsReceiverSoftware/src/DataProcessor.h | 42 ++++---------- slsReceiverSoftware/src/DataStreamer.cpp | 24 -------- slsReceiverSoftware/src/DataStreamer.h | 36 ++---------- slsReceiverSoftware/src/Listener.cpp | 40 ++----------- slsReceiverSoftware/src/Listener.h | 69 +++++++---------------- slsReceiverSoftware/src/ThreadObject.cpp | 11 ++++ slsReceiverSoftware/src/ThreadObject.h | 5 +- 8 files changed, 57 insertions(+), 200 deletions(-) diff --git a/slsReceiverSoftware/src/DataProcessor.cpp b/slsReceiverSoftware/src/DataProcessor.cpp index ad8a89a77..e45a29a13 100755 --- a/slsReceiverSoftware/src/DataProcessor.cpp +++ b/slsReceiverSoftware/src/DataProcessor.cpp @@ -29,13 +29,9 @@ DataProcessor::DataProcessor(int ind, detectorType dtype, Fifo* f, uint32_t* freq, uint32_t* timer, bool* fp, bool* act, bool* depaden, bool* sm, bool* qe, std::vector * cdl, int* cdo, int* cad) : - ThreadObject(ind, TypeName), - runningFlag(false), - generalData(nullptr), fifo(f), myDetectorType(dtype), - file(nullptr), dataStreamEnable(dsEnable), fileFormatType(ftype), fileWriteEnable(fwenable), @@ -43,7 +39,6 @@ DataProcessor::DataProcessor(int ind, detectorType dtype, Fifo* f, dynamicRange(dr), streamingFrequency(freq), streamingTimerInMs(timer), - currentFreqCount(0), activated(act), deactivatedPaddingEnable(depaden), silentMode(sm), @@ -51,14 +46,7 @@ DataProcessor::DataProcessor(int ind, detectorType dtype, Fifo* f, framePadding(fp), ctbDbitList(cdl), ctbDbitOffset(cdo), - ctbAnalogDataBytes(cad), - startedFlag(false), - firstIndex(0), - numFramesCaught(0), - currentFrameIndex(0), - rawDataReadyCallBack(nullptr), - rawDataModifyReadyCallBack(nullptr), - pRawDataReady(nullptr) + ctbAnalogDataBytes(cad) { LOG(logDEBUG) << "DataProcessor " << ind << " created"; memset((void*)&timerBegin, 0, sizeof(timespec)); @@ -71,10 +59,6 @@ DataProcessor::~DataProcessor() { /** getters */ -bool DataProcessor::IsRunning() { - return runningFlag; -} - bool DataProcessor::GetStartedFlag(){ return startedFlag; } @@ -91,18 +75,6 @@ uint64_t DataProcessor::GetProcessedIndex() { return currentFrameIndex - firstIndex; } - - -/** setters */ -void DataProcessor::StartRunning() { - runningFlag = true; -} - - -void DataProcessor::StopRunning() { - runningFlag = false; -} - void DataProcessor::SetFifo(Fifo* f) { fifo = f; } diff --git a/slsReceiverSoftware/src/DataProcessor.h b/slsReceiverSoftware/src/DataProcessor.h index b6cdbd21d..64111ebdb 100755 --- a/slsReceiverSoftware/src/DataProcessor.h +++ b/slsReceiverSoftware/src/DataProcessor.h @@ -59,11 +59,6 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { //*** getters *** - /** - * Returns if the thread is currently running - * @returns true if thread is running, else false - */ - bool IsRunning() override; /** * Get acquisition started flag @@ -89,17 +84,6 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { */ uint64_t GetProcessedIndex(); - //*** setters *** - /** - * Set bit in RunningMask to allow thread to run - */ - void StartRunning(); - - /** - * Reset bit in RunningMask to prevent thread from running - */ - void StopRunning(); - /** * Set Fifo pointer to the one given * @param f address of Fifo pointer @@ -254,11 +238,8 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { /** type of thread */ static const std::string TypeName; - /** Object running status */ - std::atomic runningFlag; - /** GeneralData (Detector Data) object */ - const GeneralData* generalData; + const GeneralData* generalData{nullptr}; /** Fifo structure */ Fifo* fifo; @@ -269,7 +250,7 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { detectorType myDetectorType; /** File writer implemented as binary or hdf5 File */ - File* file; + File* file{nullptr}; /** Data Stream Enable */ bool* dataStreamEnable; @@ -293,7 +274,7 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { uint32_t* streamingTimerInMs; /** Current frequency count */ - uint32_t currentFreqCount; + uint32_t currentFreqCount{0}; /** timer beginning stamp for random streaming */ struct timespec timerBegin; @@ -324,21 +305,18 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { //acquisition start /** Aquisition Started flag */ - bool startedFlag; + bool startedFlag{false}; /** Frame Number of First Frame */ - uint64_t firstIndex; + uint64_t firstIndex{0}; //for statistics /** Number of complete frames caught */ - uint64_t numFramesCaught; + uint64_t numFramesCaught{0}; /** Frame Number of latest processed frame number */ - uint64_t currentFrameIndex; - - - + uint64_t currentFrameIndex{0}; //call back /** @@ -349,7 +327,7 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { * dataSize in bytes is the size of the data in bytes. */ void (*rawDataReadyCallBack)(char*, - char*, uint32_t, void*); + char*, uint32_t, void*) = nullptr; /** * Call back for raw data (modified) @@ -359,9 +337,9 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { * revDatasize is the reference of data size in bytes. Can be modified to the new size to be written/streamed. (only smaller value). */ void (*rawDataModifyReadyCallBack)(char*, - char*, uint32_t &, void*); + char*, uint32_t &, void*) = nullptr; - void *pRawDataReady; + void *pRawDataReady{nullptr}; diff --git a/slsReceiverSoftware/src/DataStreamer.cpp b/slsReceiverSoftware/src/DataStreamer.cpp index 2b840dab1..9c9b5f1a1 100755 --- a/slsReceiverSoftware/src/DataStreamer.cpp +++ b/slsReceiverSoftware/src/DataStreamer.cpp @@ -19,18 +19,11 @@ const std::string DataStreamer::TypeName = "DataStreamer"; DataStreamer::DataStreamer(int ind, Fifo* f, uint32_t* dr, ROI* r, uint64_t* fi, int fd, int* nd, bool* qe, uint64_t* tot) : ThreadObject(ind, TypeName), - runningFlag(0), - generalData(nullptr), fifo(f), - zmqSocket(nullptr), dynamicRange(dr), roi(r), - adcConfigured(-1), fileIndex(fi), flippedDataX(fd), - startedFlag(false), - firstIndex(0), - completeBuffer(nullptr), quadEnable(qe), totalNumFrames(tot) { @@ -46,23 +39,6 @@ DataStreamer::~DataStreamer() { delete [] completeBuffer; } -/** getters */ - -bool DataStreamer::IsRunning() { - return runningFlag; -} - - -/** setters */ -void DataStreamer::StartRunning() { - runningFlag = true; -} - - -void DataStreamer::StopRunning() { - runningFlag = false; -} - void DataStreamer::SetFifo(Fifo* f) { fifo = f; } diff --git a/slsReceiverSoftware/src/DataStreamer.h b/slsReceiverSoftware/src/DataStreamer.h index 4a31fa550..8b7d81881 100755 --- a/slsReceiverSoftware/src/DataStreamer.h +++ b/slsReceiverSoftware/src/DataStreamer.h @@ -42,25 +42,6 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { */ ~DataStreamer(); - //*** getters *** - /** - * Returns if the thread is currently running - * @returns true if thread is running, else false - */ - bool IsRunning(); - - - //*** setters *** - /** - * Set bit in RunningMask to allow thread to run - */ - void StartRunning(); - - /** - * Reset bit in RunningMask to prevent thread from running - */ - void StopRunning(); - /** * Set Fifo pointer to the one given * @param f address of Fifo pointer @@ -158,19 +139,14 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { /** type of thread */ static const std::string TypeName; - /** Object running status */ - bool runningFlag; - /** GeneralData (Detector Data) object */ - const GeneralData* generalData; + const GeneralData* generalData{nullptr}; /** Fifo structure */ Fifo* fifo; - - /** ZMQ Socket - Receiver to Client */ - ZmqSocket* zmqSocket; + ZmqSocket* zmqSocket{nullptr}; /** Pointer to dynamic range */ uint32_t* dynamicRange; @@ -179,7 +155,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { ROI* roi; /** adc Configured */ - int adcConfigured; + int adcConfigured{-1}; /** Pointer to file index */ uint64_t* fileIndex; @@ -191,16 +167,16 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { std::map additionJsonHeader; /** Aquisition Started flag */ - bool startedFlag; + bool startedFlag{nullptr}; /** Frame Number of First Frame */ - uint64_t firstIndex; + uint64_t firstIndex{0}; /* File name to stream */ std::string fileNametoStream; /** Complete buffer used for roi, eg. shortGotthard */ - char* completeBuffer; + char* completeBuffer{nullptr}; /** Number of Detectors in X and Y dimension */ int numDet[2]; diff --git a/slsReceiverSoftware/src/Listener.cpp b/slsReceiverSoftware/src/Listener.cpp index 0416e90d0..48b9e133d 100755 --- a/slsReceiverSoftware/src/Listener.cpp +++ b/slsReceiverSoftware/src/Listener.cpp @@ -26,12 +26,9 @@ Listener::Listener(int ind, detectorType dtype, Fifo* f, std::atomic* int64_t* us, int64_t* as, uint32_t* fpf, frameDiscardPolicy* fdp, bool* act, bool* depaden, bool* sm) : ThreadObject(ind, TypeName), - runningFlag(0), - generalData(nullptr), fifo(f), myDetectorType(dtype), status(s), - udpSocket(nullptr), udpPortNumber(portno), eth(e), numImages(nf), @@ -42,19 +39,7 @@ Listener::Listener(int ind, detectorType dtype, Fifo* f, std::atomic* frameDiscardMode(fdp), activated(act), deactivatedPaddingEnable(depaden), - silentMode(sm), - row(0), - column(0), - startedFlag(false), - firstIndex(0), - numPacketsCaught(0), - lastCaughtFrameIndex(0), - currentFrameIndex(0), - carryOverFlag(0), - udpSocketAlive(0), - numPacketsStatistic(0), - numFramesStatistic(0), - oddStartingPacket(true) + silentMode(sm) { LOG(logDEBUG) << "Listener " << ind << " created"; } @@ -67,20 +52,15 @@ Listener::~Listener() { } } -/** getters */ -bool Listener::IsRunning() { - return runningFlag; -} - -uint64_t Listener::GetPacketsCaught() { +uint64_t Listener::GetPacketsCaught() const { return numPacketsCaught; } -uint64_t Listener::GetLastFrameIndexCaught() { +uint64_t Listener::GetLastFrameIndexCaught() const { return lastCaughtFrameIndex; } -uint64_t Listener::GetNumMissingPacket(bool stoppedFlag, uint64_t numPackets) { +uint64_t Listener::GetNumMissingPacket(bool stoppedFlag, uint64_t numPackets) const { if (!stoppedFlag) { return (numPackets - numPacketsCaught); } @@ -90,22 +70,10 @@ uint64_t Listener::GetNumMissingPacket(bool stoppedFlag, uint64_t numPackets) { return (lastCaughtFrameIndex - firstIndex + 1) * generalData->packetsPerFrame - numPacketsCaught; } -/** setters */ -void Listener::StartRunning() { - runningFlag = true; -} - - -void Listener::StopRunning() { - runningFlag = false; -} - - void Listener::SetFifo(Fifo* f) { fifo = f; } - void Listener::ResetParametersforNewAcquisition() { runningFlag = false; startedFlag = false; diff --git a/slsReceiverSoftware/src/Listener.h b/slsReceiverSoftware/src/Listener.h index 83e788cd1..bd8d382a4 100755 --- a/slsReceiverSoftware/src/Listener.h +++ b/slsReceiverSoftware/src/Listener.h @@ -53,40 +53,20 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { */ ~Listener(); - - //*** getters *** - /** - * Returns if the thread is currently running - * @returns true if thread is running, else false - */ - bool IsRunning() override; - /** * Get Packets caught * @return Packets caught */ - uint64_t GetPacketsCaught(); + uint64_t GetPacketsCaught() const; /** * Get Last Frame index caught * @return last frame index caught */ - uint64_t GetLastFrameIndexCaught(); + uint64_t GetLastFrameIndexCaught() const; /** Get number of missing packets */ - uint64_t GetNumMissingPacket(bool stoppedFlag, uint64_t numPackets); - - - //*** setters *** - /** - * Set bit in RunningMask to allow thread to run - */ - void StartRunning(); - - /** - * Reset bit in RunningMask to prevent thread from running - */ - void StopRunning(); + uint64_t GetNumMissingPacket(bool stoppedFlag, uint64_t numPackets) const; /** * Set Fifo pointer to the one given @@ -140,7 +120,7 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { void RecordFirstIndex(uint64_t fnum); /** - * Thread Exeution for Listener Class + * Thread Execution for Listener Class * Pop free addresses, listen to udp socket, * write to memory & push the address into fifo */ @@ -168,16 +148,11 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { */ void PrintFifoStatistics(); - - /** type of thread */ static const std::string TypeName; - /** Object running status */ - std::atomic runningFlag; - /** GeneralData (Detector Data) object */ - GeneralData* generalData; + GeneralData* generalData{nullptr}; /** Fifo structure */ Fifo* fifo; @@ -190,7 +165,7 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { std::atomic* status; /** UDP Socket - Detector to Receiver */ - std::unique_ptr udpSocket; + std::unique_ptr udpSocket{nullptr}; /** UDP Port Number */ uint32_t* udpPortNumber; @@ -228,36 +203,34 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { /** row hardcoded as 1D or 2d, * if detector does not send them yet or * missing packets/deactivated (eiger/jungfrau sends 2d pos) **/ - uint16_t row; + uint16_t row{0}; /** column hardcoded as 2D, * deactivated eiger/missing packets (eiger/jungfrau sends 2d pos) **/ - uint16_t column; - + uint16_t column{0}; // acquisition start /** Aquisition Started flag */ - std::atomic startedFlag; + std::atomic startedFlag{false}; /** Frame Number of First Frame */ - uint64_t firstIndex; + uint64_t firstIndex{0}; // for acquisition summary /** Number of complete Packets caught */ - std::atomic numPacketsCaught; + std::atomic numPacketsCaught{0}; /** Last Frame Index caught from udp network */ - std::atomic lastCaughtFrameIndex; - + std::atomic lastCaughtFrameIndex{0}; // parameters to acquire image /** Current Frame Index, default value is 0 * ( always check startedFlag for validity first) */ - uint64_t currentFrameIndex; + uint64_t currentFrameIndex{0}; /** True if there is a packet carry over from previous Image */ - bool carryOverFlag; + bool carryOverFlag{false}; /** Carry over packet buffer */ std::unique_ptr carryOverPacket; @@ -266,22 +239,22 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { std::unique_ptr listeningPacket; /** if the udp socket is connected */ - std::atomic udpSocketAlive; + std::atomic udpSocketAlive{false}; - /** Semaphore to synchonize deleting udp socket */ + /** Semaphore to synchronize deleting udp socket */ sem_t semaphore_socket; - // for print progress during acqusition + // for print progress during acquisition /** number of packets for statistic */ - uint32_t numPacketsStatistic; + uint32_t numPacketsStatistic{0}; /** number of images for statistic */ - uint32_t numFramesStatistic; + uint32_t numFramesStatistic{0}; /** - * starting packet number is odd or evern, accordingly increment frame number + * starting packet number is odd or even, accordingly increment frame number * to get first packet number as 0 * (pecific to gotthard, can vary between modules, hence defined here) */ - bool oddStartingPacket; + bool oddStartingPacket{true}; }; diff --git a/slsReceiverSoftware/src/ThreadObject.cpp b/slsReceiverSoftware/src/ThreadObject.cpp index e78b5e3ca..08d2a5a67 100755 --- a/slsReceiverSoftware/src/ThreadObject.cpp +++ b/slsReceiverSoftware/src/ThreadObject.cpp @@ -36,6 +36,17 @@ ThreadObject::~ThreadObject() { sem_destroy(&semaphore); } +bool ThreadObject::IsRunning() const{ + return runningFlag; +} + +void ThreadObject::StartRunning() { + runningFlag = true; +} + +void ThreadObject::StopRunning() { + runningFlag = false; +} void ThreadObject::RunningThread() { LOG(logINFOBLUE) << "Created [ " << type << "Thread " << index << ", Tid: " << syscall(SYS_gettid) << "]"; diff --git a/slsReceiverSoftware/src/ThreadObject.h b/slsReceiverSoftware/src/ThreadObject.h index cc9ffddf1..624dec1cf 100755 --- a/slsReceiverSoftware/src/ThreadObject.h +++ b/slsReceiverSoftware/src/ThreadObject.h @@ -21,7 +21,9 @@ class ThreadObject : private virtual slsDetectorDefs { public: ThreadObject(int threadIndex, std::string threadType); virtual ~ThreadObject(); - virtual bool IsRunning() = 0; + bool IsRunning() const; + void StartRunning(); + void StopRunning(); void Continue(); void SetThreadPriority(int priority); @@ -41,6 +43,7 @@ class ThreadObject : private virtual slsDetectorDefs { int index{0}; std::string type; std::atomic killThread{false}; + std::atomic runningFlag{false}; std::unique_ptr threadObject; sem_t semaphore; }; From c725a05ef84cd4912be9a8bd52241cc071046308 Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Thu, 16 Apr 2020 15:51:28 +0200 Subject: [PATCH 05/10] fix RH7 --- slsReceiverSoftware/src/Listener.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/slsReceiverSoftware/src/Listener.h b/slsReceiverSoftware/src/Listener.h index bd8d382a4..38fdd072e 100755 --- a/slsReceiverSoftware/src/Listener.h +++ b/slsReceiverSoftware/src/Listener.h @@ -12,13 +12,10 @@ #include #include #include "ThreadObject.h" +#include "UdpRxSocket.h" class GeneralData; class Fifo; -namespace sls{ - class UdpRxSocket; -} - class Listener : private virtual slsDetectorDefs, public ThreadObject { From a1a5a20845d51001b58604bbc4991bf13451024b Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Fri, 17 Apr 2020 09:35:38 +0200 Subject: [PATCH 06/10] from thread sanitizer --- slsReceiverSoftware/src/DataProcessor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/slsReceiverSoftware/src/DataProcessor.h b/slsReceiverSoftware/src/DataProcessor.h index 64111ebdb..10f5f2f5d 100755 --- a/slsReceiverSoftware/src/DataProcessor.h +++ b/slsReceiverSoftware/src/DataProcessor.h @@ -305,10 +305,10 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { //acquisition start /** Aquisition Started flag */ - bool startedFlag{false}; + std::atomic startedFlag{false}; /** Frame Number of First Frame */ - uint64_t firstIndex{0}; + std::atomic firstIndex{0}; //for statistics @@ -316,7 +316,7 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { uint64_t numFramesCaught{0}; /** Frame Number of latest processed frame number */ - uint64_t currentFrameIndex{0}; + std::atomic currentFrameIndex{0}; //call back /** From 095ced153cf1ff0c1c6d27f59fef9557e0cd312d Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Mon, 20 Apr 2020 14:31:10 +0200 Subject: [PATCH 07/10] removed need for pointer --- slsReceiverSoftware/src/ReceiverApp.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/slsReceiverSoftware/src/ReceiverApp.cpp b/slsReceiverSoftware/src/ReceiverApp.cpp index 89f1687cf..cbe4305ae 100755 --- a/slsReceiverSoftware/src/ReceiverApp.cpp +++ b/slsReceiverSoftware/src/ReceiverApp.cpp @@ -42,17 +42,14 @@ int main(int argc, char *argv[]) { LOG(logERROR) << "Could not set handler function for SIGPIPE"; } - std::unique_ptr receiver = nullptr; try { - receiver = sls::make_unique(argc, argv); + Receiver r(argc, argv); + LOG(logINFO) << "[ Press \'Ctrl+c\' to exit ]"; + sem_wait(&semaphore); + sem_destroy(&semaphore); } catch (...) { - LOG(logINFOBLUE) << "Exiting [ Tid: " << syscall(SYS_gettid) << " ]"; - throw; + //pass } - - LOG(logINFO) << "[ Press \'Ctrl+c\' to exit ]"; - sem_wait(&semaphore); - sem_destroy(&semaphore); LOG(logINFOBLUE) << "Exiting [ Tid: " << syscall(SYS_gettid) << " ]"; LOG(logINFO) << "Exiting Receiver"; return 0; From bc389f482547862f901c240ca55448a80108e8ae Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Mon, 20 Apr 2020 14:51:48 +0200 Subject: [PATCH 08/10] moved data members to top --- slsReceiverSoftware/src/ClientInterface.h | 29 ++++++++++------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/slsReceiverSoftware/src/ClientInterface.h b/slsReceiverSoftware/src/ClientInterface.h index f83b7cab9..3bc941578 100755 --- a/slsReceiverSoftware/src/ClientInterface.h +++ b/slsReceiverSoftware/src/ClientInterface.h @@ -10,8 +10,17 @@ class ServerInterface; #include class ClientInterface : private virtual slsDetectorDefs { - private: enum numberMode { DEC, HEX }; + detectorType myDetectorType; + std::unique_ptr server; + std::unique_ptr receiver; + std::unique_ptr tcpThread; + int ret{OK}; + int fnum{-1}; + int lockedByClient{0}; + int portNumber{0}; + std::atomic killTcpThread{false}; + public: virtual ~ClientInterface(); @@ -49,7 +58,6 @@ class ClientInterface : private virtual slsDetectorDefs { void verifyLock(); void verifyIdle(sls::ServerInterface &socket); - int exec_command(sls::ServerInterface &socket); int exit_server(sls::ServerInterface &socket); int lock_receiver(sls::ServerInterface &socket); @@ -144,7 +152,6 @@ class ClientInterface : private virtual slsDetectorDefs { int get_additional_json_parameter(sls::ServerInterface &socket); int get_progress(sls::ServerInterface &socket); - Implementation *impl() { if (receiver != nullptr) { return receiver.get(); @@ -154,19 +161,9 @@ class ClientInterface : private virtual slsDetectorDefs { } } - detectorType myDetectorType; - std::unique_ptr receiver{nullptr}; int (ClientInterface::*flist[NUM_REC_FUNCTIONS])( sls::ServerInterface &socket); - int ret{OK}; - int fnum{-1}; - int lockedByClient{0}; - int portNumber{0}; - std::atomic killTcpThread{false}; - std::unique_ptr tcpThread; - - - + //***callback parameters*** int (*startAcquisitionCallBack)(std::string, std::string, uint64_t, uint32_t, @@ -179,6 +176,6 @@ class ClientInterface : private virtual slsDetectorDefs { void *) = nullptr; void *pRawDataReady{nullptr}; - protected: - std::unique_ptr server{nullptr}; + + }; From 8afa11ed337b673e450ed74141afa25927a5b7e6 Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Mon, 20 Apr 2020 17:20:33 +0200 Subject: [PATCH 09/10] removed pointer to server socket --- slsReceiverSoftware/src/ClientInterface.cpp | 41 +++++++++------------ slsReceiverSoftware/src/ClientInterface.h | 5 ++- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/slsReceiverSoftware/src/ClientInterface.cpp b/slsReceiverSoftware/src/ClientInterface.cpp index c04d378bd..83c751a16 100755 --- a/slsReceiverSoftware/src/ClientInterface.cpp +++ b/slsReceiverSoftware/src/ClientInterface.cpp @@ -24,19 +24,16 @@ using Interface = sls::ServerInterface; ClientInterface::~ClientInterface() { killTcpThread = true; - // shut down tcp sockets - if (server.get() != nullptr) { - LOG(logINFO) << "Shutting down TCP Socket on port " << portNumber; - server->shutDownSocket(); - LOG(logDEBUG) << "TCP Socket closed on port " << portNumber; - } - // shut down tcp thread + LOG(logINFO) << "Shutting down TCP Socket on port " << portNumber; + server.shutdown(); + LOG(logDEBUG) << "TCP Socket closed on port " << portNumber; tcpThread->join(); } ClientInterface::ClientInterface(int portNumber) : myDetectorType(GOTTHARD), - portNumber(portNumber > 0 ? portNumber : DEFAULT_PORTNO + 2) { + portNumber(portNumber > 0 ? portNumber : DEFAULT_PORTNO + 2), + server(portNumber) { functionTable(); // start up tcp thread tcpThread = sls::make_unique(&ClientInterface::startTCPServer, this); @@ -73,11 +70,11 @@ void ClientInterface::startTCPServer() { LOG(logINFOBLUE) << "Created [ TCP server Tid: " << syscall(SYS_gettid) << "]"; LOG(logINFO) << "SLS Receiver starting TCP Server on port " << portNumber << '\n'; - server = sls::make_unique(portNumber); - while (true) { + // server = sls::make_unique(portNumber); + while (!killTcpThread) { LOG(logDEBUG1) << "Start accept loop"; try { - auto socket = server->accept(); + auto socket = server.accept(); try { verifyLock(); ret = decodeFunction(socket); @@ -95,10 +92,6 @@ void ClientInterface::startTCPServer() { } catch (const RuntimeError &e) { LOG(logERROR) << "Accept failed"; } - // destructor to kill this thread - if (killTcpThread) { - break; - } } if (receiver) { @@ -253,7 +246,7 @@ void ClientInterface::validate(T arg, T retval, const std::string& modename, } void ClientInterface::verifyLock() { - if (lockedByClient && server->getThisClient() != server->getLockedBy()) { + if (lockedByClient && server.getThisClient() != server.getLockedBy()) { throw sls::SocketError("Receiver locked\n"); } } @@ -299,10 +292,10 @@ int ClientInterface::lock_receiver(Interface &socket) { auto lock = socket.Receive(); LOG(logDEBUG1) << "Locking Server to " << lock; if (lock >= 0) { - if (!lockedByClient || (server->getLockedBy() == server->getThisClient())) { + if (!lockedByClient || (server.getLockedBy() == server.getThisClient())) { lockedByClient = lock; - lock ? server->setLockedBy(server->getThisClient()) - : server->setLockedBy(sls::IpAddr{}); + lock ? server.setLockedBy(server.getThisClient()) + : server.setLockedBy(sls::IpAddr{}); } else { throw RuntimeError("Receiver locked\n"); } @@ -311,7 +304,7 @@ int ClientInterface::lock_receiver(Interface &socket) { } int ClientInterface::get_last_client_ip(Interface &socket) { - return socket.sendResult(server->getLastClient()); + return socket.sendResult(server.getLastClient()); } int ClientInterface::set_port(Interface &socket) { @@ -321,9 +314,11 @@ int ClientInterface::set_port(Interface &socket) { " is too low (<1024)"); LOG(logINFO) << "TCP port set to " << p_number << std::endl; - auto new_server = sls::make_unique(p_number); - new_server->setLockedBy(server->getLockedBy()); - new_server->setLastClient(server->getThisClient()); + sls::ServerSocket new_server(p_number); + // auto new_server = sls::make_unique(p_number); + new_server.setLockedBy(server.getLockedBy()); + new_server.setLastClient(server.getThisClient()); + // server = std::move(new_server); server = std::move(new_server); socket.sendResult(p_number); return OK; diff --git a/slsReceiverSoftware/src/ClientInterface.h b/slsReceiverSoftware/src/ClientInterface.h index 3bc941578..76063b5ce 100755 --- a/slsReceiverSoftware/src/ClientInterface.h +++ b/slsReceiverSoftware/src/ClientInterface.h @@ -12,13 +12,14 @@ class ServerInterface; class ClientInterface : private virtual slsDetectorDefs { enum numberMode { DEC, HEX }; detectorType myDetectorType; - std::unique_ptr server; + int portNumber{0}; + sls::ServerSocket server; std::unique_ptr receiver; std::unique_ptr tcpThread; int ret{OK}; int fnum{-1}; int lockedByClient{0}; - int portNumber{0}; + std::atomic killTcpThread{false}; From 68f76e5356923c1f8aeffd24e55bab0f4a6ad54c Mon Sep 17 00:00:00 2001 From: Erik Frojdh Date: Mon, 20 Apr 2020 17:24:53 +0200 Subject: [PATCH 10/10] more like UdpRxSocket --- slsSupportLib/include/DataSocket.h | 5 +++-- slsSupportLib/src/DataSocket.cpp | 20 ++++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/slsSupportLib/include/DataSocket.h b/slsSupportLib/include/DataSocket.h index deaba27d8..470a72a13 100755 --- a/slsSupportLib/include/DataSocket.h +++ b/slsSupportLib/include/DataSocket.h @@ -21,7 +21,7 @@ class DataSocket { //No copy since the class manage the underlying socket DataSocket(const DataSocket &) = delete; DataSocket &operator=(DataSocket const &) = delete; - int getSocketId() const { return socketId_; } + int getSocketId() const { return sockfd_; } int Send(const void *buffer, size_t size); @@ -51,9 +51,10 @@ class DataSocket { int setReceiveTimeout(int us); void close(); void shutDownSocket(); + void shutdown(); private: - int socketId_ = -1; + int sockfd_ = -1; }; }; // namespace sls diff --git a/slsSupportLib/src/DataSocket.cpp b/slsSupportLib/src/DataSocket.cpp index abef23141..48af8a9a6 100755 --- a/slsSupportLib/src/DataSocket.cpp +++ b/slsSupportLib/src/DataSocket.cpp @@ -15,13 +15,13 @@ namespace sls { -DataSocket::DataSocket(int socketId) : socketId_(socketId) { +DataSocket::DataSocket(int socketId) : sockfd_(socketId) { int value = 1; - setsockopt(socketId_, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); + setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); } DataSocket::~DataSocket() { - if (socketId_ <= 0) { + if (sockfd_ <= 0) { return; } else { try { @@ -32,7 +32,7 @@ DataSocket::~DataSocket() { } void DataSocket::swap(DataSocket &other) noexcept { - std::swap(socketId_, other.socketId_); + std::swap(sockfd_, other.sockfd_); } DataSocket::DataSocket(DataSocket &&move) noexcept { move.swap(*this); } @@ -121,19 +121,23 @@ int DataSocket::setTimeOut(int t_seconds) { } void DataSocket::close() { - if (socketId_ > 0) { - if (::close(socketId_)) { + if (sockfd_ > 0) { + if (::close(sockfd_)) { throw SocketError("could not close socket"); } - socketId_ = -1; + sockfd_ = -1; } else { throw std::runtime_error("Socket ERROR: close called on bad socket\n"); } } void DataSocket::shutDownSocket() { - shutdown(getSocketId(), SHUT_RDWR); + ::shutdown(getSocketId(), SHUT_RDWR); close(); } +void DataSocket::shutdown(){ + ::shutdown(sockfd_, SHUT_RDWR); +} + } // namespace sls