From f0225c30deea9a2683b15ce61a3c099c0d884c90 Mon Sep 17 00:00:00 2001 From: Roy Gollub Date: Thu, 13 Mar 2025 16:53:33 +0100 Subject: [PATCH 1/5] # Release 1.0.0 ## New features - Supports FlowConfig to forward encoder data to other modules - Check if persistent data to load provides all relevant parameters. Otherwise add default values - Provide version of module via 'OnNewStatusModuleVersion' - Function 'getParameters' to provide PersistentData parameters - Check if features of module can be used on device and provide this via 'OnNewStatusModuleIsActive' event / 'getStatusModuleActive' function ## Improvements - Now compatible with CSK_Module_DigitalIOManager version >= 3.7.0 (if using DigitalIn port as encoder interface) - New UI design available (e.g. selectable via CSK_Module_PersistentData v4.1.0 or higher), see 'OnNewStatusCSKStyle' - 'loadParameters' returns its success - 'sendParameters' can control if sent data should be saved directly by CSK_Module_PersistentData - Added UI icon - Changed log level of some messages from 'info' to 'fine' --- CHANGELOG.md | 17 + .../assets/CSK_Module_Encoder/UI_sample.png | Bin 0 -> 40694 bytes .../pages/assets/legacy/settings.json | 8 +- .../CSK_Module_Encoder/CSK_Module_Encoder.css | 78 +- .../CSK_Module_Encoder.html | 1204 ++++++----- CSK_Module_Encoder/pages/src/converter.ts | 61 + CSK_Module_Encoder/pages/src/index.ts | 17 + CSK_Module_Encoder/project.mf.xml | 208 +- .../scripts/CSK_Module_Encoder.lua | 7 + .../Sensors/Encoder/Encoder_Controller.lua | 132 +- .../scripts/Sensors/Encoder/Encoder_Model.lua | 24 +- .../Sensors/Encoder/Encoder_Parameters.lua | 30 + .../Encoder/FlowConfig/Encoder_FlowConfig.lua | 18 + .../Encoder/FlowConfig/Encoder_OnNewData.lua | 67 + .../Sensors/Encoder/helper/checkAPIs.lua | 15 +- .../Encoder/helper/checkParameters.lua | 46 + .../scripts/Sensors/Encoder/helper/funcs.lua | 2 + README.md | 18 +- docu/CSK_Module_Encoder.html | 1860 ++++++++++++----- docu/media/UI_Screenshot.png | Bin 50682 -> 86035 bytes docu/media/src/UI_sample.pptx | Bin 0 -> 126421 bytes 21 files changed, 2581 insertions(+), 1231 deletions(-) create mode 100644 CSK_Module_Encoder/pages/assets/CSK_Module_Encoder/UI_sample.png create mode 100644 CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Parameters.lua create mode 100644 CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_FlowConfig.lua create mode 100644 CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_OnNewData.lua create mode 100644 CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkParameters.lua create mode 100644 docu/media/src/UI_sample.pptx diff --git a/CHANGELOG.md b/CHANGELOG.md index ff52c8d..23ac8e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog All notable changes to this project will be documented in this file. +## Release 1.0.0 + +### New features +- Supports FlowConfig to forward encoder data to other modules +- Check if persistent data to load provides all relevant parameters. Otherwise add default values +- Provide version of module via 'OnNewStatusModuleVersion' +- Function 'getParameters' to provide PersistentData parameters +- Check if features of module can be used on device and provide this via 'OnNewStatusModuleIsActive' event / 'getStatusModuleActive' function + +### Improvements +- Now compatible with CSK_Module_DigitalIOManager version >= 3.7.0 (if using DigitalIn port as encoder interface) +- New UI design available (e.g. selectable via CSK_Module_PersistentData v4.1.0 or higher), see 'OnNewStatusCSKStyle' +- 'loadParameters' returns its success +- 'sendParameters' can control if sent data should be saved directly by CSK_Module_PersistentData +- Added UI icon +- Changed log level of some messages from 'info' to 'fine' + ## Release 0.2.0 ### Improvements diff --git a/CSK_Module_Encoder/pages/assets/CSK_Module_Encoder/UI_sample.png b/CSK_Module_Encoder/pages/assets/CSK_Module_Encoder/UI_sample.png new file mode 100644 index 0000000000000000000000000000000000000000..8b2701e6f7c04e4eb411409ec3c46df10abe0311 GIT binary patch literal 40694 zcmb4qWmsE57bX>?K%qs87i*wcaWBO^xVux_U5c0D1Sddow@}=Q6Wkq&1ot4t+0bwI z+kN)OKF|JONGA8p%$)@-;d!Iua7nYiTJlWh5kI5E9b!7_=9N8kX(6 zG9;uINQ!c*;xuf6<%@Uz&Fe^>?OW#NgFWqDfuUuPx~uK2docUK(UFfB&w6%t$Hu95 zN^?hqCEAsu*%VcsggQw{wfzwc{T0({h_-GJskm-iK*%H z^YiGqge-TTKVb0wV7|1Oe-SI+=W1RE-QpudI z9<92Ur9=F~c=pZZ_4(iHQ0K1``}ZbA(|540-SU#9<(0gemg%~<*oMWY7Z)we4egTK{Or672VjGv6DtYvw@Zya5m}#w z%_DpQ@`_5c!dy}y-Aj{Wq8c`pbxr4Y55T-BRj0IVXXh`x{DTGGQhh8Vx?n{XRu_|# zzhU=5KHt8HD>~OL{|IuNOXIx1IalQpH?{Q`RaNgVjkz2enz67v1%d9)x0jmAu7`)W zyR(nrr&mEi>!;TO8cvgtjNA~L^R3ZqFE8Kx$+)EKbSEX3A2|;hysh7L0}QqL7xw7o z>})d!18gj_a`IX`yM64#LxUqWhWkCe3pb|50`dk*6GI>GmT*IQT85@Fx|iYE+16V2 z$#qkmHMw#QsiK+VKCUkNQ>}CR7b}B#xvf)2drP{e7H6w%MdJq^KJklf;DxSsv#-|I zU0u*Dmjrv2&wM~Rbvq6XM?H1L;Nak%!4XXX{@aSegS*Fm8QHLuMhl@3y??rLNqA^|8?0?*8EsJuB~_@yS{lE%;aL zK14`7GnSPUlMX|M`5?Z$a*)z;Mnb~se)@Zs$bdzHg!CRsT1;5gW8okJLl;+L;iLm& z%tm;b_N^@AG+KIRq1sE4r2(0p^~?nIWh)`HeoozwjA`%j>OLm7<>=DTB{QqK%M?ek zFwz|t%o$To#gxp6Xh1?1LMD^g;FajGNas)751Mu32Z2R}$e zeg5$*tB?O=>U0SIYI=%)QvX>W0zOuyd3gU$`1~kG@0qz?!O1%*SkWIy{8@fhPX%jq zeYnGSC_;?E>-`(61QvsplFWn9yydk)o!n}RTd>WKM4xafLjHMjPjltt>O^PS1d#Lg z=%d9eA5?^qHfiZ(O8sk0%)s1k(4wN8z{-yf^Y#iSuS!tH_GmpfK8TJEV1jJ#dh|)q zZ+F5`VFF5~vu&{VSUu5)HvVzd`Wey}1tmWUn>Q_5@9Gu|n`PL;wxu6NbHydSaei46 z`-v_mQvW!86tJq6c5ylXeFz?5Ye*bww|!&d5RtzR!zU{!AZy^*h2fM3(l%b@R)P2( z<^=4&RJD?3kEx5DMI}g4Q&W>sk-b?vQReVbKtkFH2MK-pRHZ7LOVWy|uB`QP{B{ec z`Dd`g2vcLK+tyONsIFwXu9M#?FG`aaodWEl<#vx+HX2LHmCl`rW>t$&i2W1Yp|ev6 zmwPp5vXfI^{&i|=IdfH3y?)x`;i z8}-`JNGTc|Qfhn79^mD9Zvg9zW1kQ^k!dpq^z{0S{dQ4LJ7O$AU4)#x0xcg*iX*Ml z)`FkOj&K$KsuOKvX~J^WF&dojwz8Lexg=Q|#iVDixf&BddVzJ`OBWw9bfveLQF@_b zZeU5$WDW~C()v;8y4;U)%~sXsh-lff?MLSj!RxkQ8+Rr6==GWNt-Z9h0eR(dfRH|G z9wHpaNW>^<(MYQ3tW)dO#Fb{42pmoFv;H)Jx5qORv+J2ulHAE~h`pE5?+-{G;&lIt zmIW-vq7c0UQ)~Qv#m2L;ZK`6+=lMVzx|JHYVj(5RIU4zidAR*$@he2cegGHeec*VPkDrb-ie*{k?ok z-$3C=B(X6Omh~~OzeQ{yEl!%NtLpd*-I?+SKYyQnb?@(i2r%&qzy7_t6u*dql2jIM zFG~FDzE1cgp4R;}S2`6LfLc0SB>PP)ghfg5D3Pukf9mizj`*t!^=DBr2wN9A+2w|& zb2N9hwU0_HNqqBVpHF{tZPGtEPwo>Hn%! zXa@;qsEJYvOL0=QzuA9UM}L ziS&*4(6q-Munv<$7ICx4;=K)i_5_qb^5@>KMC?C1(gr<2CIA~b75E_d1f+&sJf5M- zgik{_?}Vl{`o8`c--62PnfdYeiWa*8x9j*m%%VtoPwgzLH)|o;!pp$r@Fvm?i}N(n zPU$dh^qhI>`u)>w-K?4}SHSmC_;`~;&{Q}(4xJ%q-==!neDg&A9!@^|VLvW&Bj5}u z?59n8EFx-sR0auJTkCtPzbE|qp#w1sI|^W%g$NHrFqq-=fKFJd4{EyCna}NdIyA2l zlhAv-Xrbn1I^0${3)X82#!Mn4>wE<3)@>bl!#cA@$ao=J2Mn^A^RtaR)3kGc)EDi^ zt7(y2&S!?Hy~`ZtTdhablj6_Hq7yPbws@UFELNyxTiuV$ix1iV-FYvxeYr&R(Z{Kv zCb&=JN{9>qGO^gtWB$yfS7Tp!Sh`5qs@=4+&w0G8o#zNIZ_m*4vKua}pJ8nBTrX61 z%I<|8D*>Ym15JisYGLtT!^*|8$hCt859#&JmzbPF{wA#|Uv0uJ#s75%0J^E4xdBtsr?w<1Mbrgu%eQ=csKKe-CeWlb8s0W|{J>i{wrUCHZE-t)9-WFHf{eX@$tH z7w0Q&T{mE~5p=F9xTfD*Esu#AtoSodM^|%%jB@>b*IQ|=2@bvS`5W;N4)u+k*K^Lt zi5bUIa)3bUW9V1cQ#j6^{*ppQJ@7!s>j*ppubl&@EV()))GdVu=G3Vfc$hu-z;8;_TJjnvz~FGtUjkOZCJElc{$e`7$- z-Y)XGm*iUkaT@iHhXfS7g8 zkiNu#%RmYr_9PtP-%ur7$PdQ^{ve!RhJj(R{pXlOJWl}jzp?96dWPIna%KMNU)@u42;f0nU;)a=wbFDHT?&Pv{0y#X6UIc zDj4${2q1&v<648rw1(EEN_p&rY<`whWdYs+XHwUpe7yD|{z6YT76i(21ITX8$HeBm zu~5fs{kCSycc)Ez*{HHs!QQn% z35`Nwa4rE9{tg~hMKJo=RlN7I2pE|5>>H>9st^ux)`*<&Dl?(GP4kMu@s>Hr0Bo9~ z3_rtV63!99rq(LAz7IFQTm0hU*{}VT)rfo!GJ!!3LDwF7g{?gB+KBvXjOZLdKnl=j zNHJNFA=Uf#q1Y$pdqSjC5fS34f$v;+w9JI8f)FgaG0O5*Y4Ta-sy+Dzh{cAwKh#tA?)$#9#UZ8p?t^0e3P1#F+EzM`s z&;QLM0YK6_hH^HIpN`kZ2UEDJxPF^v-87m(Is|ZO!-)F@mBWz{ZIgaRuf1Ju(&j5pMaZ3pheH*vc~+pj2IauUXe> zd@vso2Zx3bW841*ws~Z?aPPUklAYnW^jjW3!tyH!AD6F-74 z@^?5C1{DRED-+MZS{R~d*i0}bW4B0ASA5-0IUg@YI>?mT&SrnlxRqZDyog93cX8XNFn`+`W;)x0Tu<=ACTN9T6az&2t;yaF7alb53 zzTQH2SQx4a8lc&CUlcP)DHR^0vzKabt#I%QBucUp^ip(EM`SQbr6nF#$ z;AU6s8J$7JS_@*izLW2mc7~Uh75wG79;V}jD~$1I!H|I{1v5}_Ns=zr=$ z!j`}fg3f=&OUeU7SL`HbV;VB%7#})_u60T{8!;xJLU%6&4YL})Hr0^LnsN)U-asdh zv*?}Swcq(+ysn&7IY4J-`s*~1vMR;fx0Eogx%yMN6I9 z)|?o5`4d%+h5ePA(@!&ZcjRMKd^pRoGlXut4KAZCSGarQcF`&(B*3~?2-PiCG2q(W zBEJV8wCB2IC-XkE?i|%|iUyp;j#?~Wb&ys%G&k$u@A%7p)g*lBI-|_;QvPfH#K?O~ zVw0A}#yW2!GaOzGk<4uz0b!KD4;*kIAA-pramz-`yu!9NN1T4dg`f;nu`X4Sx%43( z7u`K~9fch4X67b;ne~bMa$*!-qG1wiE*r&mhj4P@G_9P9ZhLxw!8Oi>Kp&u#cALZP zwNJD{f6K&{^nL6Gw?y?-;YNbS;!hIyfP4>vxQ!?@^UNJNHs`OIqoUA`hF?=Jcf5&G z7M75snRVXv2e?4=LJM>80h~V>MRRPRx3bOMLO+0{CwXAT>tGkQR0TJdeg><;TbDF~ z+R}R_TGTK%HsSliQ^L_vw5*E?33?D#f@?+@%?mujRAX!`O-9qb{lABSUO?G`Ci}M^Al?RoQymlsjPivi(*4I?lC~LQiX|&Mnh~ zZC3Wl(J}0r4;KCFo|QipEzWvg9w4%*TKZE$zL}~dCc@IT`aA^g?t0t&J&AeBB_iOD zC11bg)W}$VnR@#C`^wZjIW6SQ!dE3mg~h^v->Uqnbdk*}6~BQ=p*@?)l3Oq5S~u3> z%G4Je@{1kGZ8NRGN=D5z-jyjf?x#Cqx=2pYaDK@!y?GLQ0e6ga^ajp+b(b1K?^$75!x9ke?~aT*@=Aw#fC_lHkw)s}P^i*LzjpQRj%Jc)ITZ*7koiAJZm_=8fPEZh1`#3VY z`bLfxj7d3#0ytl=xYJ^Ug|^7{QhcC{!@`-;43Jx)1%&=&CFb;!G1${Gw=w0w_odk9 zyJiuzy}$~7N#^za5>*oK-1PDQKB?PGksMpx#<@fa_gZ-McymWf)EA%8BBjaL}vE}|y9!Q6zl*@NOYTujH zhQBxC@tI4=iAfIG;JZD6LwsRGy3qhT9>adzi2@aSEc)AjN!38+s)e&Vk254o0|3c} z06=^|_4h1QvH3Y)RO_SZQ0YyOAbY(sp-lXe9Q2zHq_BQu)K&VCnN5lilwE7J2S=o# zK)>}JJ}s9I!hz_2CDcC{s`b9A7!_5n41NGU5c7NX+|bo#I7$g;7O}beHZLsqjtTu> zHg-FpScUyYI(MVrHGMR%#p#3cSAiVCq-nU{i0|@z5+f4PGuCyH0oXR~**n$>Yl2nF zAr(Jw>zm(a$QAsQ4YhP^Bh+fmw$cKG_?)hv6W$ko$mJ2Jq+gMPly6`62x%!l1NT_SLuGk^x~A^tl}ZoE z8ldUF!L-pL0TXq_-vZG2&)j;sYcaSuM30OOZ%qYqMR5sYmOiJljzQadk~v3$z-3hw z)`J93%sEpHh+^IgwKS}BAk$$X)8Jh+XyRB+-QS2@Qz2I+zs?%zL)`}T;QB^mBj{qy zD70PiP-VaOS2g4l#0K)^*t*Lj<3OOQJz@u6XM6MyU)8=zNUP!1)vSKue9uhu+Q!$X zUi?ZjY}T9XmicCPSZyQGdP4LJ0zW)%xI$>0$0kQJpNDK)?$ymEmWz!`wsYdAnyg?y zS%oek^LQ40@uU+#E#SesG`YU!|GZ_l_K*F85j2LLXpo1R!AUZU{ay9_T&2$c`e7!; zd8+PhWQWtmFACR$W}I6`m&=zx6a?Efbzl=evQ#F(#+Pw7jW*U*@Sf&zd;Y|@I}ks< zK9fjt65omyRAEJY2=_hmxJ2kU|9*q5NO=Z>Ayy5+HN}6?n3nN2e#9dr4Fn~{1kOZx zI)}YVT$8zaA3OC!MREjaiLy?b^+>7q2J&iBskw@q~B zo)oMUS{ePSy$xctgeU1}2#c1*<7ocHdn$xuAk~ON-+cHdbNxr``hs|PNJ#%FkrWZL ziS%nO1KQ;MD|cJZgogC(zbbRgaRAEZoqYYvH1`RLdhbrwXHV9K1t-^z{ABo_adHh> z(A&fpA%Vk8PDi;H$dMx0^&T}iJYRky+y+Si%HtUmV1^tWwOkW@fZnFt@O3`9kZV5o z9wk|blgDZCx9)36v;<5b$~Qp2b+HzTWRKWbqd>EPpgqnny~F4XQp?!euT|)yc)y=L z|JO0!y9#1lPCc!95r5Zu`p?$1^0UxT%W*bVQtp2PE(pqQ#!?P@SJhupi1sxA{I!C8^^r zD}3~$nVALCStqc~+PNMt5JM4e;|AFQtu|a4CZ6kY##LCB6@_D#OR-Q+=-?NfR@=Mh zag43XDtAOJvr;%HMYTMiwpp$6L3Ils@HTMjoh4DS8$j@1A@M}kA+_THPDU0p00L18@)4IWZ} zN`ufBsPv@3H|cPL^e zNOXMv+JvEqBfvokn}{ILTo3aG z<$m+%ElGOV{5B?8HK=&gn`<(W!hw5^!&V!5b6TYRb@3MlvFCGy9)RPGPx_+C zfEP90(2r=K?H}$v`@H(`RxgPk$^MY z4>cOlzJHMq#vEd-yr93v-o@hh7e9a(>|x7~H`lIFHX2NATsAc(f*McQ0C=JBQSQN8 zAw@t0Ty`vzDFX-tdT(npkDc)#R;nM@$L-b|ri@GV6JeMM7y8g#-gyzO&}g&pDgosb zw#M=(c@;)y#&vj~;yT-AA^Uj1coLwFuaE)*Sc=8wW3)SlnEP z(343GoHNAf57HG&7k?pu4JDfbP%7m6vEw7~}=H<^f5Jso^vc4SV(h^#szry&Efezsk zm6G_`3Tg@8cW^ju? zZ8QUz7Mo{J(_i!K4Yzq-tFku}?O*@lR%v;zd!!oYtcRn5Rf^@My2&y+Umb0b-oIdI zcPg&dHb+Z^ZN-V;9Hq)Av@uXRhIT$b5`*yeHW zJ6^uNHlT>fy8oeJo%D|d#u96T z&hZOjS>iuLu7cf;PS2Ge8mk1Hbe@Zdd70MLt01w_o7aV3dCoTirS+h3M!MrIf`H$dj-f}dE4A&d)YuBZ4-5Xk%8fNJeMJOllB zTPYqQoEgR}a6wZqUQ>u%hAmV?a{?{H9`t7Dhc#)p9Mv9?Td};s59KP*s{+k#g636t zQAg<)alAT?LTQ!sA%{f5kW|^OH1hSI8$aVT628$z0=$z!1)qOh#s8)#Q1|j3#Cs!$ zh2z?zla$a{T~xzNjD$i>1cTk9#85@@aG;_Z_~2VWupXBpd<;4hhhh0St4K7jqY~b0 zb3`444btGTh(+z^#7%K8aO2=F*G9_4NOT7z6X)i$c;>tq7o!<|#@Qs11NnJK7IiGc zQOY5~rT#m`LWOekl<0E821gtv_llAHw^AB`WI+%1hMJc8vZNyt|Vdwxph0_x25$pg?;P znr{gJmd#Uo&*_pFz3q~OFbBwKcb(X$Lcz*a9NDlvOw7I}cT`^9lcQk**_ljH@O}sQ zC-uIa&t#(OwUzqs3(@47pcl`v9iJypqa->xoS*Je_7>*Dk8v6O%#Jb>V;@?tLz{KS zV9*h8uiqwJT_|)ck#um? z$2bH=$Ioy4jVU=b(@dh&{?+(7g5xCgcJL?D%;s1hRX3IPR@3Q^BJ1 zcjK(z7x!=PS^w@E9#4p}pX1UKwKRDmb+*8nEoLw9lA`z4o8{9NgA`cfe=xWFoQiTz z^_qX?UM1_}oxi?+-zUUCc&J4LO7yNCg^t{VBdK8u-eA)6mb{%?J_op}l;@#FUvDW4 zxpYJnv0;|U2O!aen3d;&fpSBFv>UL!#AO?!|BI2yA$8r$0bVh&^Rs-1 z561j~ep78!{tJ{6v9a{6a0L3m#l_`430~N>w=@5)Y`cKGLzv$B?7PDP$=hu?yafT} zaDD0b7&t&KCH$hH!CpB?wm5BxjS2w|F0Xlf=s=)+(w+!O?m!rUqjY}`TuDq-y369# zgA!xh4h2rEKY+(GURfVW1_kiKYYhgaGQR2Y$wCTqyUA}sdg8smp}8VFzOiPn z6v6~3>4Oj}v@;q%E_qDhcAp_JII?^wXy76^&&Njg`wKcEuz(~Y1BkIEwe+fp`Rd4a zl9$)A*H}wk%QHiQV8`3v;|=_MWxuz{K8gOao_R(p7gC7b3f>%3UA=nUNoV?}%@2BP zcG3htdPHLu2`z>l{kuF4Y@Sx$BCejq`DF?+A_YHo2+n|^>qOyS9895-iSCT7N zAQ(4zHT<>aWcP@imPPZzF-jkGK$NessI@Q#@9Vpusx|?Bx5T+RnuqaKnZi4Wz4pK? z*h|k>FEr~fdP=D8nkPq(HxFjo;cd5Z&3Jy9h79>M)BsHkRo5CHjdMUpP@E-1_029E zU5pfMxt!-4AQsxG{mklIZ*Ffl{zeg}@-99WjFarWVhrRc0M z{F{7OfXKp}9wo1WooG`P*8(7G@Qg_~8{UUww8>b#q$_>{}q8bi9}^NtUmS z{>f4es-;v6(Ev23>bSocwYRp#x_KzV8V?&!Dy?{x` z?NLsgXf~C;R$u#_VB*JU0=R{3HZmIWGZ@F1vQH(gZLxD8-b|7p@3M`N+R5rNWNS_K z(kCn4=Y~3<2RX|5==GN@&Z>`_%rXqE{ zs+UfIeAEg9on6Cbyo_2u9lzJ2`%o?y`uHAt;nW^7?Xh|GoSr|5K96Bp`N~z<54~A_ zJu{_OL7f}zUYRLEv>$Cnq+g_vLdLv0-uiu(z!+49TK8ETn?VZ2;Lj=N1bzY`ZcP(Yk<3R_y&U?VxnBYay+TV z0Fr85s*fH=P*$iF?7bO#o(~p6DPIh3B4Sk)o*ixT8D6PRIDNrM%kiat^S?ofurDFP_0gcR##+z7;kS~8A4Qjo}0PE(Q%eXm5(4uZohaG_6$rK}^ZV;(Lr1OXwsF_WGISU8ylEvYy z0s1@LPX6sxo}M)bPkUg011?s4_%=~eYiC6fh?Tu!Z{2C$+Q%Zrz`H}~f{XH9zeDI; zM4U^loat(Mz4Li?I!PFM|LHca+(`Sv6tf+^5hjcp+zLcwAP1)7uLr$!m{S`&tBaZ3 zoW9XrENWoBu-6Y~PlNWIaX7N^mJt)h{hYMzrwx4MZ7VykZ;YSo#T?_|9gB5>cU(0v zK;a*2cM@roG{N$LJz3M=(U!b72c~{8WiOxhp#LsxkE_OPcLn9z)hB#BZy1={8b~a` zacluXk{{K(MgPU52~{l<}Mc=mrHWmmD}kdcV#S14?gr z=OyXJ__VtFN}@hZL-kY{=m57gAn}T{3!c;LHa~|Et#?D zxt$akj#akayPz2lN+csdG6vdy*sqjdY}~ys+)!^JWLazW1oQX|9XTxv1qDB>QCIo1 zvcyWS0|Qbt!LJiFLC9a{a$_`)k~EiJZ(_j&+d9btxtXZH?!Gndp44pYk8E+<)Vr#S zgQ7Jbr}Xcl)77>4SAn&)Raf3>)BdJVQ@^)8f6+Do$Qth_c$^?0>nfM7K$9kDz<@6G zu-L`{|9B|hQb8)Hk5L2Tv#0>aLeZs5MrumB;zmxV*x31VP*nH%+@Or)Nizknp)_Ok z;aQrSHS@b%ZEI3M&5w1Actu~IfOlrxM77zI9wnJJ{HC3wgIsv|Tlj(l%^LGAjg;c@ zSeyq_>y)57oJT2I^oaVrk=Nwq9&(t?EIp1Hr{rxm#7u#N z%raO#Nk%(zr9&+xPSyHrX-B#UN1Z=em59KTl8zplzvRCA(-ui*Ca4OxMC7l`^n;2K zk^G8s!Bw5KLD6!|V%S~F|_(Pqab2(q>nR}62=-g(r@14 zQsVx+v4@K5+1c4O4D@_IWD|%ZBOqCR!Oc{YGjp^C0n3f%` zWY_U4tPo+s}S4Q7eflReutHUO`_$F0TGoM0j(3I-~5LOs3QS zj8;e<*|(hjr_HMdXLgwTz`op-r`$;{Vz`mwUK35ac71AE4&(JbCB)e>D00 z`=6cQ25MPP8US_@qz%EpF%d}tf{>RbNV>qk(C=qFaep;v<$Yp-eG>{`vL#SzoG2=FjiSlJiy=P@xriNxj@i%QM`Wv@sB+<*Gw?z zt5bbL!T5J)jI5oiXE!H9{qOPx(m=O)h?zw+twn3HGQmQx&_?WKFqRsX(A$EP1!amh-NYOrgDrSM zAFdmle1qdfZ{Wx44RsUj^e7Tr3BwHu>@X<5mRELutE3rD*yPO|?1ghy`+GbL?fjNd``;T-RkOT+<&G?;hHIneFZ3*VRNaa^JEzDZb+SA}&c-etUx6<}b=iGsJdS{+(CU3k3=t{MVbNhFCPr zbhP&4eFaXq4s6~Ry$x-C3}5Z2`R04gUAs*)7;9gD33fykSu9wA?nm|u!#(Js<0dX} zBS@c4Gk9?;KFIO>_e(+x7D?v0)pZ%R-m2JBq}#yY4X&P10;Y?96R5< zeLf70=ExNELGu!#Q0Y9om&B1`K2$g7II+Ykgn({kuQ#8Yr1sfyNC3`OU(q?hC%gPF zKVHA1^OVUr7=tvKeF^0;#sjbrJ))5z*1=?@K(VY%AP6)L=gAF-yF&xEiEJ`g6n&q% zl03hE<*`s{$_6BVzgf_*X=wxGx-M-iVCda8rI!!d7En32ejufRcNE2IU^gS#DRS#N%NCN)QFbGvuypl&VrD>*b##uw1@Qt7N?AFW?CF zOc_X|sTbx5fl5Zy>pFiMc`o7XBxfeeixJ7QF?U~?<()Rp@=v=~go}Y~TD)8bzG<;U zxg}Rx#W!L!V=y_IdT8^=E_7;5%;BTq)BgPxA__A+GYRNpUpuvIdA4@gFTWn@#>P1| zX&n0m)I|huj9lrqBJAU0{)E&SH-5pZs9K$sW|zqu6tuqjYDL(N_l(Z@rVG~oHyBFY z4gCo#c4D*T01STEg#r9k3pP^U;;vxu8{F-c*3Md4zKF-7mhqGVSof{yw5T8F3;Y1x za8d`hW8^(7)C)8YXp+U~ z-x@fYGI=M|ms+(_+Xo?#$Ac=P#%|Wwre-FE4?L`x_qDRKll`=F9aeV_>gd_+<5s2fYBlwu;Bho0$@1`iR-hqF^ZfGXXue3mcs$2cXcU zU6yr%n&xFqqd!Jt-|_QF;NM-4Hm(2F8VPV{6F!qkD_00wK(+^)h7Rg9hb?w@hB!6) zxbN!<@WrrySkkbV-ity$zNF_h{>ATOCsmnow=j}rK+)MxGa4;E@B*=hAkf;R$#=Zy zn{MZUv_T}VV?gPn29|T}f?I~ssb}LB#`zLkn!`K?rU@YwXTq(aBG}8ycfj;*@U0q#YE-0On=2?%?6@v zMS>o0)a+M`r%OyqGj)~>1`B;8TgPWdp@phz2^>F(g#x@_Ti-#h33p4iFBZZ|JXv-)A{QsxrN`bV&p19U-2#8} z?$#R;TMVcBR$+jVB?qUhct_hw$D$0N$Oh9NrOX|jS@fqBP>;Uyes9hrQ9vVobJPw{ zf5Vg|eTT>xJOXu$E=jN_ctkCr3KW-&Uid8Idh}g4QJLatlvFC(!cPFs@Lv%z^O+_4 zpz6V3giaB<3mg^*dUPC~rPOwx52!3!aK-6zxj3xL>N{>9QloHpsctk`p4rWP$Vzb)L@(7J&#j}&srs|fD~>uzyf24w81392Z!QGR4& zWBVv2(!VShm`AuC8P5@sB0-KU0VT_|k1}KBr;OFdh$;U< zCrBGa>&qQVt)0>tCnXB=!{1i1qTP8mS@|NQj_qFaGw-W$N_?0M=Ub+!lzI)27F9uV ze;0K`y!96Gzd~@x6IhiEk>_JenzNct)*vwt>@AuM60Lk^21@$Gz>Am9gJNG_O8u1T;1cOv9yKm4Izc5ce^6TedOtJe%*ybxYiNc{d`En%At zrZ=7EmrDZ4DHi__7s#>^%E!SI?&R+)tTZ=V(tKK4tMwy%b^M78twtY&%J^7pYOxwD zr$1~1^bp71(U2bwNoR3g2@HPGBi<-eT!o8Ev{u1>RdE&S`tsGK`=%m}p(DAhVW5%E zu!`c25w1@v;+Z#4K5Hyw1)+TosscJSRXW0Zx&5F+y>3oSKx9cfU7 zo#P2}{4S20_*&PwE33#&kNeI=WbB*-*PytB2Kp|QU)nxjLF^CEc!#O2{`y@8L%MaG zcOx-xzLxWBexVb1!e>%}anvHa^i>)g?9=hX-w2$s4)Yczl_z4a zJGK9G2B&awrZw_$HYHr_9wA;}Gf!O7A&V&|+sd(xeOfo2A+d-fH4-z92c}s(T59-x z>z^)N$UEek>=0i_=EZp%8H%$&;y!9IAY4$aIK=Q9Nc5@`*IfCt8=Hn|mWuxP6*`7( ztllaWo=RAToUUO5OtSa$_t*C#s|gWk1%K8AOet$LW&GN>=FC8Lbcv(pNh$yg4Jbu7yMvdMa2V*84cHs8 zmDXcE_aaPk51c8aVBt1U5L;}ds*z&RNku!383 zfhX04ZP1|C^CW}|xJ4502w!U>u}SlR>^yI@J`!=wW`|&OO&&q%U6f=rk9xZNmY46s zUg|_9Tz`xLVYE+`S-mZlu(S|)jSt|D+peUGT=GpeC&(-xKbU?g7EE;14dj}2bsDON zVL#{`I#GJ=RMZuZA0$HjYM+sfKm`nOVzE`Nt>>UZ!B@uqSQ_x2o`t|dQJ*jd@x|*J z;7^~ZCDPZ?tO}E@EAKMEEQ(y9MvVM2t^%gk)abI1nAEQX(+Qw*kS?5;5`r}WeJsqY z8m?h&8ik5O6Uu)uP8UhdY0nkRFw>Y!dJntsKh#s|zxsuXS+@Gdh~dmASIveyD|;AV zLgp5im2=Q-PSDWcXdFz-A5t!)S<)OFvrkciZFvTr`W%`s7%fepLj96Um5(|(iSJ+l zNN^36`l>3tmF^#oOQ`kPFOoZow7-3hIr7$nGQFCzf?1-!X?9494p*^~@v$zJ)Y;|R z$F9l<>4FVCUd@U&&E@LD&z(YYBm*klb2l-m)j1eTeFsSVpSRP>Op;`@M$FXt-Xg> z%_nNfk+)7I_*dnx=bnwWbk((bHK>!2@+xg;x*EkA-!ic^n)KC($jWWJf-#}GKc6XX zDT5m?17uT-u?o;YRc(65LA<^y8_hMTkqWaKuerQi&CT=(LqSYMuNf$O|DRj{%!y}Q zMq-|*w(k9Jso1jsAEiLpR?W|Y0x=BVDs3dRi}31$%A@-y{RGPgUdM%DYOCbEmS`#> zV2gc8JzPU5CR<)XqwmC7KEk&Q(nNRVP%iJa%c-T&HxAQSRVLyx-!QX%s5_}td%G>p zwwx+8UEMVs%*vdwbT~*a`_?E$D`Y?2;!Sa^@RNFOwb^Ed;vcZ7A9hsT%O~#3RMIt4 zs6*yWVX#rd}us%adX_fA8 z_G&NaJ`=}tCYc*w-WXgn(gLibXFn%dbwbW4Ab~#jatgY5?cPrrlp)F5U)jNH{QB2| z?@1kA>aOtz1?7IE$795 zNKR$LFLOgVF!ai>N6I$XMvFH6&#c zpm9lgengLG3@81x@o5eX)1DPEhvWyJ?w;K$wSlng4pE(Su92yaqV2gd)Qt&U@2Zfb zsLU)M_K$qHq{0*;}Jh;;Ykc-H!Rxj}@XX;_zn z-n$+~8y!b$TRU@r=6-!XAb5J7bi05>)c*2(HjwBu)JY85n%$}P&0mbkuRaA)zCgxS zc(EvwMr6s(RByPX!~uQ58AHVe!>U8Oj0B#Bl$NA@L>|Zj2kAN5Bf|;2f{xzTasc^g0 z`5RE*Fx2!gZ}h8kfII!XJ!BawC$5iv1$xk#&7G&?p-nW-qH-Mg{nO67=&$d9R~O#r z@bo`7E#KBJu(tgVN}G?Ry5%t!bGMGge=H0i8}PYYI1yj^D+^rRe7@UyzReqWU$Pb8 z=@VnGJ!_vCi5CA|jME))cDpE;Ko!v8UD}sk^in<$s5Hijk;}uRVJt^EkoFG0jh zd~4P6kEiP=!Ohp%w>zF%d5~tD{Sd8){rTPc;jTegCD?5oW?DLwYjr^Z8<}R7Z`4}2YR}q*R(2JM zW|%#zSY|RRSK^O_tZ^(?K>m2AQoC0?a{_St(3bu9;r5UkRB?2dLWJNkd2;#CLo za`;A{_VaiLvlqE1rnc2Y|I+-An-wOXwbDu%GAK}xlhaLiBR^vUUgZWrg#DQ$ln5r@ zIH(>|zaeF3@8`D3Mfzu!@>t{qX+1HLEtLD~-|=}rCCr(TFW;s~W@5<9ZEJhS(^M=v zS((iU{ZVsG9;Dltvm-xkj!9z*Xli4%_L${IyA$y4jOX|&4zGM1a>DVu;;K0cY9)st zV!T)Z-wGs(DVM%uyuuR9*v?^?(Uq5qU*hJW3$VEaBWDVVT)vbV+q@CE1(wLe0lond zAE~4WYCj7WM#p|aO&p2yafEG~X9Z`I(_s(enNIT&RPs-~|GRFz1hVe*AzN9}hM~Gv z36AloCs4~R5#uYA)Y08L*9NsX3(M#R^5=17EGK}3jzPL-)Zv7S;K%*O{&1~kE zu_K8TR%Ob%B^+>gg6iM0_}KvKhItUC%jcpk0x#FeKB%pF>GLR$%umOH95b4S5rx=c zixAps0R&dQKtynAz7cuXrGM5Zw<7g<(|^y!6 zD~S}}>lgW};z&?hE5AnlYc+OWgYJT|_pS@7E8jn>_ileL`tdn}4_pBML}Zh**+uIb zq=C#>{5U+2?em%m&yP8v!F!nZ-HN?|62MYW#1g1xmA`He=K`~d1)iKts2yHBZ9@Ut z(K%D!y&O3hXs&EEjy;hQA++;1Y#r&_lw!InX%k1^4Oyo=mg`HFDR0Ee|1g(={j7bs&jlFvBMm!o4>X7fBdW>`3nVdeVSuS+>;d|3hrLNcC zrZ?GV$62CNZe_>>54PNs4X-!>0;wD69MW3|ZL_I`gdnuzJg=OXS%wiVufN^^VM>QT z?>-PD{@?1O2}ApLFw_)k0{36a4c-6U=5@ReoL28P7A3!>eXVAyUECTV@u4`emJ6u6 z|L2COAp6N^7!T?qkHhV56OVYneGVU0uAzsO)))}+*#%G zacJNPd%&am1sjQMF*0GWs%^=QIp4ZY?(S=&q{plM8&z3ieB|cpQ41kcGmC@w8S*OC z?72V-XJO%%zhQ{fQ&6wf5HNEMKD&0> z{&LxqwauR2B%=diXqavi7Pgle&pi#)sfDoV9e+v4HJs{V=BJk(>XORw)B32^W$WL* zrN&fsu~u+@*S%_U5z?3$dkni*bEL}|A(Mu8K`On1S6Juv6~+($UMtt=PTqJa3B{yF zB^IX0Z+-creCPokGnPAiUAGl@+mM`Mn(o`Lk>>YvYKnbB47U+gTU>!v&gha!rV`rezrmn@IPJ_fmMnRO;8rJ=?A_ z5p$xch~<)O^2Q0fS71ya9Uxa*Hl+R$`Sw9JRta@XOU_8a z!M67G29o&!L>zwQ1E#%E`A3&?YE?ts&K^ zslN1E#F||k_kbQOpm>1Blot#gB~_OCsUH!Ts`>TVhVjFpS2y(fjOxML^=aVMQi!>q z{hFX=reQV)lks4WmTGdP=3+qm_e%BE zcj@5rE^Dx@VdV0ns((+Jt6Ov$ZW?Ax%ph+c*ULGgi|k@#?S_6!s_bsJ*fHDvl>sR} z6}|J%+5>mR4coHVW((?vOPQzMP@7vJkuZp%K9eWJiR=pB_)?BzC>>3RPx9wmR~1~b zrZAK1#57ZU8`}`3w}mH!+ZyPC@!j7~ZgqHFsPUAgisVSkVg}Ni2J|4|w(ZUy=FLAt zIL!0ajr~~?iQu=)UnFiB@+)n2)}UI`DlvbtOaJm2UiQ9nuU4d3PgNspBje>v2*30d z68kE+;@K>Ewx6p1a5_Lnb?)*jDx=O|`63Ngt$fZNxDGb&urC?~bNVUd@x01=HfV*q z2hANiO!#lcwbAdUx7f$iU%IJyzVoB=68RPJ_a<}`5+)(o_n}GVBXxzd>$0`+m!EDs z#14uViocUrznpU-x^#!45zrl463uu^FI!~$%WiLa-n1wb*m$i$sbUGU3J3! zt3d4|fBVf5gb@@%$FU^vCe{I^ni7T1?0`4u>_%%jLffJ)W)F;l%O3kksrV-irY}}> zTX~KO(%dAofAq`GOn@%RDfbTX{WcS^Ja$Of?^z+380Ti|R$~QlgBaT$r~AD#V{@_x z`KDd3TK3CzGMvDR2cp}=3`dC6p;@~xX%R?JU;$VH5&sYnRU2*YIWgW$=(i*@>%kjn z9ngL~MHm<5Q;CzYRgjV&u)r>k z#7m`pMul;Z%(JLHmIj39XqQO;Wm|->munB%od7J^tG#CLYQcXhPo(LO#W;{WZ;hm5*z9`-;mbsVnGFOf0XWWC6705}j?%h$Zv)bM`F ztq5{4-d9lQabau5WmrdM4fky{u^7Bz8GHUd zuSJ$TwpC`*k=zDxQ_Q%9x!kihjV2~us7-04;<|x6pZ2$_j%war{InbpZaCi`&E&KpuNzXHd&RX zd*cn{xEl4n&t**OEG){$x-U<%a>wFh&^*gMK;%X>6HUM&Hm2J~PbYy$fUq@2?mTZ~ zYOqvfAhBt}f)wNBFL!%~?)EuzW#dx-`)Mn~2e!QkPpGolr$1@Jlr*>z4#?Od0aAwA zdNnzb$)0;9fW%e&&<6xV+@y8ox&_cX#~`crOoq2L=4Z6VSICqsgCBabW`&fIff1Jv zl^8Fx#vrV~i^s*a|Ln=FWbc8;@f?Lw;LM&HYK!jhCN~z%C`=Iz{G8+e-6UrK#_%2T zjI;uMyZa$V;~!P^)Pt;`525EV6*2jZc_wjlOORw@dD5}RiCHGYEa}qN9?-|`lkX@C zHBVy4Q@w$bJI2?K}Xnoj`@9ESJ}bK3iWkXT3h z>=nL+*XnNfgac3j@7Nh1uT4*b`^dtCZvTHHUic{eK*j^gGozM7@u|78I%=4pPO*NJ z{$lMIt$eXR_xb6!khZDCJ)~^Y=MId6qHdq$nvQB0S0j@UWPc%leCXt4{eM6;{zPS2O8J5ZymM{s)gIK0NETLOc|VToXZCm?2cRHHrVHFj>-~-_C#B?)1n?$mUBMydTic2<6uh>#?NU$W48Vz z0Hhgxs~|NQ!D4D)6eBfQN38OCk;TBy*=9!=O;0AKfXVbKrfD3%xCdHGnVyX3)R*QAy{NA3}LYH$rWUcbMpGQQQo66hrNr;nDXO8cBOCM zW?vy*g@(9T(q>Uf9O-fG;_;&TAU9-3a|gPxcMJ1ceo*jH&gnjb|M_lJI+nRRYP6Ge z)&-?&SVYnkn5)FCfoc}^l+EN_hoALom%|?_&#^}4&o#x zz*Ww4GEOa{R4JIWvt}^E;d(x$z==TRsdd+~Br!z;5Bt0u7Z^h=>;MFM+!F~mc-54~ zDP>1y5O+%RPj)X%Nj!U9@`UUQ=#J1atRb1HLT^S@;kb^!;$H6ga;8rzPgnP;2Nhqp zT8GwJh;EdZ z{qs*VP2I^4Qz$`wsnML1xGFZYipj`vzD8iP3I=Re(Q}xmBgf|-3%{u7s6|&bI?a7< z{F_t8(r8}_@Hmc;(^y?FY)m2CkyhD!L3eLILCbGPouv^|LxO?V0MYgf_Y$VW>ybUAR7VeBs=dUn_Fucm=Q@vk?o^Q&vSu05p zE;aIU0bF!^@-$=TL!t<^t}&x}4(Koz`kb!MqVz5Nt>299(t8J)j3l%E-$uNxtF_cz zdGw_lu)e{WDwHiN?&L0+XOVwLx+5mYURb0hk}%v7_tUR~&Ock6F$UC#H+$xinMJ!w z{tdvU>q8o`C-a&oH?|)jVu9go>VB#Ll4dzjWHwP_E>VN1W^1?kWj`@dOWrJ z9()vB)Y@kG$%AmrLRl8DnmL9uHR){lt%HW<_5buQ0{546a=UkFUFUZ%Cb%38 zRofOZoG%}guPE4KK|_|Mq;*(BLHx1yU509>r4-_4GlXTF&P^W#1yGT*;yp;>Joacd z%fE~T@>S)0c9c{6BogKs-ehADR-!Un+iOO#@x0=d1GHID^jKO*ZZ6=#tdxJ*;K}NS=>pEaYMQrJkrJq|W zKu9}=+|n1}rro5v42C@nK&|ID^^!324}m%#Go(4s%BtB5Cz=()%i^c%E9?*ugP?-D17gYA&z;hPBXgZy>iy3 zoGTrY;W&MEldjvR7q5$6&_Sp(ap>b@ zul{ClzBr3aq%;Mty-eyWzE<$xq_kWtMXe#|ox|}D|L8YT-vhee42J{3YK%e{;PjUe z!{j~;JA=16G|Bf$^|Z-rDsfi27_k8n_`SucG z3bg~ulp7v>&2&VlES2HxV8^)IsXi8wIa<_#%rI~1Xk7W!$RNC*k-zxweFce?TiLm} z_g@+tFh4t5Hulaz8*P)hFdC@RM~HRtRt~)!Z6UfT&(vAZOvq5QUN%K1U?p_A1D=VFqZl2YTktqop+ZnlDXl~8H z>?w>hW1lEy`0MT%Xc!O~-Tu#areNgU5M-QK(2-|S<*HryF5lU(qJU3hEAO^5 z1~B6To`FwL6_9V^B>62NUl`A|bi)3b{j-zi|B_jCYV^~qaLHO_^BPiGD~nrcr5WhC zJ6--Foh&!!Iv&5gpbarhG|UnpAvzKpGu$z=U3j}1P!cYh)&1{gq-FQ@dDf7$MO`Q9 z$iXz8dg&oij{e98FaL{4d>6i&`8^c~vEnX0grz$!<|ZclrFr0Q_!2Cv!O=AEG*)50 zJ!`PPpUpAtimpEL$L%zpXMK#KeYRmh6c49W&2^HbVk#Ytrwk33Xh0HnDAQz!ju!Yp zq{F2AoU-5D^Xh5#Wp0>olbP%M=RM_fqHh1fhu==F_7LZKxeNPjLP%B{H;vL)=rt*o z5QHhVJcT4NFURA@4MJB;ScqkJ6td8zM0#tIJQq6WI9}yI#<#m#U!N{5xL3TeCu7+$ z5KJWM94khxUecNUN5pQoq8;+!)pHo$Z^q1Xym7GR zNGjyZG}x;rtK(nH^c!f7O|Lax`@3Q5xhgqIVZJiA?DD7Br7a7;ef$n5Rq@^nG4IzY zb&Qii&fPdHCBoy=tm8d^EYV{D#I&Ccq)J|W_FY7e0oF?8lKgp`A+yt<#UbtGXka2dX0;SZh*$TLOPUYNDgZw0GMMyEwO7R4&c%xCi_F zSJ0R201C(JBv$il#0tLFu3akDaM_9p*GN}}pVfu_e*&B+;7J3JqZMWE^B|HG5{MNF zQl1@0qW4KR$`0*`XTv0OMLu$ADHGydit0JI!Y)Ed>ej|yDChi4<5@Y2H<-I0Y^Nr; z@UUR#E{9yrJbPyaT=QDcVu%4)(?Tbaf0@r29k^RhmnM}1Z>HM|WxBJ#5SS#QTUgl^ zR8rWLS*PfTa~UtzR;$)#8nTD*H!#^#Ga$ zF(-$-O;ltIMRr*LLytcXI{dpj&OJBG#>e)`T5OQsSApmz<9Vz5CX?0f&+|K{hU^u1 zr!uAH<)HkpnxXkGeZMVa=Yk|*%TLVD%Ozs|*WW4H@oVC(uOdO&)g|Wl2A-zLa~r}y zmzA;71(5cEh@OavL^T#{Q_*ojL@j5@f$xH0!SOgqtaudCly%E)b-V*jk9pe1o~pU4 zc+a2De4(mv3EPfzPz^sgJvfEG+0>sIeI+t!Wbj){-@Mg4NBk;$34lT|%hUqFYM0Sc zv#5_kz_3M=TAZ={b?;smOw`P@3#RC#TMp^JTJnFjd~(vsBm&1v`bD2aUVOnCebAdO zmY3pl&zeU*xgmjNW4YYeEpUb?e>o%BSZNfNcb<7Mu%N5D1uoUc^a1CHMa_HGg@Y{G zwS@B0xc`;V37wPX9RR$Vk3x8T9CosH?~y|j_L|$;z)(RS!*(epfbBIqQMjd5BnAar z0=E^zlJ5%o%`2L~&o(EYVV#n|PrA#thF^hrbQfF@l4O)uIl3PBnwHpjMWoy#&z1s- zmTxpdC^u3{yDgkH+9`$ppIm^Kx!2HMzghnQ^I2s1NimZ`*Q!E94m)ki_?T~onWO#8 zJ|Qgqw*5~0*k?;lLk6=J`IZ`&e}K*wlp1A?Kbl6iW-bpMOwa!Gi!vOfEB56r$L}9R zJu)Ge#QHC9*uTspX~U0)M|8g+7CeEO<>d~*jPhimI|I(vG})$(nKsgmY|eyzY)E{{ z`PzwRu9Imb%s<7u6Glpen6*5PYP(~lEyD6m)`xVhIof{^aGrhIq>;a4zYRwunumYBGDO^Y zY%GMn9LL$wgiPJ~4kQ1~xeD20UU4>;7qUhy?K-X@I7GWj;CJEUEAQns#GEesfh9_Z z2@aN3C1MY|J#mXA{K#4-?F~^lblwOPba?QN-P8$vLlO;}PH4r#YB2X7Y{twbTh1wh z5K`IxKu5Bb%l}cg{giNYK|N^2B$O-qO&MxS$(=*p>*y^gBqa2$bgRE_E7suB!Lw=i ztLI=dar*wBZ6b+=r(hJSw&0dop?k&KaY|4LY-o8iD8%fU$Ub;yZ_yWY{4TUD{)%q;~w zlS24k*E4#lvfPwNGqwbE={-SVj6sVpb+9HBvNLr}i{pt3-&bL#l~UF;RsJ# zIC)tG+f)T~2%LpH52zcNhIgTzQFc|+pb(r0eVgK$y|gY@KsaV8Il#+Q%Rs$s!uDsr zqD%R?$WtpQda|M>DlLFdWCmaJIVb`)#*}>IMF9R(8OomVK48RR(`x7gffbt-Q0hF; zuMw820yxcn8*GXf(KISp5c?U%iD1G?1Elkr*h#LL)ghQbo}YLC`cfIH0+)ddDHLjq zj|inmLU*aK>hF1zhirE7QWbvs;vPU>I-@$}i0s_NIUI;;H&=_1rAh6rN(7A36iS$k zMd4Tg3>PE;_!;GQ@S>q=Nz1f9D4or+#|1aS+b|TXU$2rpLvRR&y>MZm zDV$RghP5mQZU4}zH)Lf0+W1-6(-`ySn+EFuR&0EHO&%;8cU{T{lAEG0veo(=w&S9! z=o=Z7c@cUqFMvyber*CxL!JYKx>PI~Ez_4WnbA=7yFSEt_UOn{H>$CEmEMFL^t8Yr zvJ*-9Xq8PZ@> z8DmGc**aLQ-04OsL<;$P zKApv?pj-a!D*!vayb=|_KH3n!P-WF$k+dm~I{PnBr>D`+QFY>gvs65% z=Nn|j_?9|z=Df57IM)ocx#dn7)wd?-Orh;_?+FF`$%y8E5y8>h?mz4? zZvEwY{R$vNlbJ*KJ5daSEgF>>R>>Y)gE45MYAG}|0WgK?mr7*3|MI~~Qlye88JC&R zj`3vT+HcfC^+De7eSeLr*ux!uV_i8$^H#$(^Y3&170#oFZhU32SVcwN>sMPvZ*Mry zU*WH1|22i?&~eu=zarGp6fn?c+@rKc2Nzv{QcvrjGn6eL_Nw<;=|caKhM;Sdeu_rJ zYb5&8LK4`vZ>oQd?Pr?)anRD!w{ki3#{VB0Q8`KNZ6l?orp z601=j`{+z5IAsFeN~lsr9M5!Z&2Yp>Mx6kcmg)-3Vw8Efn$JNl zb~>Zd35LG%?grNrPjO-=DtZOyY#5AE$c!@B{LetP|0;m(IcIYmACeIHC_<6 z^9gEQ3ar{NqaA50VuM9sU0bfbAY#U~<$ESBRn!Xp;nzcLxbIf76#Oe24zIxm6C_Vw zqtq~@&>0YJpFhGc37oQR1+Tvm^e{z!Luqu%WkRPcwiu*c8|JoPZf3g@bu3U#09k&l zN3)6R5syxBjS8Q2-~gT5jI@$`>7DF%L2A%BPja(T8neCX|B}b*6(+#Ka)VE~-J8*F z<2Urap!_Pzi**WX6-F;W#!XacaRkeXITLI8elVjlibe9+4#dI@S;@Y&PSEgD&{arg z=mpQNv;&57MIm`Xmj5F1LXb@X4t=`}sij^4bD>N3T>MHtAiosb2%~Kob5BL!&2MbO zL7)~x7%Vs1wc_6y{&|SU7MoJuQJczaLG0$r4dAH7=rqiG^BW;=QvlOlma9Z?C5m#7uS zZXSho;UEr_Iq(s%4wAK)o4L_0F7AvX!|u$vd%3Bz@R803GqHF%=C)K`mzBc31$~;B zT%GSOci+fMkFcvm}=+s+xsVf9aX@aHndj#4O6Ee4$liyZ06CFU(wk4s$2 z%wx?87;x810p~jQ(6O+vJl&|P>~h+`u>e!eVrLA{;J`%X z=Laq7mp+r_rZ>vB{*9;F!$NwPHeEf7Z#vJXaumdL1RORqW>&t`+;##EcE2h1RSx1g z-FUxbTE6dB&SM`=7qNkzi=Wge-LKyh$G=6dZoA$%*ch&1y>LC6`j%mG(3!%L%u$*Z0~ib_*@7l;EGd z0ONP*UOrc3jU;IefETZqpA8^Mw7m|;%|S{$JXg{4NOd}ghqMR7+gUk7FFmdR=^Jd+I=($qHL7 zo-Uo9D-Cn0G|6z%nZJ{Mi;sU6jM}CzXp>=-<^SHjyc_zU*C|XslNs%{M*~T725fx_ z{E_Y-&y_*G7*xu6rJW68GA97X5XkcBO*TzutxBy%e!?{=$9J1wT8-h*&t%GYDB$?; zQTML5HX+mKVCs~`4ghxQ|3$dCHJ@cXYjead4zC`HSe&!}AQ6mG>Q_+MF!!2$ z(8QW3)HIFaM`aH^U;#oq7gLk#)t&E%(j{}61&aOmM(#TC(nza+p`p6fEs-(d7ORaY zK%@PwC!iy8Y9@#c#sg*;>FTcs(j8%?0^FAs;|q!`%PGuSGO1B={v9q@V#~cEo-wOpzxvKzaP#XA3y7A6`HAFLS*_X9u<)76$bRmiGq#Rrg^dJNg!nDr zoo8P)|ED?7N@P+GU!f&mS3Ll3vFwr`utC?Es~tsv9KwQ^|a z{8K9Y|84QtR3o@rFW=gTk{#E}6CUiZ`__NY5y<|EpiYrv<3>frB;G-?E-D0*GJsdt z=clydP)&r~Ugbr^X5za|?|OIR4^lWRLXy+gJIu=oBErK0Zi z^0&Y_7#6fJ>&+MT_5~~}&4Dokl(uLmy7zEhPC{|ORS3`aH^};}C{LF{&lWSL^lnBX z`sh;)#L%8v;dd6^b>ff&!f6~b+%7@1bjQV<*-@^acO~TcGu(F~%aRT0NM|5Kt`yCy zZ349_Av>7GhTEoih`f8dEZo+MT$ubicsMC^SVtOXrI>n0Z4x!NVOX(vHfHqT4e?zs zThq7kiJ843pg{>h{&yrZXTs+L@5hjAbigkl@_D}dOlFM*gz#30ry@T%jAF0dw)VI# z>tnvd4~uvO?^k(Fe(lW>z>~WM?VInFU+h3}SfgF(WYeM?In)W<0y($Ytv`a~a&hAJ zpj6Z;%G!5sKWu7O>Hq`-$YYDMfc`*I$O@~4BISI+!?387TR^;8(|kR$$&&=ks)Pv< z;Ua`T2TR2bZI(DufO~PYE=;nY!D=|Kfa+NT`=j!E)_E>_)-aJ-WHn~1se=jO=IFJcJ#w2C_w)3akY*-n1kNDuR(!U9XQz&jcZR_4K zz#(w&=FrVKQbDneaXR!CKc308lr>vRw1kCBuX5>isZkhdZjNHZs zk8Fg(a+3w**RdvpQOahM`+tBxXF?!q$tEQ;9~P{|jz5(Y9r3nYgS6~a5sQGt39ER) z>3IwR3Ruav1YTFng2Sgv%t#nfQMwpQbc=rX7%(uN*nk6bx<;Ysb-D%tzba}F%B&DE z`E9dS=U}I>v%LTm3A#|@S(w(*Wi6)BaW-yNBIp0BW}<NUS_B0FL-o2fIKw+#;IaBSZlu6ohSe($CZR(I6saDq46#1;;{=IL?#xA6wVhX3B0nIV?rObO;>oJ=5 z=%nx4XhuDfy6#(l@9)5#2J;NHCeNjC)6c-`0~4qXa`x9op3m1B{PKNz%T{0AnD4&f zs#$Lt_oT=!W#Rz<;0irg;`s9D5J5S=xvcNvrj`bZ5M1*yog<6_r^!WeF`mRl( zs=3)WW6i0M?u9`fb3F3P;Nju=%5fxz(SE(R%e?}}=-gu2Sa6zE9;}w{R(c%wPVA=T zWsZ=x-vT17&V&_o-eH}V$-9h9`#-p$bT56a3(|0*I68pEJvup2j zw8HX|UBzoUQR&fx_M05iV>=Gh$D#XhZFg}qyY19ylsvD_*}5oKeXPYZ7Iq!Zhp@Tt zg8P>DI=7Hby`=4J;cHna>ztg|Ow;#3X(7pla=jMg!>>pE8Ei^%75ipRWMk79cMEVr zB}ts#m_kEAv&3+WX9>8e?81XvE{dbSEsi^yh?Sg<+pS4B5d=6JVjh!)Rh=9XQ)6Ys-)A>Pu>sfn^xbclA~ae1nvY+P?5y*ZR?0WRl`HW0fkm4!Ne5( z>RIn0VEl_jQ)nNkB+|Vunsx59=nUg%5CpOmO6J|u|ACUVCMUZzdD7D=lt10wu^UnB zqsXFwzx#W?4j7r5c{Mm>2mv6-)KBV~nqQ7i*6M3EF9Fy^%u~UGQL0&e;gU~I=5p)F z8m9uV_+XCPH~2X-?XJ{z-Ct|S!WwiAr<%S&xMp^21=idmVk7M#)I_>Ttbp)g9Sr4gp7;+9joo7`b6Zo_Y1+LudzO5rQ> zUe(DnYG{ff3FK@8J3gh5q|VI=A}o5zddsRTb@9ty;_TfHuKY$imwWmHsZeWGdE6_L z-cGUUBR5@!&nM^M0ebh{-l1R^rHyNaJnL*q_bvC{V_4JC)(r~V-#zPrU;By&$jeK{ zh0V2QkYouXyDSB&H6EqQZWXOvY;fo@j(JjRbEE)M&N*$5#1~52<0tu?V}G=pg{Fb< zUVEXyaS`qeyG#(9i6eV%B5d_+x2<>KTg{_1>4pOyof!KVim{Yoamf3|zTxDR<&jSX zjJAKxwQ#`L0fAI;>0$vjy_JOQ^r6|_cU>(nqlL!Mpkl**j`@o|As%yd_3_mc9<1gP zja!-I97JSZ_$QiLz8vuOhpZ+qf9QC8*vOX^A_)|&0pEue^>f%Y+XELu@HheLT^LS_ zsMzlL;>$^=W0pD)@Fr=sxGW8d4x_X~1r<3LiVpJX%#N=QFBliBTsOwG^@j>ZA+Ol~ z=GA>;V~!_Z71aY~SQ1;ADiTS(JMHR0Ib18&+##eeqt!ObqK37cn2yebT56nEK$5kJ zuXQJ%5OT2H@qHXgpW&rHlq5`}V5@sWm(g-H*mxm(f_RMsm|B#kjfEvoJDa0F+V?{y z*6gx=t3+!rHf(YDV>B!;%Ew676YS z&Im&aJw)1vi>h-Z3q+OLbsHaaYrfK@`+#|Uu7CzXCE*NZ$G_B~fd*Tgr+j?xY7F#| zJj%R1RDY<38L$8#IE5b|u(+IV-1ks(NJMHP+?ZbD>~~LWsfob!a;$Kts&HH1(5S+c_BxDXHlfA;>PrV|~Tmr+{vK zDwaD~J_#y$eE{u2%<`riK^<@Kp5q)+>M=B$K7uoQXc_RQCjK9<70PT&%E&)&cLGM) zak0y5|Dk0KOpcP_%M!kC9nWHg5k+KHnbslZL0yNncvht-G=%Y5t>fMiCx1&s-ALME zFjx17(HeJ#NN>i-I{T3e+eNYRLM$08N&t5xqEu$))EoSi-|#)=DB&hEO69HW;SB)d zU+4WgmiW1RX*g&o}20|Cisuf9W^w!NFK0OzNzcA0Mb4v`b|FVRjV^hGo9E=^_ zKbp(oe7g-1OI(jjM(-iL21BZ9@lc5{!m zpohV)lfQrWExz`pqG$Zh$)zmRqlcf!j0Ou>u|S2s^0;T$)Z2TO`&_eO!ZusgRY4OT zgP+_*vN2H`A%E0yR}CJ`L^FG2*3ZGpKLaipv$}dP#r73h?e=1tfJvlL9PJHz?oq?w zC|+g-?M8M+xHf9P@PshyexujT7cQk5mpdv~Zdv)!99E5j_;*P;C|MB?g5~su2`=U= z0*iJN*qrBibe%+MJsl>S=znmB4r)K|)b^-IYi$}|LR4VIlLLZn1aS9`_GGc+dr@|sjG?gC|M)IOhMRhO zNfxj-ZsTjC_SJ92ZxbpeP``{3M;8Kv+T>;B*NZ4|*SKQ1?zRgLHSW2{+5NJyghku& z=#zPHGJ;+R1$>c|mZw66D~;K=O>=E&^YbtB@I`5*FE1@LI1H@+`Napmcr2L)o1a-q z#`pZnS>j|XR7g8fh(fKI6;D`TZ>o1ZjbvvmzBV3Fs)VOLg&2|>eJ=qK{VzRu9t?{E z>biYp=j;b0wa?$4`W$p5YMr7mH|mK*M|AAt^S-O;wG-j zKpAcx0q5|c0$4*)0re;56bxq6zg9>M=u7OJlc_dDKlTFuIbeZm6niLohiKS>lB31w zbr}V++@w7hmnx6|WoGHu>HD8iAxC>RxJJt+l_oxGORYXboPL7Ve_$T)q5#E6Xi>q; zU|1%L|I{d6MuN5H6|WxuXEkylYYGavmple4Fwr0fah7-Hwv~wd{-i(GVKq%u`TjVk z#ZbrPFDBub|I?DaRYY0Zyv5chFB>3-jIUV!FmC?z|AEY7-*dKFc>wKf=G6bv5%yR% zZF3)#O&svk{OG_>e|*T`0R-1{%y`m~9p*}soU*c<`teSn+#s0)9g)S! zmOO`(clGG~*&Suz4To+vIWxH}2%s`XQFC$ezU?fu$X@LEaE+81v+FnLj76Tt(pcHG z%P3Ew9|aW!@qbf`SFoToF7#JL{ME^`%`mLz*oAg}i+}*YY+$LOyKc9>M@urpQy8T^ z6jOXr^zMg6K>ofWQ}QD)4FNJIm4ErpUSX`)zUx{!W|0YaP2vFz`KIKlk#EoWM7O&t z{FFRzj<9juPAHj+{J+WdG;!DGcflkZpXTnSnqGar^!#JPE7Z{5K$bb5JRvidlM>h9 zM40ABzALb2gf=YSeiCYu)UEGA*L(4w1ARR-Qu&)#3>K_6#35!h&9Hrv}Wf})22r1N+)bL@k(`^B)>?fm-0VP zWlN)fv#_}qK4%wU+fX1x0^lx>4Y2!qj9l|*2-yfAqD!b247!nFKN9d(170npSVctABSBOv?hKQ9-2DD1W zamS-fk_Ka%C;Y;>Ti}%L@8iRiQ5O_bRkUsEzM(u`S7g0 za?+#nXar>BewT5e8^*)Yc_N<5T4C*z>%Tm+SkRlIkoQ~E)tHireLj%(Xy`8y;zx(< z>KRvmZ$U4X_95%aaTb^B;c%9daJ{cLj*6V2}NlVU0NV0O_35Hl+cyl zBoL{Buu2V8kWK)B5I`vs5C|nX6IOS>^IhkhKj%lTNoMlSyzRc9`+43nz<_WPsd>B= zQ^a)%vlm0?_VivlPuDW;;>GG|QV+uIqAe-!(Z`cZ%;}9Q1uxgbcncEaO*DP|zw3~_ zIPUJ9vxO17=`4E(GM?M?Ns72gGEyF-cV;`DbuhJiu(>%N5lIn%s<&bTTiM_WGkEkv5e5>32Y@v zC=Ul-LHQ2P&wCODXkp_d-IAlHp~;0A={|Tks#E zh9c!uTRZubeb+zw%cOG=w*6b1omrHQeEB>0_!XgCiba1JPJezt9x+f}Stut;R-5qA zc4*`2DSZ{WZFj|lY{Np$C|5uvkF*4a7>s{$)kRSzbo)wsBlnR7?e~KIVPg9O3Cy$9pa27`ar99h8KxnTqdTjUO8xOyYF{?IUJ}G zLi%UU#--ju4UY}8@10)1)-_@IAS)s8w8os$+``2)K`$o&kLnqm+gxsIp&s(W5xK(H z35`2Ocv9RAohHTZHB6?&_D_D+s&UIjmM~k)-e2Q0~7Jc*6w$9DSvD zFM>h-HUj?Wx6|0{+(wCWs`mEwx1YWz7XgD9fc5frN-3>R(7W}o8|U;TPV1UKfgci3 zgd?6Io_c5^Z+l9;{BXE5DC}Ek9v{xD+4RoHbXnP!EXMCP#LDuGsHD719a}2#VTny- z_w$SX%iKHyqp#krV~8Zap66&Z-O%~5o$O!$SojIwey2e@B2+agV}-m%@6o{$3vQt_ zUq!wyt&3Nk=i8Ou%_M9#lq~zZCl;(hvA z`p1B+OP8s#9d$wMwqJfJ5a^o!peT7gPi;xad2N;#E0 ziezUFOhDdTb0oY~JSi43uW<{%t031TNv7^B7if@2uA?BSs%Dlc zzST*&eW}+zI)A+8?YNeeYMAw`{Fr=euDNTQIt;R;{IC?Lo)Kon`P?@p7&eMfm`J z^|-#*#}VbWLAT%a2k^4qqI$jQS2}UZeM639p2zUX7au7}x@$JwXPBH(ckSYWY#M4` z=2IO4wz!8$l|4Krd|AJ!E$wSX^)?ec+|dM7E=hn&n#9(JEcz7)vs?wy>XiGqVBjmVg`fKz zH;Y~3xhuV%(#t3JKS`6m;>=On!aS8hV6|{++AYcuJzZ~F*N&AxGkdZ#b)q-5*yR3` zj=)cF{}owD!vR7J(v9BgI}9I-kruNx^e#fArMrWRh*$+k$quKG7Mo^EinT_noTIuB z$0 zWZpK%iES>xcSz?GxVD-DYA5o4J6L+T<+lVCS%4agYdOof*daPkwhyI3^%JJGwa7VB zq>v~={9H~$dVtjF`c`d3zKWCGK_ai7ku5ki7PjP;q49j>4jGEdyV*OqUP^f9AK*59 zQpl9nthFzUJeibKs=-}}c*I2AZV~f?cOGjarG<5Sb)r}>d%nJ>TUC)e8UnwqHiPV( zzD1Am##X=lV%HUul-qS4L$C7}N~zG41Wiv9+N*Nq>L?ad<;$gW1y-r_bd9--u86B! zFUgLs9(x3lbT!dAydbmI6~9|9hK@9Jw_xc6OF^k2t)O^$dVi4~QPwaEFm%WM+J{0y zqS#U#3O=h@fQ4oc-|5-udgV^j<1UqmND9ns6va`LOvRhj*Wqv7T7Cs+M8Fg8#J)FX9#$MmZ4#rm)gr&DJsHAKB7;~*dJn?nkOUBE%kR3<@C zLOe>_PFj819$t<1Lo3&~6DMw&sw&lJJmH(}e_Zmwfsj+{NV~NJA6?3~im$Ld$UWK& z&t)575G0^K0WJh|ZPcF(-{Qv^hTvofHO864i-0!`oB`q?z~_HSGBy-ft7NQ^blEko zzP|!UyOag0nMXf%0?aV`u5J#l8T@P^P-Cf@G(ySnmICg*izeVA ztdZun5QVXELmM#ul*v47<*2mD#Z)(ULuM3CpzDyHF94nuBdb7kz@Cx8!y_YG46Q%= z9%Oi1aQOGGX0v)_)n^32>lKk3^Nxdn@4V7+Wxm5Y>R!NDDFJvfB;} zQ}Z~+-RB|2^o|D%v1A&-xK4yuLzNmm#3gDR@Wp#LE)Gmzt8-B6{xjwJ@K}~NZXug5kzGJJ!QJ26BI*L)H`922WOSc5u%>SPbYFar=S= zrpZeL)AWqMG*AOgYGJtwi}vhXU8#7u*+!(eAY%DV&3#JN*&IXpHenw8^?4(`FGP$q z!J~oDvMa|i=W;z$y3JZ=eIp30Nk*$EA22y;GoCQ1&ZrE5dw5YRHwDO=J*EmgcE)ZG zN4R6z;{H!xqsW=@{r=jOc41hbdm`t=@UMtd81<7Nk>|XrP_Rzxhn+ZAi=(!Y9O|eK zOaOM|E<7bn7A3rC)v0*!C?H_V|J7)GyMvcsxXrVZ40K(;hwq8oX;HSE+%>r5(b-$G z;dqF;>-P{oW-&eADw3kwy_@ubh!226sCU*OGS402iNPNXwEjH)L~XDymY`X6q0I0# zI*#C8ftD7lUWqz5Wo7m5n{HQ%D;z%rBdifB8eCQ^04~}z!Hn^wNW#ow1geq*t_-1v z9cs_S5~E{)GU?Oh|6-?qmu)oO+U@t~j`blqxRPclU{;wpZdo`k5+Qciu z*r=Dr*Ut|Ak37jJIfF=la^*j{VUVE~NXrE?Cj~^~wf*0d+WO`^!$x-#S=U&1XnQ+2 z)OT$>wLO%sbfn-ska34(0!Ok8oAgBt)TP>M1M6+P$cp%&-Z^=bKx{J+)wm_RVaL3I zE`L|&qgh+~#w$2od}e((giuV?Bkizjt#WV?|n}o$&-Dlm@@c^ok|MW8djkd_^H94oO|sx!BvaN^6l5iyk?#t%F@d!$iy%$ zur<1Y>k~xJ?M>ak7o=b;5A=Z+8H_UP zwlEtlw#qvyXrt1lmsH%e!m~^GrMFbV*AP{=7$NQqMaIjkdViPTz|6ZkhnItbOijP- z^vp|_61uG5ZW%!A259T86k-63Tszc!ip$PM3AMxX00T4Ex^+WI+OGP`iMh>h_y|Lv zp2nFPeP>GK1I$Q~QQ3?@E#>;ma`r}#?pJfJ`72nZH>Vd4TOCax5e;U#rGz8dZ6}1) zsVsFG`8TRzflP5d9JYJ$W-|OD;yrU=+BEsPFO89NhFPK{%pXE@j5rkbNt2Rmi zLDV)Y6+OMrJ(5S>DR#T_*ruw9+O9ASriI9RaSq!C12kv~EPKB@*5_+7($QxlC}rta zB%kt8xXvwXW8^hDKp0J}-7=NEU6(B#7u)vCWQ|oQ+GM2mGv7KI{emn`ahTt4d**ui zu7;CwTgb?;TtUgp*DuV#&gF4-4Vq7=+boC=R?YmRuv^lJyjb3Go5vkMR(FF5k(zr% zbnKibTkZKP-rwIUifz9*=twx}YVO#^=B?BTDU%nYWY=Go9V^~>CtjRt!sfent0TJx zq8zjoj1e`wEJo@GX5BMx@bR%E7rG3TD?`+u#JmW?iNHI9w>sC=J?)~CAZL+rVk~LO z&S#_@HKR|*oAAw7E$($k+GVG_2l{Y3snQCsl@M2`LCyZcDu!8#NA@@$MDkqst`roU zUv`v?k@EFqiMSZh{k20aXiRV*IbGPaH{4ClWPp38zJqjuMJeimkm~^n!fk!IPVdD* zekn`b%1)m>{XLcl4pT4fVsXrAAP>fNcwW4KG3gLL!_WDhB|^$gPdx;v_dv`NdLvUH z%h;O;)5P)hVbf(MOk#@<2)InjC zOR`&M511r+A*Gn+yEag9LrRBam>LH2(d#GxSz}DHlqh_5LORImp@jQH|3p8yjTBUd zifcgB%)d`LD=x^h5gzo3LU4Pw{P9WntD`ts3sVh9Ztb^$R~a(rUy7>-qF0RW^ZnlT zLOo9dZtyW2M@Iw-Qw*qhqoZK4RcgpqysCLUymMLjcM zIr%|dPR{3a(E8$n#la|1Ncb=N4PhN^tPTolHmjSryyy*Lc4qwSnldTrh7Oq-F_-Pk z`E}@}Uv~PrfOohY(Yffs&(EVXFhD4OK<~UX6afcI6{QyiYnf$qbZ0M|tmt~GKztyM z=bxX$K1+%VDeitpSQcW_fdCEY08rsSR_i}S`F}Gsf1ABS_50J%{bv#zKGo?edF!(Z z>w1!PgE<9n;Vbpx*@+(jWn8aE}>`x#n-K_q`257qXeN&HJBpOLp@xB3c`zJOjq#r+To zZuK3S>qZW*s-CAQElg0sz=VQB;XbU<&CSwUyu7os{9VVZaO`=CUg8KahyxIBuCh_D zg)Fzn)WNZh3r$MKZbuz^9R-nRSbk`9-V)OH*^dw0H6(>Xat((T%3<{zs3ZSNf8)?SqabXw@T zo{^+8uqN3%XgSO)LrS&L6Kn*K(0jcQfha*~b;}!lEDzAuK1;p>Q~}yP`}b)cgVH&p ze32yDR93^(OdsFQSM8Nn6oQeZmVOYLlcha{$mz-$@<*BM$;hN{3P%wV4Z5F(9|+fj zsh=ax;zPAP=6SB{Q^ik@_B7^iK$KO8qzGqcp53mpgm`RE2o~Lw_LnTvOIsGeB4MD8ZA~RyP!%);SY9sxCbMF=BVYMQo2;w1aQ}K|+*Q4u&c?*}?0WbUGbvFo= zP&Zw?@s$UdP}d>(wHTf=2ttlIP(CnR0Ac~Q01J%00ej9R^ze6 z+m-`Y{I798vbtG+{IVk;(f^1>D^ll>q#d%LmW)`BbH5LRD`r6LO3ZUc!u8!S7JLPYrn8Sm)*;u+e>wM80@W&?un)`o%iB+DqVtSHc|Dlc#BME@V;vvx*@tX!Xf`!8#Jg^OI+;kA`Juw@> zeBS6D<2Pt6d*j%8ONU)|FhSo=LDg>tt-|$uAumA^XrO@wC`e1j?Y*2uOY?2T-N|br z7%>%beIWR4?Z4hSkaXn4I<_)FX3#cmI5mJ%)+M(O+YMM?LO;{2yo7tvK($R{8(ed0 zStzgreq#Nj{$o&%9>Z^#ofV~g2jy%ZV|?GeZ0QQllGUc4oE-zr-Bz6K7Z#l7O^f=K yDY#Y>5Ey3}5tLvCJOBT_0nhqz7x`p5o2kcWQrg-aum$&#BL=$0*UEJ61^*X`atvPp literal 0 HcmV?d00001 diff --git a/CSK_Module_Encoder/pages/assets/legacy/settings.json b/CSK_Module_Encoder/pages/assets/legacy/settings.json index 939ec2a..a845df1 100644 --- a/CSK_Module_Encoder/pages/assets/legacy/settings.json +++ b/CSK_Module_Encoder/pages/assets/legacy/settings.json @@ -1,3 +1,9 @@ { -"showLoginButton": false + "canChangeLanguage": true, + "showLoginButton": false, + "defaultLanguage": "en", + "disableEditMode": true, + "showPageHistory": true, + "compactMode": false, + "canChangeCompactMode": false } \ No newline at end of file diff --git a/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.css b/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.css index 56a9b40..569104d 100644 --- a/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.css +++ b/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.css @@ -1,9 +1,81 @@ +.myCustomSpacerVert10_CSK_Module_Encoder { + min-height: 10px; +} + +.myCustomSpacerVert20_CSK_Module_Encoder { + min-height: 20px; +} + +.myCustomFrameNoColor_CSK_Module_Encoder { + margin: 6px; + border-radius: 10px; + border-style: solid; + border-width: 0px; + border-color: 007CC1; + background-color: white; +} + .myCustomFrame_CSK_Module_Encoder { + margin: 6px; + border-radius: 10px; border-style: solid; border-width: 1px; - border-color: grey; - margin: 6px; + border-color: #007CC1; + background-color: white; +} + +.myCustomLabel_CSK_Module_Encoder { + font-size:30px; + color: grey; + margin-top: 10px; +} + +.myCustomFrameLabel_CSK_Module_Encoder { + background: white; + position:relative; + top: calc(-1.2rem); + left: calc(1rem); + font-size: medium; +} + +.myCustomPersistentDataMargin_CSK_Module_Encoder { + margin-top: -53px; + margin-left: 130px; +} + +.myCustomPersistentDataMarginBack_CSK_Module_Encoder { + margin-left: -127px; +} + +.myCustomBorderLeft_CSK_Module_Encoder { + border-left: 1px solid lightgray; +} + +.myCustomMarginFirstRow_CSK_Module_Encoder { + margin-top: -49px; +} + +.myCustomBorderBottom_CSK_Module_Encoder { + border-bottom: 1px solid lightgray; + margin-inline: calc(1rem); +} + +.myCustomMarginTop7PX_CSK_Module_Encoder { + margin-top: 7px; +} + +.myCustomBackground_CSK_Module_Encoder { +} + +.myCustomButton_CSK_Module_Encoder { + border-radius: 30px; + padding-right: 0px; +} + +.myCustomMarginInline1Rem_CSK_Module_Encoder { + margin-inline: calc(1rem); } -.myCustomCssClass_CSK_Module_Encoder { +.myMinWidht100p_CSK_Module_Encoder { + min-width: 100%; } diff --git a/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.html b/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.html index a2d5d62..6c79f76 100644 --- a/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.html +++ b/CSK_Module_Encoder/pages/pages/CSK_Module_Encoder/CSK_Module_Encoder.html @@ -1,493 +1,717 @@ - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BIDIRECTIONAL - - - POSITIVE_MOVEMENT - - - NEGATIVE_MOVEMENT - - - FORWARD_MOVEMENT - - - BACKWARD_MOVEMENT - - - - - - - - - - - - - - - - - - - - - - - SINGLE_PHASE - - - DUAL_PHASE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TICKS - - - DISTANCE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Load Config - - - - - - - - - Save Config - - - - - - - - - - - - - - - - Please login via CSK_UserManagement module (at least with user level "Operator" or higher). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BIDIRECTIONAL + + + POSITIVE_MOVEMENT + + + NEGATIVE_MOVEMENT + + + FORWARD_MOVEMENT + + + BACKWARD_MOVEMENT + + + + + + + + + + + + + + + + + + + + + + + SINGLE_PHASE + + + DUAL_PHASE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TICKS + + + DISTANCE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Please log in via CSK_UserManagement module (at least via user level "Operator") - - - - - - + + + + + + - - Module is not supported on this device... - - - - - - - - - - - - - - + + + + Module is not supported on this device... + + UI sample + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CSK_Module_Encoder/pages/src/converter.ts b/CSK_Module_Encoder/pages/src/converter.ts index 5556714..a1f1885 100644 --- a/CSK_Module_Encoder/pages/src/converter.ts +++ b/CSK_Module_Encoder/pages/src/converter.ts @@ -1,3 +1,64 @@ export function convertToList(value) { return JSON.parse(value) +} + +export function changeStyle(theme) { + const style: HTMLStyleElement = document.createElement('style'); + style.id ='blub' + if (theme == 'CSK_Style'){ + var headerToolbar = `.sopasjs-ui-header-toolbar-wrapper { background-color: #FFFFFF; }` + var uiHeader = `.sopasjs-ui-header>.app-logo { margin-right:0px; }` + var appLogo = `.app-logo { background-color:#FFFFFF; background-image: url(data:image/png;base64,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) }` + var uiNavbar =`.sopasjs-ui-navbar-wrapper { background-color: #737F85; }` + var navbarMenuLiActive = `.sopasjs-navbar-menu>li.active { background-color: #283c45; }` + var navbarMenuLiActiveA = `.sopasjs-navbar-menu>li.active>a { background-color: #283c45; }` + var navbarMeluLi = `.sopasjs-navbar-menu>li { color: #FFFFFF; }` + var navbarMeluLiA = `.sopasjs-navbar-menu>li>a { color: #FFFFFF; }` + var headerToolbarButtonHighlight = `.sopasjs-ui-header-toolbar-button.sopasjs-ui-navigation-navbutton>a.highlight { background-color: #737F85; }` + var toolbarButton = `.sopasjs-ui-header-toolbar-button>a { color: #283c45; }` + + var customBackground = `.CSK_Module_Encoder .myCustomBackground_CSK_Module_Encoder { background-color: #737F8522; }` // font-family: "Open Sans"; }` + + style.innerHTML = headerToolbar; + style.innerHTML += uiHeader; + style.innerHTML += appLogo; + style.innerHTML += uiNavbar; + style.innerHTML += navbarMenuLiActive; + style.innerHTML += navbarMenuLiActiveA; + style.innerHTML += navbarMeluLi; + style.innerHTML += navbarMeluLiA; + style.innerHTML += headerToolbarButtonHighlight; + style.innerHTML += toolbarButton; + + style.innerHTML += customBackground; + } + else if (theme == 'None'){ + var headerToolbar = `.sopasjs-ui-header-toolbar-wrapper { background-color: #007fc3; }` + var uiHeader = `.sopasjs-ui-header>.app-logo { margin-right:10px; }` + var appLogo = `.app-logo { background-color:#007fc3; background-image: url('data:image/png;base64,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') }` + var uiNavbar =`.sopasjs-ui-navbar-wrapper { background-color: #f6f8f9; }` + var navbarMenuLiActive = `.sopasjs-navbar-menu>li.active { background-color: #007fc3; }` + var navbarMenuLiActiveA = `.sopasjs-navbar-menu>li.active>a { background-color: #007fc3; }` + var navbarMeluLi = `.sopasjs-navbar-menu>li { color: #697987; }` + var navbarMeluLiA = `.sopasjs-navbar-menu>li>a { color: #505f6b; }` + var headerToolbarButtonHighlight = `.sopasjs-ui-header-toolbar-button.sopasjs-ui-navigation-navbutton>a.highlight { background-color: #006093; }` + var toolbarButton = `.sopasjs-ui-header-toolbar-button>a { color: #cce5f3; }` + + var customBackground = `.CSK_Module_Encoder .myCustomBackground_CSK_Module_Encoder { background-color: #fff; }` // font-family: "sans-serif"; }` + + style.innerHTML = headerToolbar; + style.innerHTML += uiHeader; + style.innerHTML += appLogo; + style.innerHTML += uiNavbar; + style.innerHTML += navbarMenuLiActive; + style.innerHTML += navbarMenuLiActiveA; + style.innerHTML += navbarMeluLi; + style.innerHTML += navbarMeluLiA; + style.innerHTML += headerToolbarButtonHighlight; + style.innerHTML += toolbarButton; + + style.innerHTML += customBackground; + } + document.head.append(style); + return theme } \ No newline at end of file diff --git a/CSK_Module_Encoder/pages/src/index.ts b/CSK_Module_Encoder/pages/src/index.ts index e69de29..3bbe022 100644 --- a/CSK_Module_Encoder/pages/src/index.ts +++ b/CSK_Module_Encoder/pages/src/index.ts @@ -0,0 +1,17 @@ +document.addEventListener('sopasjs-ready', () => { + const page_1 = document.querySelector('div.sopasjs-ui-navbar-wrapper > div > ul > li:nth-child(3) > a > i'); + page_1.classList.remove('fa-file'); + page_1.classList.add('fa-circle-o-notch'); + + const page_FirstLabel = document.querySelector('div.sopasjs-ui-navbar-wrapper > div > ul > li:nth-child(2)'); + const page_App = document.querySelector('div.sopasjs-ui-navbar-wrapper > div > ul > li:nth-child(4)'); + const page_Setup = document.querySelector('div.sopasjs-ui-navbar-wrapper > div > ul > li:nth-child(5) > a'); + + page_FirstLabel.remove(); + page_App.remove(); + page_Setup.remove(); + + setTimeout(() => { + document.title = 'CSK_Module_Encoder' + }, 500); +}) \ No newline at end of file diff --git a/CSK_Module_Encoder/project.mf.xml b/CSK_Module_Encoder/project.mf.xml index 2c567fe..f09d443 100644 --- a/CSK_Module_Encoder/project.mf.xml +++ b/CSK_Module_Encoder/project.mf.xml @@ -22,7 +22,7 @@ Activate encoder feature (setEncoderFeatureActive). + **2) Pulling encoder / conveyor data periodically** + It is possible to configure a timer to periodically pull encoder / conveyor data. + Set the cycle time via setTimerCycle and activate it via setTimerActive. + -If activated it will pull now the encoder / conveyor data and provide it via the OnNewEncoderInfo / OnNewConveyorInfo events. + +If activated it will pull now the encoder / conveyor data and provide it via the OnNewIncrementInfo / OnNewConveyorInfo events. + {empty} + ** 3) Use ConveyorTimeout** + It is possible to configure a ConveyorTimeout to receive an event if the conveyor moved for a defined amount of ticks or a distance. + @@ -181,16 +181,22 @@ You can make use of the mentioned features / events from other apps or you can d Notify if module can be used on device. + + Notify UI style to use for CSK modules. + + + + Notify version of module. + + + + Notify if FlowConfig should have priority for FlowConfig relevant configurations. + + Function to set the name of the parameters if saved/loaded via the CSK_PersistentData module. - - Send parameters to CSK_PersistentData module if possible to save them. - - - Load parameters for this module from the CSK_PersistentData module if possible and use them. - Configure if this module should load its saved parameters at app/device boot up. @@ -275,133 +281,77 @@ You can make use of the mentioned features / events from other apps or you can d Function to set the prescaler value for the increment input. If greater than 1 all increment values are scaled with this factor. This can be used if the incoming values need a higher resolution than the increment should have. + + Load parameters for this module from the CSK_PersistentData module if possible and use them. + + + + Send parameters to CSK_PersistentData module if possible to save them. + + + + Function to get status if module is active. + + + + Function to clear FlowConfig relevant configurations. + + + released + Function to get all parameters of the client in JSON format. + + + + Function to reset main configuration of module. + + + Function to configure if FlowConfig should have priority for FlowConfig relevant configuration. + + - - no description yet - - - - - - - no description yet - - - - - - - - - - - no description yet - - - - - - - - - - no description yet - - - - - - - - - no description yet - - - no description yet - - - - - - no description yet - - - - - - - no description yet - - - - - - - - - no description yet - - - - - - no description yet - - - - - - no description yet - - - - - - - - no description yet - - - - - - - - no description yet - - - - - - - - - - no description yet - - - - - - no description yet - - - no description yet - - - - + + released + Crown to provide CSK_FlowConfig relevant features. + + Mode to provide encoder / conveyor data. + ENCODER + CONVEYOR + TIMEOUT + HANDLE_ENCODER + HANDLE_TIMEOUT + + + + released + data-flow + Provide encoder / conveyor data. + + + data-flow + Provide encoder / conveyor data. + + + + + released + Internally used CSK_FlowConfig create function. + + + + + released + Internally used CSK_FlowConfig register function. + + + + + + - - no description yet - - - - SICK AG - 0.2.0 + 1.0.0 low false false diff --git a/CSK_Module_Encoder/scripts/CSK_Module_Encoder.lua b/CSK_Module_Encoder/scripts/CSK_Module_Encoder.lua index 19ca3c7..8bf7c65 100644 --- a/CSK_Module_Encoder/scripts/CSK_Module_Encoder.lua +++ b/CSK_Module_Encoder/scripts/CSK_Module_Encoder.lua @@ -43,6 +43,13 @@ _G.logHandle:applyConfig() -- Check this script regarding encoder_Model parameters and functions _G.encoder_Model = require('Sensors/Encoder/Encoder_Model') +-- If using FlowConfig activate following code line +require('Sensors/Encoder/FlowConfig/Encoder_FlowConfig') + +if _G.availableAPIs.default == false or _G.availableAPIs.specific == false then + _G.logger:warning("CSK_Encoder: Relevant CROWN(s) not available on device. Module is not supported...") +end + --************************************************************************** --**********************End Global Scope *********************************** --************************************************************************** diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Controller.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Controller.lua index 7e9a2dc..8217c24 100644 --- a/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Controller.lua +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Controller.lua @@ -20,6 +20,10 @@ local encoder_Model -- ************************ UI Events Start ******************************** +Script.serveEvent('CSK_Encoder.OnNewStatusModuleVersion', 'Encoder_OnNewStatusModuleVersion') +Script.serveEvent('CSK_Encoder.OnNewStatusCSKStyle', 'Encoder_OnNewStatusCSKStyle') +Script.serveEvent('CSK_Encoder.OnNewStatusModuleIsActive', 'Encoder_OnNewStatusModuleIsActive') + Script.serveEvent('CSK_Encoder.OnNewIncrementInfo', 'Encoder_OnNewIncrementInfo') Script.serveEvent('CSK_Encoder.OnNewConveyorInfo', 'Encoder_OnNewConveyorInfo') Script.serveEvent('CSK_Encoder.OnConveyorTimeout', 'Encoder_OnConveyorTimeout') @@ -52,7 +56,7 @@ Script.serveEvent('CSK_Encoder.OnNewStatusConveyorTimeoutActive', 'Encoder_OnNew Script.serveEvent('CSK_Encoder.OnNewStatusConveyorTimeoutMode', 'Encoder_OnNewStatusConveyorTimeoutMode') Script.serveEvent('CSK_Encoder.OnNewStatusConveyorTimeoutValue', 'Encoder_OnNewStatusConveyorTimeoutValue') -Script.serveEvent('CSK_Encoder.OnNewStatusModuleIsActive', 'Encoder_OnNewStatusModuleIsActive') +Script.serveEvent('CSK_Encoder.OnNewStatusFlowConfigPriority', 'Encoder_OnNewStatusFlowConfigPriority') Script.serveEvent("CSK_Encoder.OnNewStatusLoadParameterOnReboot", "Encoder_OnNewStatusLoadParameterOnReboot") Script.serveEvent("CSK_Encoder.OnPersistentDataModuleAvailable", "Encoder_OnPersistentDataModuleAvailable") Script.serveEvent("CSK_Encoder.OnNewParameterName", "Encoder_OnNewParameterName") @@ -130,9 +134,11 @@ local function handleOnExpiredTmrEncoder() updateUserLevel() - Script.notifyEvent("Encoder_OnNewStatusModuleIsActive", encoder_Model.moduleActive) + Script.notifyEvent("Encoder_OnNewStatusModuleVersion", 'v' .. encoder_Model.version) + Script.notifyEvent("Encoder_OnNewStatusCSKStyle", encoder_Model.styleForUI) + Script.notifyEvent("Encoder_OnNewStatusModuleIsActive", _G.availableAPIs.default and _G.availableAPIs.specific) - if encoder_Model.moduleActive then + if _G.availableAPIs.default and _G.availableAPIs.specific then Script.notifyEvent("Encoder_OnNewStatusEncoderFeatureActive", encoder_Model.parameters.encoderActive) @@ -162,6 +168,7 @@ local function handleOnExpiredTmrEncoder() Script.notifyEvent("Encoder_OnNewStatusConveyorTimeoutMode", encoder_Model.parameters.conveyorTimeoutMode) Script.notifyEvent("Encoder_OnNewStatusConveyorTimeoutValue", encoder_Model.parameters.conveyorTimeoutValue) + Script.notifyEvent("Encoder_OnNewStatusFlowConfigPriority", encoder_Model.parameters.flowConfigPriority) Script.notifyEvent("Encoder_OnNewStatusLoadParameterOnReboot", encoder_Model.parameterLoadOnReboot) Script.notifyEvent("Encoder_OnPersistentDataModuleAvailable", encoder_Model.persistentModuleAvailable) Script.notifyEvent("Encoder_OnNewParameterName", encoder_Model.parametersName) @@ -172,7 +179,9 @@ Timer.register(tmrEncoder, "OnExpired", handleOnExpiredTmrEncoder) -- ********************* UI Setting / Submit Functions Start ******************** local function pageCalled() - updateUserLevel() -- try to hide user specific content asap + if _G.availableAPIs.default and _G.availableAPIs.specific then + updateUserLevel() -- try to hide user specific content asap + end tmrEncoder:start() return '' end @@ -188,7 +197,7 @@ end Script.serveFunction('CSK_Encoder.getEncoderHandle', getEncoderHandle) local function setEncoderFeatureActive(status) - _G.logger:info(nameOfModule .. ": Set encoder features status to " .. tostring(status)) + _G.logger:fine(nameOfModule .. ": Set encoder features status to " .. tostring(status)) encoder_Model.parameters.encoderActive = status if status == false then -- Check if digital input port needs to be freed for CSK_DigitalIOManager module @@ -200,7 +209,7 @@ end Script.serveFunction('CSK_Encoder.setEncoderFeatureActive', setEncoderFeatureActive) local function setEncoderInterface(interface) - _G.logger:info(nameOfModule .. ": Set encoder interface to " .. interface) + _G.logger:fine(nameOfModule .. ": Set encoder interface to " .. interface) encoder_Model.freeDigitalInPort() encoder_Model.parameters.encoderInterface = interface encoder_Model.setupEncoder() @@ -209,7 +218,7 @@ end Script.serveFunction('CSK_Encoder.setEncoderInterface', setEncoderInterface) local function setEncoderSource(source) - _G.logger:info(nameOfModule .. ": Set encoder source to " .. source) + _G.logger:fine(nameOfModule .. ": Set encoder source to " .. source) encoder_Model.parameters.encoderSource = source encoder_Model.setupEncoder() handleOnExpiredTmrEncoder() @@ -217,7 +226,7 @@ end Script.serveFunction('CSK_Encoder.setEncoderSource', setEncoderSource) local function setDecoderInstance(instance) - _G.logger:info(nameOfModule .. ": Set decoder instance to " .. instance) + _G.logger:fine(nameOfModule .. ": Set decoder instance to " .. instance) encoder_Model.parameters.decoderInstance = instance encoder_Model.setupEncoder() handleOnExpiredTmrEncoder() @@ -225,7 +234,7 @@ end Script.serveFunction('CSK_Encoder.setDecoderInstance', setDecoderInstance) local function setDecoderHighResoltuion(status) - _G.logger:info(nameOfModule .. ": Set decoder high resolution status to " .. tostring(status)) + _G.logger:fine(nameOfModule .. ": Set decoder high resolution status to " .. tostring(status)) encoder_Model.parameters.decoderHighResolution = status encoder_Model.setupEncoder() handleOnExpiredTmrEncoder() @@ -233,7 +242,7 @@ end Script.serveFunction('CSK_Encoder.setDecoderHighResoltuion', setDecoderHighResoltuion) local function setDecoderCountMode(mode) - _G.logger:info(nameOfModule .. ": Set decoder count mode to " .. mode) + _G.logger:fine(nameOfModule .. ": Set decoder count mode to " .. mode) encoder_Model.parameters.decoderCountMode = mode encoder_Model.setupEncoder() handleOnExpiredTmrEncoder() @@ -241,7 +250,7 @@ end Script.serveFunction('CSK_Encoder.setDecoderCountMode', setDecoderCountMode) local function setDecoderPrescaler(prescaler) - _G.logger:info(nameOfModule .. ": Set decoder prescaler to " .. tostring(prescaler)) + _G.logger:fine(nameOfModule .. ": Set decoder prescaler to " .. tostring(prescaler)) encoder_Model.parameters.decoderPrescaler = prescaler encoder_Model.setupEncoder() handleOnExpiredTmrEncoder() @@ -249,7 +258,7 @@ end Script.serveFunction('CSK_Encoder.setDecoderPrescaler', setDecoderPrescaler) local function setDecoderNumberOfPhases(phases) - _G.logger:info(nameOfModule .. ": Set decoder numberOfPhases to " .. phases) + _G.logger:fine(nameOfModule .. ": Set decoder numberOfPhases to " .. phases) encoder_Model.parameters.decoderNumberOfPhases = phases encoder_Model.setupEncoder() handleOnExpiredTmrEncoder() @@ -257,7 +266,7 @@ end Script.serveFunction('CSK_Encoder.setDecoderNumberOfPhases', setDecoderNumberOfPhases) local function setTimerActive(status) - _G.logger:info(nameOfModule .. ": Set cycle timer status to " .. tostring(status)) + _G.logger:fine(nameOfModule .. ": Set cycle timer status to " .. tostring(status)) encoder_Model.parameters.timerActive = status encoder_Model.setupTimer() handleOnExpiredTmrEncoder() @@ -265,7 +274,7 @@ end Script.serveFunction('CSK_Encoder.setTimerActive', setTimerActive) local function setTimerCycle(time) - _G.logger:info(nameOfModule .. ": Set cycle time to " .. tostring(time)) + _G.logger:fine(nameOfModule .. ": Set cycle time to " .. tostring(time)) encoder_Model.parameters.timerCycle = time encoder_Model.setupTimer() handleOnExpiredTmrEncoder() @@ -273,7 +282,7 @@ end Script.serveFunction('CSK_Encoder.setTimerCycle', setTimerCycle) local function setConveyorActive(status) - _G.logger:info(nameOfModule .. ": Set conveyor status to " .. tostring(status)) + _G.logger:fine(nameOfModule .. ": Set conveyor status to " .. tostring(status)) encoder_Model.parameters.conveyorActive = status encoder_Model.setupConveyor() handleOnExpiredTmrEncoder() @@ -281,7 +290,7 @@ end Script.serveFunction('CSK_Encoder.setConveyorActive', setConveyorActive) local function setConveyorSource(source) - _G.logger:info(nameOfModule .. ": Set conveyor source to " .. source) + _G.logger:fine(nameOfModule .. ": Set conveyor source to " .. source) encoder_Model.parameters.conveyorSource = source encoder_Model.setupConveyor() handleOnExpiredTmrEncoder() @@ -289,7 +298,7 @@ end Script.serveFunction('CSK_Encoder.setConveyorSource', setConveyorSource) local function setConveyorResolution(resolution) - _G.logger:info(nameOfModule .. ": Set conveyor resolution to " .. tostring(resolution)) + _G.logger:fine(nameOfModule .. ": Set conveyor resolution to " .. tostring(resolution)) encoder_Model.parameters.conveyorResolution = resolution encoder_Model.setupConveyor() handleOnExpiredTmrEncoder() @@ -297,7 +306,7 @@ end Script.serveFunction('CSK_Encoder.setConveyorResolution', setConveyorResolution) local function setConveyorPrescaler(prescaler) - _G.logger:info(nameOfModule .. ": Set conveyor prescaler to " .. tostring(prescaler)) + _G.logger:fine(nameOfModule .. ": Set conveyor prescaler to " .. tostring(prescaler)) encoder_Model.parameters.conveyorPrescaler = prescaler encoder_Model.setupConveyor() handleOnExpiredTmrEncoder() @@ -314,7 +323,7 @@ end Script.serveFunction('CSK_Encoder.getConveyorTimeoutHandle', getConveyorTimeoutHandle) local function setConveyorTimeoutActive(status) - _G.logger:info(nameOfModule .. ": Set conveyor timeout active status to " .. tostring(status)) + _G.logger:fine(nameOfModule .. ": Set conveyor timeout active status to " .. tostring(status)) encoder_Model.parameters.conveyorTimeoutActive = status encoder_Model.setupConveyorTimeout() handleOnExpiredTmrEncoder() @@ -322,7 +331,7 @@ end Script.serveFunction('CSK_Encoder.setConveyorTimeoutActive', setConveyorTimeoutActive) local function setConveyorTimeoutMode(mode) - _G.logger:info(nameOfModule .. ": Set conveyor timeout mode to " .. mode) + _G.logger:fine(nameOfModule .. ": Set conveyor timeout mode to " .. mode) encoder_Model.parameters.conveyorTimeoutMode = mode encoder_Model.setupConveyorTimeout() handleOnExpiredTmrEncoder() @@ -330,29 +339,48 @@ end Script.serveFunction('CSK_Encoder.setConveyorTimeoutMode', setConveyorTimeoutMode) local function setConveyorTimeoutValue(value) - _G.logger:info(nameOfModule .. ": Set conveyor timeout value to " .. tostring(value)) + _G.logger:fine(nameOfModule .. ": Set conveyor timeout value to " .. tostring(value)) encoder_Model.parameters.conveyorTimeoutValue = value encoder_Model.setupConveyorTimeout() handleOnExpiredTmrEncoder() end Script.serveFunction('CSK_Encoder.setConveyorTimeoutValue', setConveyorTimeoutValue) +local function getStatusModuleActive() + return _G.availableAPIs.default and _G.availableAPIs.specific +end +Script.serveFunction('CSK_Encoder.getStatusModuleActive', getStatusModuleActive) + +local function clearFlowConfigRelevantConfiguration() + -- Insert code here to clear FlowConfig relevant actions + --TODO + --encoder_Model.deregisterFromEvent() +end +Script.serveFunction('CSK_Encoder.clearFlowConfigRelevantConfiguration', clearFlowConfigRelevantConfiguration) + +local function getParameters() + return encoder_Model.helperFuncs.json.encode(encoder_Model.parameters) +end +Script.serveFunction('CSK_Encoder.getParameters', getParameters) + -- ***************************************************************** -- Following function can be adapted for CSK_PersistentData module usage -- ***************************************************************** local function setParameterName(name) - _G.logger:info(nameOfModule .. ": Set parameter name: " .. tostring(name)) + _G.logger:fine(nameOfModule .. ": Set parameter name: " .. tostring(name)) encoder_Model.parametersName = name end Script.serveFunction("CSK_Encoder.setParameterName", setParameterName) -local function sendParameters() +local function sendParameters(noDataSave) if encoder_Model.persistentModuleAvailable then CSK_PersistentData.addParameter(encoder_Model.helperFuncs.convertTable2Container(encoder_Model.parameters), encoder_Model.parametersName) CSK_PersistentData.setModuleParameterName(nameOfModule, encoder_Model.parametersName, encoder_Model.parameterLoadOnReboot) - _G.logger:info(nameOfModule .. ": Send Encoder parameters with name '" .. encoder_Model.parametersName .. "' to CSK_PersistentData module.") - CSK_PersistentData.saveData() + _G.logger:fine(nameOfModule .. ": Send Encoder parameters with name '" .. encoder_Model.parametersName .. "' to CSK_PersistentData module.") + if not noDataSave then + CSK_PersistentData.saveData() + end else _G.logger:warning(nameOfModule .. ": CSK_PersistentData module not available.") end @@ -363,47 +391,64 @@ local function loadParameters() if encoder_Model.persistentModuleAvailable then local data = CSK_PersistentData.getParameter(encoder_Model.parametersName) if data then + clearFlowConfigRelevantConfiguration() _G.logger:info(nameOfModule .. ": Loaded parameters from CSK_PersistentData module.") encoder_Model.parameters = encoder_Model.helperFuncs.convertContainer2Table(data) + + encoder_Model.parameters = encoder_Model.helperFuncs.checkParameters(encoder_Model.parameters) + encoder_Model.setupEncoder() CSK_Encoder.pageCalled() + return true else _G.logger:warning(nameOfModule .. ": Loading parameters from CSK_PersistentData module did not work.") + return false end else _G.logger:warning(nameOfModule .. ": CSK_PersistentData module not available.") + return false end end Script.serveFunction("CSK_Encoder.loadParameters", loadParameters) local function setLoadOnReboot(status) encoder_Model.parameterLoadOnReboot = status - _G.logger:info(nameOfModule .. ": Set new status to load setting on reboot: " .. tostring(status)) + _G.logger:fine(nameOfModule .. ": Set new status to load setting on reboot: " .. tostring(status)) + Script.notifyEvent("Encoder_OnNewStatusLoadParameterOnReboot", status) end Script.serveFunction("CSK_Encoder.setLoadOnReboot", setLoadOnReboot) +local function setFlowConfigPriority(status) + encoder_Model.parameters.flowConfigPriority = status + _G.logger:fine(nameOfModule .. ": Set new status of FlowConfig priority: " .. tostring(status)) + Script.notifyEvent("Encoder_OnNewStatusFlowConfigPriority", encoder_Model.parameters.flowConfigPriority) +end +Script.serveFunction('CSK_Encoder.setFlowConfigPriority', setFlowConfigPriority) + --- Function to react on initial load of persistent parameters local function handleOnInitialDataLoaded() - _G.logger:info(nameOfModule .. ': Try to initially load parameter from CSK_PersistentData module.') - if string.sub(CSK_PersistentData.getVersion(), 1, 1) == '1' then + if _G.availableAPIs.default and _G.availableAPIs.specific then + _G.logger:fine(nameOfModule .. ': Try to initially load parameter from CSK_PersistentData module.') + if string.sub(CSK_PersistentData.getVersion(), 1, 1) == '1' then - _G.logger:warning(nameOfModule .. ': CSK_PersistentData module is too old and will not work. Please update CSK_PersistentData module.') + _G.logger:warning(nameOfModule .. ': CSK_PersistentData module is too old and will not work. Please update CSK_PersistentData module.') - encoder_Model.persistentModuleAvailable = false - else + encoder_Model.persistentModuleAvailable = false + else - local parameterName, loadOnReboot = CSK_PersistentData.getModuleParameterName(nameOfModule) + local parameterName, loadOnReboot = CSK_PersistentData.getModuleParameterName(nameOfModule) - if parameterName then - encoder_Model.parametersName = parameterName - encoder_Model.parameterLoadOnReboot = loadOnReboot - end + if parameterName then + encoder_Model.parametersName = parameterName + encoder_Model.parameterLoadOnReboot = loadOnReboot + end - if encoder_Model.parameterLoadOnReboot then - loadParameters() + if encoder_Model.parameterLoadOnReboot then + loadParameters() + end + Script.notifyEvent('Encoder_OnDataLoadedOnReboot') end - Script.notifyEvent('Encoder_OnDataLoadedOnReboot') end end if CSK_DigitalIOManager then @@ -412,6 +457,15 @@ else Script.register("CSK_PersistentData.OnInitialDataLoaded", handleOnInitialDataLoaded) end +local function resetModule() + if _G.availableAPIs.default and _G.availableAPIs.specific then + clearFlowConfigRelevantConfiguration() + pageCalled() + end +end +Script.serveFunction('CSK_Encoder.resetModule', resetModule) +Script.register("CSK_PersistentData.OnResetAllModules", resetModule) + -- ************************************************* -- END of functions for CSK_PersistentData module usage -- ************************************************* diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Model.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Model.lua index 758e519..57a3bd1 100644 --- a/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Model.lua +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Model.lua @@ -31,9 +31,9 @@ setEncoder_ModelHandle(encoder_Model) --Loading helper functions if needed encoder_Model.helperFuncs = require('Sensors/Encoder/helper/funcs') -encoder_Model.moduleActive = true -- Features provided by device (APIs available) - -- Create parameters / instances for this module +encoder_Model.styleForUI = 'None' -- Optional parameter to set UI style +encoder_Model.version = Engine.getCurrentAppVersion() -- Version of module encoder_Model.availableInterfaces = {} -- List of available connectors to connect the encoder -- Check if specific API was loaded via @@ -71,6 +71,14 @@ if _G.availableAPIs.specific then -- Parameters to be saved permanently if wanted encoder_Model.parameters = {} + encoder_Model.parameters = require('Sensors/Encoder/Encoder_Parameters') + + encoder_Model.parameters.encoderInterface = encoder_Model.availableInterfaces[1] -- Source of connected encoder (e.g. S4, INC, SER1) + encoder_Model.parameters.encoderSource = encoder_Model.availableEncoderIncrementSources[1] -- Devices identifier of the encoder (e.g. ENC1) + encoder_Model.parameters.decoderInstance = encoder_Model.availableDecoderInstances[1] or '' -- Instance of decoder to use + encoder_Model.parameters.conveyorSource = encoder_Model.availableIncrementSources[1] or '' -- Source of increment source to be used for updating the system increment + + encoder_Model.parameters.flowConfigPriority = false -- Status if FlowConfig should have priority for FlowConfig relevant configurations encoder_Model.parameters.encoderActive = false -- Status if encoder features are active @@ -96,9 +104,7 @@ if _G.availableAPIs.specific then encoder_Model.parameters.conveyorTimeoutMode = 'TICKS' -- 'TICKS' or 'DISTANCE' else - encoder_Model.moduleActive = false - _G.logger:warning(nameOfModule .. ': Necessary CROWNs are not available on this device. Module is not supported...') - + _G.logger:warning(nameOfModule .. ": Relevant CROWN(s) not available on device. Module is not supported...") --TODO end --************************************************************************** @@ -107,6 +113,13 @@ end --**********************Start Function Scope ******************************* --************************************************************************** +--- Function to react on UI style change +local function handleOnStyleChanged(theme) + encoder_Model.styleForUI = theme + Script.notifyEvent("Encoder_OnNewStatusCSKStyle", encoder_Model.styleForUI) +end +Script.register('CSK_PersistentData.OnNewStatusCSKStyle', handleOnStyleChanged) + --- Function to periodically pull increment / conveyor information local function handleOnTimerExpired() local incr, timestamp = encoder_Model.encoder[encoder_Model.parameters.encoderSource]:getCurrentIncrement() @@ -149,7 +162,6 @@ end --- Function to notify when conveyor timeout expired and restart it local function handleOnConveyorTimerExpired() - print("Got trigger") --DEBUG restartConveyorTimeout() Script.notifyEvent('Encoder_OnConveyorTimeout', DateTime.getTimestamp()) end diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Parameters.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Parameters.lua new file mode 100644 index 0000000..895bd1e --- /dev/null +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/Encoder_Parameters.lua @@ -0,0 +1,30 @@ +-- Parameters to be saved permanently if wanted + +local encoderParameters = {} + +encoderParameters.flowConfigPriority = false -- Status if FlowConfig should have priority for FlowConfig relevant configurations + +encoderParameters.encoderActive = false -- Status if encoder features are active + +encoderParameters.decoderHighResolution = false -- Status if using high resolution or robust mode +encoderParameters.decoderCountMode = 'POSITIVE_MOVEMENT' -- Select how increments will be counted, 'BIDIRECTIONAL', 'POSITIVE_MOVEMENT', 'NEGATIVE_MOVEMENT', 'FORWARD_MOVEMENT', 'BACKWARD_MOVEMENT' +encoderParameters.decoderPrescaler = 1 -- Prescaler value for the increment input +encoderParameters.decoderNumberOfPhases = 'DUAL_PHASE' -- Select 'SINGLE_PHASE' (A_in) / 'DUAL_PHASE' (A_in and B_in) + +encoderParameters.timerActive = false -- Status if encoder data should be pulled periodically +encoderParameters.timerCycle = 1000 -- Cycle time to get increment values + +encoderParameters.conveyorActive = false -- Status if conveyor featues should be used +encoderParameters.conveyorResolution = 1 -- Resolution in micrometer/increment +encoderParameters.conveyorPrescaler = 1 -- Prescaler value for the increment input + +encoderParameters.conveyorTimeoutActive = false -- Status if conveyor timeout should be used +encoderParameters.conveyorTimeoutValue = 100 -- Can be ticks or mm +encoderParameters.conveyorTimeoutMode = 'TICKS' -- 'TICKS' or 'DISTANCE' + +encoderParameters.encoderInterface = '' -- Source of connected encoder (e.g. S4, INC, SER1) +encoderParameters.encoderSource = '' -- Devices identifier of the encoder (e.g. ENC1) +encoderParameters.decoderInstance = '' -- Instance of decoder to use +encoderParameters.conveyorSource = '' -- Source of increment source to be used for updating the system increment + +return encoderParameters \ No newline at end of file diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_FlowConfig.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_FlowConfig.lua new file mode 100644 index 0000000..90c71f2 --- /dev/null +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_FlowConfig.lua @@ -0,0 +1,18 @@ +-- Include all relevant FlowConfig scripts + +--***************************************************************** +-- Here you will find all the required content to provide specific +-- features of this module via the 'CSK FlowConfig'. +--***************************************************************** + +require('Sensors.Encoder.FlowConfig.Encoder_OnNewData') + +--- Function to react if FlowConfig was updated +local function handleOnClearOldFlow() + if _G.availableAPIs.default and _G.availableAPIs.specific then + if encoder_Model.parameters.flowConfigPriority then + CSK_Encoder.clearFlowConfigRelevantConfiguration() + end + end +end +Script.register('CSK_FlowConfig.OnClearOldFlow', handleOnClearOldFlow) \ No newline at end of file diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_OnNewData.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_OnNewData.lua new file mode 100644 index 0000000..589e5f4 --- /dev/null +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/FlowConfig/Encoder_OnNewData.lua @@ -0,0 +1,67 @@ +-- Block namespace +local BLOCK_NAMESPACE = "Encoder_FC.OnNewData" +local nameOfModule = 'CSK_Encoder' + +--************************************************************* +--************************************************************* + +-- Required to keep track of already allocated resource +local instanceTable = {} + +local function register(handle, _ , callback) + + Container.remove(handle, "CB_Function") + Container.add(handle, "CB_Function", callback) + + local mode = Container.get(handle, 'Mode') + + local function localCallback() + if callback ~= nil then + if mode == 'ENCODER' then + Script.callFunction(callback, 'CSK_Encoder.OnNewIncrementInfo') + elseif mode == 'CONVEYOR' then + Script.callFunction(callback, 'CSK_Encoder.OnNewConveyorInfo') + elseif mode == 'TIMEOUT' then + Script.callFunction(callback, 'CSK_Encoder.OnConveyorTimeout') + elseif mode == 'HANDLE_ENCODER' then + Script.callFunction(callback, 'HANDLE_ENC') + elseif mode == 'HANDLE_TIMEOUT' then + Script.callFunction(callback, 'HANDLE_TIM') + end + else + _G.logger:warning(nameOfModule .. ": " .. BLOCK_NAMESPACE .. ".CB_Function missing!") + end + end + Script.register('CSK_FlowConfig.OnNewFlowConfig', localCallback) + + return true +end +Script.serveFunction(BLOCK_NAMESPACE ..".register", register) + +--************************************************************* +--************************************************************* +local function create(mode) + + local instanceName = tostring(mode) + + -- Check if same instance is already configured + if instanceTable[instanceName] ~= nil then + _G.logger:warning(nameOfModule .. "Instance invalid or already in use, please choose another one") + return nil + else + -- Otherwise create handle and store the restriced resource + local handle = Container.create() + instanceTable[instanceName] = instanceTable + Container.add(handle, 'Mode', mode) + Container.add(handle, "CB_Function", "") + return handle + end +end +Script.serveFunction(BLOCK_NAMESPACE .. ".create", create) + +--- Function to reset instances if FlowConfig was cleared +local function handleOnClearOldFlow() + Script.releaseObject(instanceTable) + instanceTable = {} +end +Script.register('CSK_FlowConfig.OnClearOldFlow', handleOnClearOldFlow) \ No newline at end of file diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkAPIs.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkAPIs.lua index 1a6e5a7..ef7f6a6 100644 --- a/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkAPIs.lua +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkAPIs.lua @@ -5,16 +5,16 @@ local availableAPIs = {} +-- Function to load all default APIs local function loadAPIs() CSK_Encoder = require 'API.CSK_Encoder' - Container = require 'API.Container' - DateTime = require 'API.DateTime' - Engine = require 'API.Engine' - Flow = require 'API.Flow' Log = require 'API.Log' Log.Handler = require 'API.Log.Handler' Log.SharedLogger = require 'API.Log.SharedLogger' + + Container = require 'API.Container' + Engine = require 'API.Engine' Object = require 'API.Object' Timer = require 'API.Timer' @@ -25,18 +25,23 @@ local function loadAPIs() CSK_PersistentData = require 'API.CSK_PersistentData' elseif appList[i] == 'CSK_Module_UserManagement' then CSK_UserManagement = require 'API.CSK_UserManagement' + elseif appList[i] == 'CSK_Module_FlowConfig' then + CSK_FlowConfig = require 'API.CSK_FlowConfig' end end end +-- Function to load specific APIs local function loadSpecificAPIs() -- If you want to check for specific APIs/functions supported on the device the module is running, place relevant APIs here - -- e.g.: + DateTime = require 'API.DateTime' + Flow = require 'API.Flow' Conveyor = require 'API.Conveyor' Conveyor.Timeout = require 'API.Conveyor.Timeout' Encoder = require 'API.Encoder' end +-- Function to load related APIs local function checkRelatedAPIs() CSK_DigitalIOManager = require 'API.CSK_DigitalIOManager' end diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkParameters.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkParameters.lua new file mode 100644 index 0000000..cad66ae --- /dev/null +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/checkParameters.lua @@ -0,0 +1,46 @@ +---@diagnostic disable: undefined-global, redundant-parameter, missing-parameter + +-- Check if all relevant parameters are available +--************************************************************************** +local nameOfModule = 'CSK_Encoder' + +local defaultParameters = require('Sensors/Encoder/Encoder_Parameters') + +local function compare(content, defaultTable) + for key, value in pairs(defaultTable) do + if type(value) == 'table' then + if content[key] == nil then + _G.logger:info(nameOfModule .. ": Created missing parameters table '" .. tostring(key) .. "'") + content[key] = {} + end + content[key] = compare(content[key], defaultTable[key]) + else + if content[key] == nil then + _G.logger:info(nameOfModule .. ": Missing parameter '" .. tostring(key) .. "'. Adding default value '" .. tostring(defaultTable[key]) .. "'") + content[key] = defaultTable[key] + end + end + end + return content +end + +local function checkParameters(params) + + local result + for key, value in pairs(defaultParameters) do + if type(value) == 'table' then + if params[key] == nil then + _G.logger:info(nameOfModule .. ": Created missing parameters table '" .. tostring(key) .. "'") + params[key] = {} + end + params[key] = compare(params[key], defaultParameters[key]) + elseif params[key] == nil then + _G.logger:info(nameOfModule .. ": Missing parameter '" .. tostring(key) .. "'. Adding default value '" .. tostring(defaultParameters[key]) .. "'") + params[key] = defaultParameters[key] + end + end + + return params +end + +return checkParameters \ No newline at end of file diff --git a/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/funcs.lua b/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/funcs.lua index 5a1c284..a187d54 100644 --- a/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/funcs.lua +++ b/CSK_Module_Encoder/scripts/Sensors/Encoder/helper/funcs.lua @@ -10,6 +10,8 @@ local funcs = {} -- Providing standard JSON functions funcs.json = require('Sensors/Encoder/helper/Json') +-- Function to check if all relevant parameters exist +funcs.checkParameters = require('Sensors/Encoder/helper/checkParameters') --************************************************************************** --********************** End Global Scope ********************************** diff --git a/README.md b/README.md index a377e10..c030ce4 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,25 @@ # CSK_Module_Encoder Module to provide Encoder / Conveyor functionality. - -![](https://github.com/SICKAppSpaceCodingStarterKit/CSK_Module_Encoder/blob/main/docu/media/UI_Screenshot.png) +![](./docu/media/UI_Screenshot.png) ## How to Run + The app includes an intuitive GUI to setup a connected encoder / conveyor to use. For further information check out the [documentation](https://raw.githack.com/SICKAppSpaceCodingStarterKit/CSK_Module_Encoder/main/docu/CSK_Module_Encoder.html) in the folder "docu". ## Information -Tested on: -1. SIM1012 - Firmware 2.2.0 +Tested on: +|Device|Firmware|Module version +|--|--|--| +SIM1012|V2.4.2|V1.0.0| -This application / module is part of the SICK AppSpace Coding Starter Kit developing approach. -It is programmed in an object oriented way. Some of the modules use kind of "classes" in Lua to make it possible to reuse code / classes in other projects. -In general it is not neccessary to code this way, but the architecture of this app can serve as a sample to be used especially for bigger projects and to make it easier to share code. +This module is part of the SICK AppSpace Coding Starter Kit developing approach. +It is programmed in an object-oriented way. Some of these modules use kind of "classes" in Lua to make it possible to reuse code / classes in other projects. +In general, it is not neccessary to code this way, but the architecture of this app can serve as a sample to be used especially for bigger projects and to make it easier to share code. Please check the [documentation](https://github.com/SICKAppSpaceCodingStarterKit/.github/blob/main/docu/SICKAppSpaceCodingStarterKit_Documentation.md) of CSK for further information. ## Topics -Coding Starter Kit, CSK, Module, SICK-AppSpace, Encoder, Conveyor, Inkrement +Coding Starter Kit, CSK, Module, SICK-AppSpace, Encoder, Conveyor, Increment diff --git a/docu/CSK_Module_Encoder.html b/docu/CSK_Module_Encoder.html index b2b01f8..f89621a 100644 --- a/docu/CSK_Module_Encoder.html +++ b/docu/CSK_Module_Encoder.html @@ -6,7 +6,7 @@ -Documentation - CSK_Module_Encoder 0.2.0 +Documentation - CSK_Module_Encoder 1.0.0