From c84be294131a2eccc0c193559a0c6e4641f7c67d Mon Sep 17 00:00:00 2001 From: suyuan <175338101@qq.com> Date: Tue, 22 Nov 2022 03:23:41 +0800 Subject: [PATCH] add gps --- luci-app-gpoint-main/Images/logo.png | Bin 0 -> 27005 bytes luci-app-gpoint-main/Images/overview.png | Bin 0 -> 159871 bytes luci-app-gpoint-main/Images/overview_wait.png | Bin 0 -> 138466 bytes luci-app-gpoint-main/Images/settings.png | Bin 0 -> 47850 bytes luci-app-gpoint-main/LICENSE | 674 ++++ luci-app-gpoint-main/Makefile | 14 + luci-app-gpoint-main/README.md | 45 + .../resources/icons/gpoint_icons/marker.png | Bin 0 -> 1466 bytes .../resources/icons/gpoint_icons/spiner.gif | Bin 0 -> 31517 bytes .../luasrc/controller/gpoint/gpoint.lua | 86 + .../luasrc/model/cbi/gpoint/gpoint.lua | 252 ++ .../luasrc/view/gpoint/blackbox.htm | 33 + .../luasrc/view/gpoint/buttons.htm | 81 + .../luasrc/view/gpoint/css/style.htm | 26 + .../luasrc/view/gpoint/js/js.htm | 183 + .../luasrc/view/gpoint/overview.htm | 140 + .../luasrc/view/gpoint/service.htm | 65 + .../luasrc/view/gpoint/service_status.htm | 83 + luci-app-gpoint-main/root/etc/config/gpoint | 5 + luci-app-gpoint-main/root/etc/init.d/gpoint | 32 + .../root/usr/share/gpoint/gpoint | 229 ++ .../root/usr/share/gpoint/lib/checksum.lua | 95 + .../root/usr/share/gpoint/lib/config.lua | 225 ++ .../root/usr/share/gpoint/lib/geohash.lua | 147 + .../gpoint/lib/kalman_filter/gps_lib.lua | 111 + .../gpoint/lib/kalman_filter/kalman_lib.lua | 82 + .../gpoint/lib/kalman_filter/matrix_lib.lua | 191 + .../root/usr/share/gpoint/lib/locator.lua | 78 + .../root/usr/share/gpoint/lib/nmea.lua | 385 ++ .../root/usr/share/gpoint/lib/serial.lua | 73 + .../root/usr/share/gpoint/modems/dell.lua | 44 + .../root/usr/share/gpoint/modems/que.lua | 41 + .../root/usr/share/gpoint/modems/sierra.lua | 40 + .../root/usr/share/gpoint/modems/simcom.lua | 44 + .../root/usr/share/gpoint/modems/ublox.lua | 22 + .../root/usr/share/gpoint/proto/traccar.lua | 31 + .../usr/share/gpoint/proto/wialon_ips.lua | 160 + .../share/gpoint/proto/wialon_ips_boof.lua | 119 + .../root/usr/share/gpoint/tmp/blackbox.json | 1 + luci-app-gpoint-main/test/chechsum_test.lua | 143 + luci-app-gpoint-main/test/gps_test.lua | 96 + luci-app-gpoint-main/test/kalman_test.lua | 34 + luci-app-gpoint-main/test/luaunit.lua | 3452 +++++++++++++++++ luci-app-gpoint-main/test/matrix_test.lua | 46 + luci-app-gpoint-main/test/nmea_test.lua | 547 +++ luci-app-gpoint-main/test/nmea_test2.lua | 56 + luci-app-gpoint-main/test/serial_test.lua | 16 + 47 files changed, 8227 insertions(+) create mode 100644 luci-app-gpoint-main/Images/logo.png create mode 100644 luci-app-gpoint-main/Images/overview.png create mode 100644 luci-app-gpoint-main/Images/overview_wait.png create mode 100644 luci-app-gpoint-main/Images/settings.png create mode 100644 luci-app-gpoint-main/LICENSE create mode 100644 luci-app-gpoint-main/Makefile create mode 100644 luci-app-gpoint-main/README.md create mode 100644 luci-app-gpoint-main/htdocs/luci-static/resources/icons/gpoint_icons/marker.png create mode 100644 luci-app-gpoint-main/htdocs/luci-static/resources/icons/gpoint_icons/spiner.gif create mode 100644 luci-app-gpoint-main/luasrc/controller/gpoint/gpoint.lua create mode 100644 luci-app-gpoint-main/luasrc/model/cbi/gpoint/gpoint.lua create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/blackbox.htm create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/buttons.htm create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/css/style.htm create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/js/js.htm create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/overview.htm create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/service.htm create mode 100644 luci-app-gpoint-main/luasrc/view/gpoint/service_status.htm create mode 100644 luci-app-gpoint-main/root/etc/config/gpoint create mode 100644 luci-app-gpoint-main/root/etc/init.d/gpoint create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/gpoint create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/checksum.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/config.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/geohash.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/gps_lib.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/kalman_lib.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/matrix_lib.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/locator.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/nmea.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/lib/serial.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/modems/dell.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/modems/que.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/modems/sierra.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/modems/simcom.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/modems/ublox.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/proto/traccar.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips_boof.lua create mode 100644 luci-app-gpoint-main/root/usr/share/gpoint/tmp/blackbox.json create mode 100644 luci-app-gpoint-main/test/chechsum_test.lua create mode 100644 luci-app-gpoint-main/test/gps_test.lua create mode 100644 luci-app-gpoint-main/test/kalman_test.lua create mode 100644 luci-app-gpoint-main/test/luaunit.lua create mode 100644 luci-app-gpoint-main/test/matrix_test.lua create mode 100644 luci-app-gpoint-main/test/nmea_test.lua create mode 100644 luci-app-gpoint-main/test/nmea_test2.lua create mode 100644 luci-app-gpoint-main/test/serial_test.lua diff --git a/luci-app-gpoint-main/Images/logo.png b/luci-app-gpoint-main/Images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cd81bc60727c87aa2c622e02c3647b22cff4f5be GIT binary patch literal 27005 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 literal 0 HcmV?d00001 diff --git a/luci-app-gpoint-main/Images/overview.png b/luci-app-gpoint-main/Images/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..4942bf81d527264443b9ca9a744923530a3fde4e GIT binary patch literal 159871 zcmc$_cT`hb_bzM$EGP#N>AlwgQl&}ny?5y%JqQQ{I7pM;dx`W80wRPeAYDLdp+_Vk z6d@7_Aq2?fz3=Ne=evL0?|ygue&ZV>31japGWVWquDPD)nRCbK>#E(qOMCa)wQKh^ z)RhgdUAtL*^;W)3e08R0>L!c!3m#dpwi9UDK$7d}1cX^@&+-|e8vrH8)9`4Be_qXnPQ#b9 ze;p6E{J6CXhnM4@{~lmZJrRw0_xA|5h&#rELZttGu?Yx8y!>lqIT5G+{omgue)x2& z_AfJOk8_`i{pX(sg=TJ0RR3jC)$;j``QJY{sD4jmcXyX_tss%Kz5n-F$BJfqwPfSD zXpSd*AN$P>Ifqr2>D1thvxCh7M}xtC88!dLtXn6ZD`}%U8gg(Q>%j82QAZV}>6$b~ z{$tyJo^zC~+8I3htIJ$6$M z^?R&?mlDc*nc<;36mB_#Az|Dp{#)Y<_iP1M>Lxh&m1b5htA8$Eq`p$^`Cw}(2n3?A zPG(1`*skwSmx7iioZSOrWiX!sPVY`sT3WTsG2ho_EHHN=Ev#lwbyr!i2i~c^C)31t(TnhOA+aHcFTRN7g5c>Pt&Y z3xXW~nwP|^(d7oeLMhWB%L|pJj0tFcR{8*M=)9&1f~ck8QnQ9yo_S;>aNM8DPQX3> z^x(KoMuK-=b_uwSGvhe*vOZ&NYbgsW$i%ITPi35)xmU=AKT8g9nn>2NWkF_vab4-9 zH)uTN`Ek;h+O`3g0X%^ER0kbB6O4098^}7JwyWaD0CX(NozJO$(_jfI{M|&W7Z2Od0c%E#M-vqb|Eusrg-%Id8O+#lpUc^DR$@*y$k zrPq604w`ud9Hwrm2CR5Fc!U_wUsPb5Y$5E-_H|)5q@C=KMBt$QW`-W`7f6?nuF7;6pL#2JcLv{RuM`ZR5UJ0^zN^L!&%LeA>RS^?Y$cFG62 zh*PWZGiIm1{rSY&Qv0t3>+2;#lUDqZ1kqphM-6t=o-#V!h%L)ruP`a4IFoJq{B!8E zJG8f|LVCAX^Vb^Vj(_t9R<@gX__r$muDf4HEQ3|vAPlp?e1b0i-D;L}2Qrv066`8h z5?kKG?-SdpiE3;jGbi^TdpYf+&`acFNL3BkZZg4FN6&Wc2+?^)I~GrtJ88)#-EJpf zQo`aEyj2Ip-|WlWR6b7*9K9UrV<%i*^raJCe=jkDwr2jlHaAx}%hVUVA({(F5)+$` zTF@8T(J?QbXK{cG_W*Qo+D~hvFw18}7@MU>zLc4z{?lnkb@9fh7){jo)Q-#fdWRZI z?7$+*meXtjs%45SYw5Fz_ULy8UTDd4c!wUYIJuU@BZUK*QxdIf*3qiC9;!~Ldwvz>10$uVsx!ZblrKj~6EIdZWlQLzyK45xy131W$Ggcw(;I@2F zff{dEsdy2W^Q&3-ue?_OVoQ#=?b*xSri`h&dOrvHnKZrB2~*fm{{7A9O#O=4je3Q34S|fSd`5Vy>b7 zx39!kS^DH8+1+;m8S(V8u33bx{gy`aa>OrL^yg+J5qjPn^nfJ~k1gMxge{js-o^NsFo}$$4pJ?bmzJ zl;EXoesSWz(z)Z5veWxIg!Z#_YtxZ^>4$#Uh$XZSpZ zM~D^y)|)*c2=&2PVEK-l)cPtvoAtzNV^s$wubjJo?@WaS+7HI!E(n71VK19VA8vJF zR9f1EPKtnAlCyTBMmh{X_S#ze_GBcyc_PIA(Ed%s z?tM>mY!hA$7dg?c^}5?E8HFoe_3Ka=egdrM?of8Q4KK<{`-q44UW7Qan+uvmH?t)% zW)LgJ4;DBhKWbHQD>=R2;6iV95i~CGQ-JYM0oj2+m(ngbXQT*IUxV!b&e49kZ$Yd=at$Gh9I7u<~$Sv;p(@-JVLUR9)C;_C_^oRBt4&74u- z4sXh*v4yg#h~@Xz!FZbbE|Y+h+571d>-^I)XDHxwTJqw$mYS-KO;JMTM~(E~0L47c z8WW$s*yUuv(pelAtNfJqAcM_|-&H?;!CJs-0_gm%U4=b--{Q$)a*EjK#uiJBWtRmy zmTauJEY|reQ$}sn1R&$ceVk+Yu==Ol!5^k>_5rI$(Ck*p+T%J&f$8EIBI$K5F1j$j z5xfcaf-P5$*E$a(g3--0Se;S!gBkG%MTN;2Z#t%+F`IX-eP`QI#IOecsChba#tJmK zgm;B&&Y#sehuSJzuHnx2Ra#tTTM^x2dlfR&S;bR5=r0lYa-POP|3;bP&4a}*$Uio6 z%E?ut+U8t-<#(2E{{l8K{~zGl-&;#_Y2NcrYc>J%?Vf+kaeQTuFZ;=qx!2=hz(2=I zogBaaz)06zJm(zMjb!uzJbu#<`A_V)@c%gauRxN2o42R_Z-=e_cgC2@8XLW4dd5b5 z=pJjDnr4SMJAKi^pJ1^g>ohf$@7^(=O#KCd5AzBOQx6Y)v6?ZNn0Kr`N+qecZuAEw z82yIaq>h+B#g*AgDKmB+>Exx866@stjoV!#O_iTascJq#C@Kd3jne1I^2zrlDR}-) zd(<()6k4qM4cNKgmIrf_mOhm6X#Hj9zXSIFAN!BS-`_vuKu0xZMTn`Tb+9?`UR7V8 z{D~JM+=jg=6uW|;G@IAZ__`vx*r|6w09zrBen>zXx|$84z3+cpYhe1mm3!D=@Gm1x(tMt zw#(}46g)A&MG{ae=r*##%ZvQZqvJLMaj&pim8d6n-WQ z@e}$er8=f1&kGJm2DqK>>Yz6WMQDVSvSx@r>{S8?*~y4&Zx6LRPu>s%Tqr?$k+J9o zl3+(Gmm3jhGCQ~#@MzV+(MM)|F!WF*XwMae75`22ns;++Nc{b8&+Wp)(^wxBkk!?X}Z1;g{0ela|pB1u>(Z!(;7olit2yTTbwHDTGw0>A27__kB zofg>rDkIZr$1KfD3Vj5aunR|Ar#Z$-pSsU~p3dr&Nn<16DR&4(?OHocxcwc;IpoEp zCptn5J1TuC%F5Ke89jp`Ysx7w0It|_NDy#)aCDn(_`&x$HTcKyv)pqZSRI^#a4}-& zgWN;N+F_JGNDrIb1R}fEdBK5h4Mr@N9FXnrYVgWwnVplodpwlYIXzl(ga|$rMiYgz z{VpfySj~XEUgP;7*#y(tdxiL&s+8mLX1mKS(7AlT81s*liYI{yjQAZocvH$n1!%u$ z2wevwq=NRIuA7balPG_*?omZcZavRfB{UQu?7|R5U8gbaV`{b+G8RVlOq{GUe!qO( zYI_XP2eXc$M>0WcvFjWX*80A??Rw`ZGlY@6-1ji||2Tel=KYxxNa6|*!t8_COC+Ne zaKmAgDbD`DAwBOLF6lk^GOSPLqzFAj=M*v}7xYV>4uK}SYz1u(1+`$^QJK)5a|}AU z36<4N75+>Pr&gu(YEQ7&nziq;8+7U|2atdL^8s#SbImYto@sQ4t=HPCLx?>j{768` zaocI#9q|Nw?zkl9Q?+`YZ8kaqLtjPN^RCt7jlOK;7#SihlDuZ+tYvX3=ZcECkK=3M zrDnrZJ(ngx1J8E)-oS$&q6nEY72HY_4B&`q%EDd@njxeKXEZ@Dl&ueM;@OWt7%M6y zteF0Em;}+#N8mz75S9T!)8RQF_|cBZdxswg~;b*7@b@TuEQf$gsjd)drdYz1k`) z@Ua4Lc<<Om8`H&Vt6EtkZ{LEyyl(F<^~D zPcAIyeAhtsN8mxua7zLlA>5XMC7I4TLeSta?NdKZzJv_6waA{8ubzBsnqVvHEIA^F zV9El=)6)6iF)&KphEc3Z)Ip|M^N|T}{$MOTJZzTRgsOY4NE#K5F`S2(9Tb|r1qjFn z)=+~+=->=Yml8M)M|HuVi&#V})9d1`?KX5N1LaH@fePW*_X3DpJWdEtN6CqC(lx%$ z8~=hUeRI+U&X(S3U|3~qC!D<_ef(%b^kAGWp>z zK0bI>b12W0*(0@rh{@TF#e_3MJ?K)oP#sG0b6RdC>ht%(Y0?L$hc!@=#&L=L!_g}2V`hG z3 zGZMBI>__OtgxzKQY*YKcaw->7hTDfU5pLKi;t4C2oY{K!eg)7#r)-r_)anf1m=dHM zBO9GO9N|nn)r0Y)aJ(TPJw`eHmLR0NSWg9Tn>?#yB%D`I96E*rfZ9B)Rs=#&8e~Di zo0H(2>9Ai(uD84UPgrRDP|MExM~9!tZdb`N4B+vovv;8Hq2TPH+#59w-umd59}AbH z3y^o5Q5UpJtRZkKyVEGpcLwjY_Atbqw1%roZcGmK{+-iWS~V3x*fAvGFr-D|6{H&} z-^&SpjjV%PfQ0O-v=ZK*?o1p9XR_uG;vyTB1y-#dI;9k1;|ma;(^i}4 z=F?I-1RMA?5d?2A3nuY_kL7EDxu|3amJoCK`-v*-sBYxgM1YK5W6$Uco5jKT5XFpb zNKZFvf7)-aWouqeZ~Y_?ATw_j(r%!R;bfmravuzT{HSi?sa}jjNKDJ* zOn=A1Bf~HJrL50hLU8hTP#5Xlo9MUaKW3TIy$cifm2#V^Uq3yqK8^UCBp{ zUhvB{I&~?$+h|#vM73PiU`z9sbLn)8?#v8kYl0Z%FU43U0xO)vR~V?qAEf76hlv0{$ei%M1@%#;^Ye zQzar?v+5&XvMXukuP!`~ zW^gf}tb13-J5xRcbHDyL@z2>%KMiuWaM+rf3)QK+w~+EdIuBiJKiCQZXG%F zRVd{Z-(i>jzL-izV~T3zA9Xf=>3V6G=KthxCx0tv|1a|T|1r+~ zFK1v1n*S;-DE}<_t*6J>v%84z=RS;hDksMn;`$q2+Ed5n=}5cG&Cly}O3=`F4TA1O zD+VV<@2#Ef%E$FQF`V4UZW>;v`D=R?I4(|3N*a>m;BXG?nI)KQLKlkBenG4@|5|G}^R*Mi6Yk27+me5{p+ zWTb6~^Jf+o(%*?4e0ckogVk>xL@3cO?S&{W8ylO5j12vX$P=DDg~{>p^z`(HW7E?T zzJJqqNzb>c%6z3Xu58XX+TOqa8udHSsbhQ}zfcj~^e9(;_i(}e$3IBr15vhRUrs|- zeHy>Ne~9{ta6y1}+_Sdzf6!WSm)T??&@@lBb~?u}@mTWC@yf~4D%!hmM?Wj0&N zIu8LCX)}ZLluh7+;iMotrMCuzm$@eOE)Bi#3)4<8eUBBHnxMf+;E|vEac(T)Brw!6 z?D0ymoHY;XGPVFdaK1PqSsFTto&$Jicwc&e`Gx3ADQzL-OBWdI7!_l@r**V70SV54 zdP9+Ajya%%!S?aq#ejc@ydx2?cN7u+lqKgNHx>P6@nbXT&WkUZBbR(ij5q_2m4~zI z>x?0R;(YZaVrC!LPIJ%@y>)g8Yhy0w4dES`CGMmV+6sufwqk`pa3jGhefoURZak^L zL0&`M_{^d2$aZo@%9L9yXN?<$-(E546-Zp)oScy;@eY1k@onV;v+a?DU0M?e!G_LX zL}o^C%V&|CJze5)#EAiSl292bJ)R|y_d<{N^QZl7@#K@xrcpIpI=oCYvA|7(3%Y$N zp8M;ZzwgBGxcE-c3UnADMJECu^S%WK+fk-k2}1kA3wZPb^v2tmlE=GxEAc}odQ>g( z@$p_;lZwImM`*MI>T%8)^fK!lcTYn7n>a=es$5so-@# z^sS!@>ohkyG1UwgATLeI8Q5TcX%z84R6-B zNOVsht{yaki0BOqZ9jMLHW%#ewB>~^s9!qvrLSEygZYJP`nIAsvgp!2j)b#xaz?qXm!V!xjvwEV~;9q&y~uwOPTb(4pSWj7_1NI)bI*cD7r;_ zGKMAKl+e-2EY(VQxGUk<0D>zE2iSFlH(sxeMc~`z*o)2@D=#g*XYZE$iib|%FV81| z7l}Q?8bCN6xECo%<#g2Aqw_n**Z;Ar9ENSkc-SMa)qB`}hEvHvcge?(+sQu6B9j#n z4ELYzI6uJq;{hV@6{gAb2(4j+KS-Ctbd-9|8gkeq#|Bsu<;3@5?QyGXM@$S-o8|+{ z5)%SUJ*MNjs~p}0JZeN-vC$)BtN@OghVh6+Hw!m?W7&~8NnO31UvV-^Y2OiTN=pGD zh-;+xB%7PTvVmh&fmve(VO$5Vl_p#8Q!DTV$qm`H*GnZ_-dWgCH?yC!73h=cZW}>? zx%QD`5I9jD_UiKtOBZ6H@AS>78oCA-X$9Zf8!taRXrCDAqgvTE0#$Th>Um|>iDzmw zf+=T{t%`6mQHSgAKN=ha<41<{{DDwXzizImkG=1-1H$prGne(SjJ*X)P39-xd?}t_ zg{%Dpo(MmLqx47YgSJlq6P{#X*_Nl+bUH+(nJwQ+ZO@)Pe}LVD5qe^!mq&huDNv#@ zxE*wm0+=(_v41|^6SvQG=g2u3`bgRoxdzUTjfb%Vx~o-nDY4mWKxDhO?31K_Ps#9` zG5qI_ZfvJEuHR1t{=I+;p$8KaRIz9VY;K2gYxSNtlJXGFJ61o48F_^o#C#a;wem3S zr=CNO2kp(ZGDxyw0nZAjw0pX@D(lL%VPZ_Zuod`dmOJ6t5#37%@YfBpOOSUFGaDDT z16)DX$B>=nTN#3r$`}m#(roYWMvoWi3%^)fBd;Xu*PC64Y1@D^^H2e`)y1#6fn&F? zd)IHLWxp;db=v`!T@mtMo9mTCsa+Tha8^u^3jWo*+p}u1d;sq#F@`BHkxB9o-ZazD z@>t#Fy4+Qr9Ewg`6&qR00EN>7S7mXFYH+bTAsI?7J6YaodlJ>TMl4heEYQc1xU9TY zt#y8h@E>2!D?H%Y&y;2xTE__|YI@{$;Tc1ktuq>Z*t;pxA`+}$wZC-S zahW2|3N;;)(=Ne9q)asQC5P&F7r#$EF7n7;Z(dZrqweLLlv&eCzrV;qz-?IRVYm^! zKLLm*P4zj?U|x`Y3cJ)0UChI81;+u9oncQy$q#zHgpKJjn_^Vcmzl`?CgeBNr9q!G zJ-+gn%D<9wZ~^?|8bE?DDPhc5%xR#vIq^hJ*!9Q^A?M6ZQlIdJWgld)r6b=CAK`qh`@1-nhPk5VG3oL@51U-o3U} zKzrCO9eS}QYFjqw0*_dOLgVNmHgcym;3D0!J%#yw-jS2FusRUO!b(9wfwUuO_q27e zKGMvkF|$2g&fa{mLJv+ofFssJ&sTG1j@8M}p&nEi2Yd%Rk zc1Q|Jamg=G>jquOtMLm0Eh=*`^(TG_3GV|Nd&>kCSYyzTS43jtvIHN? zLPZ!LV2pgrdE6vz+tU(sBifK6$Ly`qfCdV;Y^O(FHGa=4RciuMi-m<%N=6skpp%+b z*^+8sT~+^TG#iExG43X#Y@(<&4yC>Jv5)(g?nQU&)DWqhg73XzWh6d^%P56}^MVuZ zspzT}P9+6p=TTYnN#CPDqP}jzQc;9$LCF3Q0l1Z}qIQrz9eTX!`{U6ueft_4{sTV| z@1Amn%U$#Dem*}KaU)O0jlipzujGMAQqfwb%l}5piIktl|ET6#sfhLJ{gEH_1BnkO z1O(w%ra@~UCW;GXXOFKdNbMLWEVSx;%Z4elfZ(}HTTP|qUvs%5E!dUv+N!c;PhpSgT z^;>v7X$?6Z4xE?-<4Ucb4));afgJkpkzvBWaNi(ya z$HLLJUf6v<>50mii{>ob8S;LHVQDyV<*}Xs6<;|C1`2$qAiI371KhutzEe}5hV-(h zckUA92bph4J!=IK<4JjWK^;+Yqm@?Y? z;EFY49yx=0>j3PkU&`vycR9D@Ee#9Fj~V}_UB0@9@UXP*qn(AZRD4)2PtRsEV2W9Z zm}xB4?!vfycUNOvMdf)Za^3N<(QVC+zn1mzSN z7Wt<%E0TcX)NH{EqYhia!mUCx+YcH>95YMJD=Y5}rEsCRsU|uO+Ukrw7@9Wg)U^fm zqHFJ%cf}|2Y7VMHYdjj~`i>ujSm{zs`DW-9DIDB$X24J9h0c7{(0u7M%STbcZYJ1s zqG`{*j~40^d17_BV2ytW*?4nW7aS+!9UI2D#04W16d-2p&SF*rRy=_#61~je%k82T zZ}yK?nv$`_H~%O8OQf9op3^$#Se#0AUN(9vAp)NWFW?9IYJM7d82^{IyqLR5R0B$ zrAff)aJ@WcmnqQ+Y_fF|o)L|2$|unc zc4_5fBp#Xr%zEU)h?j#Q8+u?qOlwRDXYW?mfor16vt7I6u%L^D)6CZtx7Log3g8*x zp_@ykuQhUA^C0-Jpjv?MWoKR%%%`k-s|cl08fbNNaskNnY(0MWm~z_r)yt={B>%}} ziK6l6D5wa#&wfWY4t3lbKTQaAekSK1^vnw)qb27bntk3BZ?>QoIyf?1vAy#rC}-Mj z;I0M9yjsx)9YK5jvNU*1#j=&y(W;76n9jRH{UxuXyTSv8Y;Y*f4t{dWoqx(zRx2C& z`iR38MLFSqoLpFpF)J-<*;`@Cu|lQDL-|`zISc0l&QKpO4h7J?b;e#v#~OYU??}Zi z)a5RcMjT+3uNmfauBtzTAHg*&Q724tg0K`OzfqAVsQEp`b5egzzm-(wO?2q?=rr6; z&L2;cArGn0Uk8=!eWTxfkv(;NZ3`{P`|ZWE8xfzMV5UHm!Fuaw8mr#sFk-P{U5H<|ER}R(H48AF@_q=#E0jybP@EN^oQKnQQG6tO?jDhfV|Y|_*w5B zlX8iZf(nPL{Y07_??w2~jcW4F>MHigoJvd0#I+up*5=53};HVIR$i)ZbbJx|jb*ToBA&?x4!u6q=4+?F27kJij~S$qK!PZ?9dm@EAm`S#jLva+n0dR zobdNn`CE06-++NcfTkP7?5_+>Cx5hxd-hm6S ziwHPc*#FAMO;21Cie2+q!sS6Om#n%J)AjYp(-~MDw^ydz0;~T{ZJPDL5SbTP?sFij z0l24kqJc~e6cDSBY6iGC;=uo2_M~h7)<>#dX{dk+-u;=Jv9GHE&T5Yr4!g{X=l52mQAz2Y;(l>i7Q3jlIddJ@-PC_J0-G zDwW?QCtmeA{-vc+t2+EkYay>592{Io_*<6IzW47o0lLU#DAbkp?`xI3_klIvSKN?O ze`I8&qOMMhn3!0*`fvSBm#&ojTl|aRrP1oTx`J2UA|k6X(IQei5B?$dB|BkU#)I`k z4LM%@C7~r|OHwt0!|%C=vIb`ycZbBW(6x?z{h8 zv-1D3v43_gpyT7=`8nN&g5mvI<=p`~9(zABD%@z}g_rAim?XP!hjSxVsL&jveN?A&WF~-{FE)d#0q0tIUQDh+^Gxy z(8kSf65PZZOonn}Ob*SEu~xEWHnO8JEOUMv5^nAPsefG^xl(7@Ai}w@U})6|xVHeyw*%>##*m{p)e)?O$YL% zsG{-*${xmbJ{#V}cll{UM!3=nTnAAmt{Ode1!(W{VFEq;Zs;&5zfx~zq?i6I9wk)j zB9_q*)S)v3Q*mZldgm{l^xmjsch>~gVRw-;R4pO7sqF$*jg9xIT!~%V+7v^ovTqEx= zFq)M-`WCE5FeEX|VdZX~KECEq>h?(6Y2|&zPb~ng+; z0@aQhP^d9T%V&6?!CZYq{)CqwkeWQKO9S4&#^X?O$;coC=zG=k`0T;h0y2`o$%V|G z)|woUL5uzw5_9eTa3VPwGb`n$YS8BngV%J#th~(KZ$5jU6|u$pZBu?K9cSHf+w+6b z)zKY<&zS`9vd;T_TkcdCf-BM)w&~8_U1(($O{CvX>Ga9Ry-^sb{<`wm&h0C4q=Fqk zuD=W}tK+JqPMY;hGvs{c7C>+MGgZ*>nY~qIqr0B*1$RDZ!f{s zrMfhNELADfYQY{kv}k_2BF$huw-@~fg2z%%uDajeR5OEe&d3T>%(?G}+}4fh{60-y zGLALc=yHnUd7k4|UcAf=$^UdRldx@ObG{+V`8x8h#4eDD_S%aMS4q`cmVs)Oqo;P$ zX78F#tv*}r-Z@~PXG;76X)Fx{c@{&hGeq4N5Yi`Ff zG(Q9c>6Im_QF*IBswpFnmWe}E=aNk?TQ&q$jL_`(^;A>DF_wxtm~`BQA67qo+f()- zCTQTp(?Ucdy{x&-+1CwfU`CM8VoW4;c+&(lyHgP4h*2u3Qt?#^)7Ko#oARkp^f9PB zFMIBx)!HLe3VDX&-bl^dQ4rfzIvehAR{`;cCH4qs92I##AsML_bcf~ zsQ3EJm@>mesFx{AUDxy-+E7>Y#;-w1?40Z<9qaJc%}s-ZaWuw+`KC=|b3;w6=;qCI zt~7|tmq(HdDBE5I5w&jHxl`e`9d%O!gKv}F7NhJM0MI7W(7FJbBq%C%W6_T-ND@LH z%)dSFaD)DU^bLD%9*+QS9D#}8Mso-W>ahEq}Wp_EpF`wJC#AVIiSK9nkROeT+o+0@% z+KP;b;Fzab+Z4!lflry_YobiTnaQd5=Kx5mSf`AIdF@-BQ@AdgnJV3AuwJ6DbBlgA z>`BFrTeVp3bM9}}*XIsbbMihvXFauvJwv{5Ru<|tlqpx~lN0;-vhl0F+zNX%eU_?I zvL3@*REy0~g3qsLAt@;qrO-jI?p+5(lJ`dpqWkwWd_CQY&rhD!ks1l!kOH2sdyn*L z$X8;E&HgynrAJ|1_Bo;weJd|J8Y6qiUH^R1R;9LAI+JQ`z?khHbRB9nPj?th`NuYH z2qH2k&A*xO#}6Tz4w9 z#)ZZMUrgU_U=jwbM_j&84#x}aZBSA!0ke7oc8?58p<*_n$`gnR=Or)T4NkV_Gk4_c z!zwm{Ymw%MXFeg_A+NUXBYi{#Q}wj&S0&~tbMFE%X%Ay!8@!+$DUM0$&uSaaU&O@i(f`POc*Fn&zgIl)C0wsc!TyQnQgDF$Vlg^tcGiLIfmccgtty`QAlae6eMpDo9p zQRNb`8Sz|x`0mWpk$IBi$;Ils%iA4MM=d>{0Tk9oRkYN>&X0ZEr!+>TN!_}0k6d*) zXu@82{STM$L(`8J0I2gwP4Hl9nc zV3bS&d;2xtIZkHraLX!p&3ffp$4onuFsEff{`E(qtZyq~=wljwtQ?G|T)-=FbkNIY z#xbw0D#?qLC%kG2tpq}-eQb;6Z6(#U-`r@G(u%P!l>VN>qByTz7{OFQG1nOv^PDy0 z>*HRSgqivNM(#peox(eZ@k8H3&qLS45Vuvq+0NW<^iga$c@ybI-u+~S3kD`e(x_WT z(CN0z=eb&8Uyqi^>x?$UznaTK&;CR!HVHG_9DCMzIPv4ixg=3-*oKTZgR|jLZGs;n zZO~Js9Q$L`JM52@{IK$MEAWjUA3h4H0hJ=8q`IH<`${s7AWok_ILjM67x%ITzQH#X z6K5h7u5SSzJp3M&PTPsi`*f>WsD_Mvqo+*C@nI6CUXbe8$^NE!X@4%AvC9Yg;m8;9 z;6IqNo_q87MP<$==)C~U7EX&VBp>U`g;qb`eWCrWPqa8{la^@7vw3Jok;TpW$)-u( z>z7u-twbOJvgjCUK_sZ+on&j|G5GLLh7)M@ z)==vX+SCrdugP=OF2pZ)DqEX0b&W2f;aQ^b&}Sk>CIbUDKNHcr;&DRI8o2>YqLOHW z!ZcTqaqSNVLS&Ey$h!BXsW4!|xVpW%eRB_x9QZ*VxX{Br?ov~HD%%t4I*U8qidGm% zqL473)tZv?i)Q@P@(T@RO%St3I4E4;clpFMht1VIw1Qi<13$8J!Raf8{@etDnA*%{ zp3?ho+>t4eQKJ@WXO9g->Bt^4dx0NLY2hpBi;e69sT(X6IpY{hQd8i74=fB@chm znVH_@2eep3?`D^#y6@`-zF2Y=w?*e^Qdf;Oy>^`l(x#`dm>ik&l^dQuDx5a`Hciyj zQJBZv7`K3_c%%OKR&zmr4pRB3!*8x&TPw7P(zJmtra4!;;fIfYo?X;g8K>EvN~oSB zUqJfpR$L;oio{onD^W}ARQi9I8B4TS5WYx`i@1hI{i#WPgls##JJ4r z*{T7^ovz|!$6cxoG8V-P{~ty~gQioN3tD=rE|pHiElwVi4_8`5B>NhbYZHKu;F*lX z&d#T1B2=ub^7st9k@cd<;q-H(5iiMC`eCOwJ$gTcyagv0C_P>tiY@>C$mqE8<7vgi&qHwv-ZKKhvhIF2 z1RqJ{`2}y3+Y&vY?CbnaxBDR2rRG)lABL!|rSE4vFyrlsJ6UX$)Ds7PkbgqHBEqNE zIlp~-Z}>XbillOn4a1%kGK+LI9K*FLLSl^0q){wKE_VnsM1^eV^E;=wv39_&8ag*W z^<)DTH)z31b$X*O z$?BrTcImu$?moq9ihhsgU_tVImDEGk+7&%_+Yvj7HT(YLTZbIQDd)=ZBB@3GgYc;w z<@v{T4_0B~tkRk-2)O=S1t3f4a3?6g_mN57(T)2vv+ID2Z@xSY0&$);H1sbH(#-l! zV-~B99lsInR?f`&E)lMJkO3JLD;(`F9X%#iXxP_UMiJ$DgVz_`e-4a#YrOIB>%i>>DO9_~BB!){oj}4`YAo!$=nhPJJe%RV!hl zX`1?nbKo`yQCVb}HXkcX$;2srr2zFP=Oi2b`D@=#! zvG=hk(WL$-QqTC$jnBH*lCL>hRo8Ee=|0juKr!LtT2*SH)+tg*T`^Jp=Mx49j8A^BqkqNlWbC^gvA=KYnc%F=WAfOX>{y%f3DDks&QMTi zh_)c_L_iSOBhtI&yPvRBCRpa++vkpTJFl}>`r&@^n+DY5&uSB@Vj2sjaZCd0| z`INLBex*>k4hFmFFb?>VbHoKJRz>3D{yZL@6&gA#C)GcFe68WjZD-g4Yx(w_dQy^d z@xY)V9-m8pT!e}nw>kHa<(MNytr+8OwZyF3{PaDi zwzyg^jJW{LhA;Q*SMm;va1_UWK4&o!y@1gNn#l))f{W8NAr6h8jA*jY=7TPjm6L2| zQjgLV1LZJ#bML<9=H`u&p4ld$1_UvFE~eQ|I|&*h3C3R?ype{4_wE|H`nBi*!%s52 z&HFpVOQHoFh!EO47FI>?Z9u7cVM=ADNWI(`WX8XF7y z-kquRyK@y$u_<4*81Il$x5ho3N$`u}WX=?1b2L1`C>I`Xh@`yyiGK=67LdDAzt32l=zUQcHC*~| zpCSF3(9ebY$gDJS{RokbOX5ftn%U4Y17&yhvrb};eDX&NJVLgmJ<9&IO$^XsmHxYU_|v#kKOcxY(|;`rnH$|2aCY| zWj8x9jEUf*!2N|~qm{Sm)?E#z%d}6+>mX{u%V&k(b%<##xI_W5XR75&v)^E{)c9d@ z?$(@M%DcV2@fY zhj3@((wKH@6*n(#t>p(|Pg#svO@7>Fn;6K z{mfd5%`1NU(}=M5XDN+~itAPvc1UJ5jq@anp`0Nf%B3?DZ6no)ppK z;fyM5)&rhp_0lqOC`*=h3U5#IP1#7VygVP}uC7XTO6zGI(T{O^Fd*=AL&mVV=XM|27guUQQ z0&k2>saIb`Xmp<6jTRLNivo^Q_dqx^M6^1dV#6SVuQMW$$!!zWx9`+&fZQP^tS;BR zhet$Yuj;>x*J-0&B4KC{kc>OTt8#4dqVO*Et%d+KMPCW?yEmS`MN~7-(#%KcZ;rjG zDW5MS{*d>{rTKlQOWK|e$3VYOVwRRN11>q{al=F|KbI22NmwXm=ZGOwm@+esqG84d zRyK3=g}g>zA;0d@Sf9uxEaq;dYx1}^0qG^>gK{*?U`S2kJ$PEY$aVirN=A|$Bis*d z?2~;By*U$b8l|nuIt6{VpZE0}d1xE>XKLeu;1VXBOv}V38cqe5@|7wP@3tQDe&1kI zUZX;L5TveJEXBTOoLUbU%P8Ei*I99#xjdDl@zOVFAkGlg{_tV!N#NBZH6*mo%Cl)@ zZjIyj$J7Tkq|jQj!_65jwkj`dY}G}cc2xwT$fd$iZ0TZ>SJjf?&Awps8%45)HKiQS zZjZ4ld}fjp@HZ7nec0z)$U-$|M@m%cn|=0H^|MIm{b3MfhZ%@UjsJS{wgF}BVmOHQ z;GX|A*z|l(9-p}p z5LDn^5dK7%Z}XsvtcVwiJKL@B%N%2TlTAMRIgzG9m3&wVmf|dA6^%p)&zZn((O8I} z$1&3EbHd$ps$r}y5I_xk4$I>|{f$&^sFh<+?w6j%W+&JVJhWS)>MLZZh|Mo6k~Q1c zNpXHy9#>J=)6%J8p->L%KKUv}`}Q4{y}#TgTXxh-b91+h4vwhE%5pu2C)8%+_e91b z^3eZ1B88yu|*E)xvMnIjhcS!Cq21MPaTxwPal@rE$ifoJ5?<2KPtIu z_@4gdBhsP2DJU(LJ3jSI8S3nn{OS_ftD-!p>0iv~6&kmR+DWv=sSo}vD_Y=pV0VeO z0{mhch?xzS$QCyHjhXqH<`V-%`JCMgh@ZmH7xJG_;pOo6fYXNbzNMN>dhK0@REZOz z@&4h2zHd6v?0QJoUsxwa6@?NC2V>uAnGa@py|I-=lCOeysBhS9AL<{Gp0-~1-bjvB z?-KB6x!w>hUx)?%B0>qL^e469FTFa<)=4W8MUk)(97)gV%#Z;Ug}!iz>bBWJACiTF zpR681j52E(0yg{}xv^Z}pnA^k~KFe{9i`n>rb3=n(GL2;Hfulr&+lGRWtkdT-yokLF zhG8rs`aqvC;aS{!1kbvQx5}Dr8>FbRR7OT4ZivfxLbFD`bC@4J8b zuwgkt#j~|rh-FTgpZd@Qsa$y!ZY{iWNbe0`1c7rL7}q`0&uAhx%Al88*8O@>jt^#cdnK_zj{o+nv}fa0=>SCvgN7^oSof%{bf?Q zwnlQi7W^2ZisD=t2OsI+S#DHs#}~DfLb*j~ZoGKC?ETF?ComDEhaNei_d8GOwy%+Z zTiqk^y=8m03Rjk`-2d=lX>Vwkp@C8No(Y-8Bbu*MGuAkRW%mebO7rB+zjJ~7#s9lP zc5W<}kUnU&W%=@!8!nJ{zT#rJq`oq?XKJ84FeZ_Qiv^qc%tJ^o2*r%vKU$k8bN&pN z^mec`!qlRaie5KFO0No*WRFBzmEQc+unJCYaKE!_g<0Jy+${Zuu`;32E4v_9h6jcvV|-lgwhEh}5fzbHctk1P}?_Vpx*&V$${f1d6hb)7IY+EZU4nDNiZZ+uGpk^=o( zp?(Qj#mTa6e_GCqlru?0LPE^mNS7)F3JRm#Y^XZi;<>=E# zrKhc1&lQmDoUl7jb~yW#h$IaGO;=e>spM+f8lF5TM<3rWL;Zu!w<}RmJfb4|kY3Bt zrw&L?f%>NU+mhM}>2B?kgSws2YGRWRDZPy z>XYRLUzmw87n^|QL2-4l6jv3UDnrTOdQo#iuBX+9a=|#)b?2{@;r?M6o9EIt=K@(p zMMXFBCYjcN>3HEA|0wqEY)W#nWA8+M{{7pe;{4~vK6!NZ4a={dY?M15IxImo8g=Cb zvj0Sv{PXRP$q(*7AV*uyj@fR1=3aU6@4Y_uNkvAIH<)6Ttg3?9!ovn=z|A7mOL@{?LFC_w%%b3Wbm-3du_Di6x&AE{q@MzeKZBQ}&mm ziX>k*7Y5vnyp#!-2C_U&v)c>n^~!$g(nwG2q_c^<7IIjU$83@*jOB*ebQur2 zY3q6!85nYP`kL(g_^BSe73Vw`Q;^2 zx~9T~`!gA3P@iGR%W>PoDgrrI)!Hhl+p=DIGcu)VVAMrNG1*adS%DOl73uK{q_DJ5 z#bHniiVO9XFU93WlB>rlsVs55sEE~KO;fQRx3Hu@2Gxh0&~vsOX_ERYE|i?IV#(2G z8{u4BT`pCd>vUU@R!TV%*0e`8ZIv6>NJ;%#x0ZfhL7wEONavjSC50}OnHnpvC|?39 z!104p5pkCYy?kdoeP4#gZy&yE9EeJ79%EF!I)JgN_zArcqRKMeaIARnL2(^A)fXnk zekVKbBpf#?R*6)51ZOI=k*k+IK{AFGjOkP zIx(!ynR5?lhFeb);lcyJSTnK+fiyBWB7N(YF3&I@OYIgENr=bY&W&X)}Q z4kJVQ=iB<9N-%zmW1wY@B{qsRzubGx9~uGNb;iNHz~IsP$3Drzamb@&vI()=O*gpa zGm`>%4FR0l6Rq4iL{O0Q={Cv$dC6E+j549uOaj1IQnt~M!9)U6uVpq6SF}2E{8Th1 zAgoFMRA@};*MxI5-WckIpKCcGtGwPdmM6)QTrDR>DmH3!15%x%0x_RShM2urMTYKA zWCQn-M|iQSO?ulI2Tq0PHzv7%^`BJbHa19o1Lq)M7r4gxKvBoVpq!_m*qs5#<5?ij z7h24)h25iG;}B(*amao-cH!}pYL{dC&V@DV3_rZPRW{ZZNiXBxG#jap^3)-q=ac0z zy1IweIgQBL@&Z>M;6xUvSS?luwlEaZYt#I9`+C3e?DM2sTB1T7m0`6FN1J=4wj$3x z+Z>Yx+b1;5EV;o_7J07sX!4ZpF^ts&rFtk5m57SeY!!exRpm0O<-Sw>vL1E7Gjl^S zq++sTXjCS3`;b1@?SrEdRAJc7avu5$tB@;FAt)13$y2?wZf%(q)s#wqC|d?q+=VhH zyqQtUN@P4gPli-Xay%lQIRARJ$rUP6GxWFP<53l*c@kEqe{xdam*h({Iwtu=d7?>_ ziE%xj+5wg$iRiKSKXE|XTDqkBM5}tlS+en}3#C<$Jsu8AxUx(pa&jc2Fkjj<&Anq$ z320h#t8}{x-d6Qh`}G(ZdagdLf4lT|K^3T6AEGov&(o`UGaO6jVu5KvY!V&FD;^r# z@r9^Iv0j)|+ItyWO5=NJ*b76(_F~HspZF{Eq>^Srf!T&Xj3C9&Kn(j!$1oIUtqS19 z7}7Np1iq1GPFWTib3K)d1^S33W9aeB*Rqu=2pJyHvce3+i!HFH!vt!K*#v57VOT=? zkTtgV%btc#H8hFclKBc$zzLx>$oU!ixbm?S)9TnBoiQXckV_W2J7 z<(Vgr%etNGr9gj^OpoVuI7eDv9GeG?)-J#GYBtz~w0mN0{%J@hq=K|KLl=ARdRG!8 zk4-qA2>fKOcRt|g_vyV2>hUq&oQ847dwU{o7E#Y3_R<1=I&iuXA{cO%_0gZW@fK`% z4HEtjlEgRmObU~11H^Q&4sJjz`$UZr7;3_NvQ;?nTHvYD2J(!2Ck(VqD^o&2X#`;` z4cmBjnMIdL9xB2C*CNdQLda{@r^GQJE6epf7nFqDcy<4|_Ztgl%4+t(zRq?#sQ6i< z?&!d{YxQOpYRU|2rq1;l_q7n|oXbyfI>_&2e!_9*n5=}*7^KD3J&y%JuQear>+Z38 zW@y3JYjWQt2N5wr^+xwMbW2@rzKlepvR)m_V~xGi)Y30ig}H9Rkk-y2xBua$UMD8k zmgY;A=0Uq|?@;l&LmlPT&OvEt>Ql!rpvTRWwPgizsG&!W>9&!vi0gf=Da+U64oFjT zzns6e)a7ArW4zM>wDGaG78&na8V5Q-S1|PN&T`-1& zukZcOJP#4wR+_B>N9}iMa8f1*hh=bJRE9&3m*c_ap3k25hL6BFKN-V^QN-p9W8za!MQw6a)&`MFY5S1adTvr~rkun14m zH=ySkj6~FLh{~uMD$dC@c-y@+a9v!}2ru_O>hgl+%GhU2f0v3nXf_aKgVXG|p%F)akj zD9N7gFr=B@JwnuR_-9GhiUUo~i+kW_0q@zywbOurnB)4Hy?I;l{3{aw`26MA>+*xL zUBxNlm~;9hYM`P#@bW@yW6yfr_C!#AkE$p=IuMc0k*GVzPCegxEjNkIF%#Zt{YJRt zA-&9KooHEs#e%ewOzbNTXmKdR&7|%{Uu3yS#%OF{y;K*8q5IC(AOJbpL1|TSSY4i@ z&fO6AA*AC|$PG3g(SNJDGCY+F|%x8HkM&l!-tN4sS2u`bzI z&2W=$*|n!d)>h=pZ}+syusUE5JlQ0pBN4f9W2tUyk=n96dFo)h9&1eMstV*AKiezi zIoa~mp>{c`u3qOS zTnopD|GmHcjBV@__ueOd`(lD9?HGzmNY6%3AJ;}*qq+SBmoWZ2YSbxAG-jTcviT_emS77=f3b_L{m+D zm2A6mv#i^`RtCBUWdE+i(s-a*@2_6i0@6fYwl{Rjk*AKyrb{+Teo21po7B+XJt(<_ zxyvR_)14XI^wkSRfv`(7jSIBZTsWbQ3cXV0Y5*Bm<}c+!_~{VhRrl@b9hPBzsxu1m zq!5F;K*53I$#OS1ggj_GfOB;!R~r~OLTWL37`)ja^yFw?M0%2Rs=L$(bJS!EsS~Cu za_BWn1j)mw)5tK>@wm@3H6&0eBy+8ZuQe*XW)iv_h2s$($|N@N$K@TLshkMWVZYhW zx^nSEYwSakaG!9LTraIY`-Br;R-P@}i-T?qb29i1M^k$}`9wLvDFFWE<%wBwi=#8` z|J9XYxk_KONY8}-`*@i6jdkg;SDed#C5Pt-(hIOukVV*CUcN&_10nB-T%jklG&|t( z2&Y8 zpg}$E5Ir;FdIm}ATZ9_#%2G8r6?NZa7Zs(#iCW4_>T`IHs~-10L#+dSt}VH-G{W8)!q+-8an)aU1I!%2`-G@AC9f>-7i2r(Nd04gEgT%4D;F{37z_k&e+h=N?-dncxuh&xeY2n&=0k&~Pf+WM z+RfFn?W)aESdu5lo^F)>u7TLMb7OR9Oqvd~xcA$x+9Cl$c~S`DS%+0q~+w>vSXVQ6jCkMTr1Lr{MCSI(IN$=c zu?rPsff_E-?ZXlEEB99~BjI&$TK_&%?g%ZP4azLSawh$N_;G?)cf#n$r=2_{+=tnf z_i7#x4jh@``d2z!d7@D0>0nRpC!xa3_FZ2Xbd3{K-l>34ekx&KxKcK_Uwp(4Ac{E` zt>MX}npwR4Nh)y4uxIqbG86exwOxd6agQl8$v_z3e-PXKLXu2nD3Mb3n~?j+=0;CzuN0Q$OL<*c zY)fi!Lm2g9nbp@w%yea8RTJ|~+(U=~Lm&|5fK$i#Q(BjR{VwU>>K6Ti$a-szxgm#c>4Eb zeSW~LZA`f()Q@m>Lg%p;@s~)?`MBQqLB0OR_qEB%lj^7i0{T2o$ljwJa-y|g)>P!n zs5)_@`V1ax>XpY2w95-Ft8-&jnMs(X1jvJug0MWYx7Ce9MU0l0Bjd$TesR7G^m=2% zfm#yLNxg~<O=DKUNyp8B*=) z0JWFpYf4<$EB1 zbErvh&MV<;*k-iNd^@ zT!|NLm4w<-rsC1t>I|{}TR+5QI!FsI9Q8s?FXeAKI5w}TN5k1BEeLgYX2BR~Yif+k*O5ZoC#Yga$RnW#J6)cqAjz zDPK5%ln0)PDL%?sV2`@BjQi zN?k9kVHjs~OvMHh7GmGxANfqRTa$4%i-tHpQC}ezXq2WW&2P{{Xd%3?efV(kCLjAa zQ+*i^ei+di46~?Ho3fIGD%VeAB-u8oc`&u!)2jbeBRF5t!07x=&C?@&o}j0}ohTeS zYvRG?GNgirPkLMD1C8+H>&{Zx2y>ka|U>D674}{L626hI{LKw(eqiQhIb|rIun!$ zjzbxw9LxuPPvu>cGyQ}TtBt3-X;DwSRMm9glx!{xy573whTZ@oJP??SRj0L?ia!4K z=`EGE;i#M3jm8?xIE`t9OSR}{d<`ZW!gS15o+%^PI?CyEW!ndvMC&^ukzNPN6b(Bg znjR)RO5r1U7S!cDW2BK*|Kt5pXD6AV-3R-5s!r3*G&>Sq;Fo=W*HgRb=P0v!G9iKP z>mnZXe;lLJA=%#8Ha`2uVH^8;+q@-?=&224di4cS6uQYUj4AL~VW(&)ju(UbcO8-~ zm)E;U2H=_F8T0qyJ#$VVs;JX^c;;zuroQx_ImY#0Ql9PX+FU^B(E#3{B3ci(B0#v| zBVdo|m5je)LVF1VCPEO~jjhMd)3`^k>(mYq#6OLk)IA)>O)jan{-oMlhDgl?^8V_o zY}a5q+g-r_dbD%Y^@s!a9G@qnu~kYY$0avEB*hg)lFw}GvGOnz^iDFfFTKfgf!{>O zqEfb|G`3~&=I5SHj~v87lfp-3=gNkvEu5cw))S9_!KYvXog|1lvB17LIF4V8NGXm zCF;hYR*ppTFrP;}dw+XCwqJc-?9WGKhMrCNqq!#>?rF*tACRFd88}gyq z*&p+bSk;1UGr`%h&-hRmm$IhxnfG`$sAou5$FL`)iB{7;ZsMZu)*cz?8gOff&IX~g zW5Z)ov98>WB@KoGl9_qB@fq)Fa^lP=Ji>dsT6&x&DqjMD3i}>9D4Qz2Fy-4bwZyO6)05ZECkND zJ9@_ao_oqEJ!j3s-V#Xk`o*_G&jt=c_t>Nhm855F4nKAH8T3fOAc(CCB}t zAu5?hDC~sq5Ya--QQSlxnzuZL$>bA1$AP0lMIN@frDm8oDNFjs#-&W}|8za@*f1?d zZYTiPi*3Ro#AYGr=29*wn^PM*D9<=1UikB*a~_x=jJ;?8pln`P5^`n8HOgqZ=USeo zgY)5s_>BsLI^&j*-}=$n?rwdKcy8wlNoCew&OxXZV~$!?!onc!^AC@@?}A#satrhHT~wlDdY|>xe!Npcxk0I@D_1d_sIYM5@d@&CV(K>kbE5sv z{EqWBKGP&?x7MU|oz-M8fg0n_Uw5ON|C+yveVRYdeE;v|pT2v0?9==Nsy;(*2@xJ- z7BR-*!T|E%VeC3prTDFLkdx)D#fNdjkzrxtM9k%Z!ofmlxK1|J#{E%w zQZ9YCkF;9S>q-kG^)zdg#TN_G%6*Ri-Bc)(lx=pQN0+~p7kb=~yNKSoG;ne(*8(1Q zZRrSOZlCNNJtZFJ0?!_zi+ecRC318sZt|0`bLuoErQmmo^2kq4{C0#T>GdjiZ+z8k zA-VBTZYfKYDQ=}d(&;(g~DJW`f?TwiH1> z7co2&~I?Lhw~BU%Tf}Q%lW{Jzu6NR($F&CmYS(3&XwH!pmdLn z>GQ&5NfR;}9d|uGym)31%cIfgyw7PEs~!9#>Mg=-X+TYezRn?OJJBxr#fSzKJ#Hcy ztuOO)rEijkqY14?y~*F=A$*h`LB{M)6_eNq-}XuccwGph*W$ueB?vD5!X0LuJ9Z3lt$EwFq2j;Oq4DbeVzT1 zlb0{Q{Nbwq3T(&E!6&_I_+6Dn;dmlBNC_J*$~nOpxdzG?m3KO}Ff*al zY%R(0#AgFpO~pOibI8}+H}be^bleFSW`lI&W;B23d1PipW|kz6=mGS{0r^?RZ|OZgB*;UM8CGL%9eOr|~UWNYhmN<;p?qUULmNo|GMICZbVKB^`Pm92mE6 z3g9rlI3-WWOUf*ho=|3)B*hoQTsOTWbIY;Q6o<3r*N-WJLJu&misEhUvjQdV8; zW;q=lib!i?ha5T9?#7)?6{kRhuy13h!Zw5$cEWF1OBU(`Z2C%ov=W6y&-82)1dcvEe`CJI!>A<)ombz%A|4+Sn@F zFWn|rJpW4Be%W@dTQtv9us1if%b`6-WcNeQ$jIQBiq>o`d!w@J{~ncx^!4yBAJc6i z86Awso`?3zLw7zRPe1sK1VcG8*f%13A2}dT-1oFR@Uw?xpli?_^S~2_^%+nfEfAC^ ze)F^n-hduE;*R;?&v!|8TfdA9N937@_e*(QrQ{XmxixGJk9eQ#oPtLe!^2;HtRc@c z4>+?^P93{Ov=`eM`#|^u&TG6QheONvIGV`>o8incV_L^Z)E$!>K{|M0MCWWsr|gbH z$0lAY!N8K<)q=mClJYs9e9|K6 zL<+|sG>Vqf*;<6D+z4$#U`-VrBoqIgN92U24X44M>OY-8NO3QcCH^uIoim9ooE|1H zVm)12^pER%8oj8nxyLjv;4mH3yg96|qq-0IL_-1jhi>-oC5=ghz?Fx+HQsOn=KDO9 zYZDO zpCt{pDc+AFos4BY*f-`T@8E?^tkv_8ufDC0htM+C#G_G&@5w*zMY6Cb>zeisj!Jn^ zj^r1Fq`7@idizG*1Z!TE@sJl3oNGbE9*G1>d@Gg7;u#??uW{`zqzDjDzEuEMoi#p4sddD1ml2g4yL zuPv9_jWu%rPalw>{vqjU@0I;e9FXkXuoRUR$p3!*4mXyy>BvcGZD^CWrVcr_@0i}J zO!@6yyJSKI#hOjElAE6+J>7ls)B`GBGc%>AqEzxr3T1+uXPmnN6$cW5KraLDamI0m zaLj3;oBCYSD#hS%3U5dYIw2+TP9F~|!j!476B)ryeV_@A7*K=FUtTzUIFmS$gj^tc zQw=^8jub~u#z9>vq>JdDZ#ppQ+;@J>YTZ6sZGHqQbK*wg|lUEHZY}3kxPJmpsEkSf^rdUH<%M zdj{?e&WU%AyjdMNK2Boq7?WMhe19ljQ%|oZ&6RV;n$~F<(qiizAM78!wTN=cGUuEr z&!)n;u!p3L1{6}ZuXoHfpdkEd>?2<&V}9dJB1j9*3^P*FBY=SR_r{T%jrO5odC4^! zWN;)Zcm4L5gtS^b(K_JPJ3Vx=M@sTT(%v)V8f4DjSmwt07Zqm9;Mm+|h&GIK5gL%_ zU7KrEh!3;*<`?HmUQupr%iO^}tC@B-Ct0w02r+y#X2o4@sId{H4Bx;eV(02rT)Oql z^*hpuJ!Vb|>U^>rn>E`tWAEo;w}X1%O~DDt>F9L!mER{}X43*EP*n(SyJU;3+fpY7 zo;)ZG2O6dMc&juVIw8CMZg;%gGvIbHx{+q`FMKf1Gg2jSo(HywKzFC}XX$ z8GYvpm5<-r%3s7W+h&5VDc?LBQ+MUF|KfOvf@f8RU8vC1GKP3?3xKQO@I#E33i2(P zH9!-h*Fm07_iK9eCi36 zTyhN?f-`q!4$cpb!9C`$*9fMIp+-zbWJHj~A zmcb!;_Rd-<$PLM%#%^a*Mpb}5XU7`(olDot@zy>mFV1nce0Ve}h2fwJD>MuvG1LwC zHPtHacnW#m=V^@{>$QHFYh;-ZcC93PZVhPI25cu0Ow1;`T##hHF2-I`-|kTR#yluY z{=|!3Q0y@o>|Z0FX+%yn9G=F8O082a(sa<#vXJ|_98|8Wk}Vh2OXb>f>1ywmKp-Gn zFFsFR_wLuqAAjf%<#m7ddu{@y))TF|Z@Kp)z3_>Ns62G{E-5G}l$XEdrBYg5=6$D7 zXRyQ=JC2SxpJe2fcxmhdq!DOEi z7t2e`zb88QE%uY%RC-7ez|PrXqk^BuWGD#Fso+QP(9^?#v&Fd(vhhM}I!Md83@xlU z&r6vPgL*TmFx~8cbAYihERk`5o}#{p>n&ooULgHA+^z@TZ8;Mhc!b_c0t16kKr|FA zMr}C*RMdpJCIh>%zb~+!4==QMgdZ56V8m_sWW}TvHWx zJ+rgHeW=T0mQLmd@G_KR;aJw|y{Qa)V^*i5I42|p838E?grvKFRQd{}X(%`J5U1PJ`EMje84)baM{;KpHQ3VOtB;-=vW)G&~|z5Wq_4A2%4;j#_2s$QelsOICrA1^dgRG_pL8?-`ZN7DA8(dt z9@-;!|MxHD*)O?9Z_cC&)MAN_j7WY_uH@_Q$HybG^XeV)n;$_*y&EN_-{%(@Z%doEwpcJ)+zqQ0Xz|IH`h(!Vtgt z`Y>=W=8|XZ>+Zu8T<^bb?ileV97{x4?R0Pdm>f{y+0i>JTh^CISxJs$>2I4mhIHGA z6c=Ypo(kEvt|2MP&z7|nd6Mb!!bGZJ%mfiM-m@2r7K_;sA|&Q&7O@eHgJ;ju3mgi2 z%XoOcWgntOn30^ww^9{v%z%%rh|_mT!|dhM+lvteL~^jDq&1{=40}tMF7+i$ z#_A${FwbscF*Tb5DH8fGn1zwNaI_$7Ym#| z#3X_^8L&0}m{+cq-%Y(NSN1etSfg;d&{#?(je4ARrrwNnfqY_eDsO$-v*A|dhJvm^ zk?S9cj_WHb;Xpugf~=i5srT2L*eaJb8TAcglln);PBms2hA}4c-H)fS_sfDF^E<`v(_s@#y9qJtZ;k*MqLo(Pi=svrpYxEQ&1EZ2tn6s#PY|G5d zz>W>&@(0h~E`R)@3lqL>xNL)Dhf?E=CxY^U!J}aedr2q-YacQ;Y`RAsL&D@JOnk&| z7>Yz|J*`-@gkdOR_RxJFe;ghR_MyceM-V43!80*uQZZIF)sQBxcOLb@yy0BF(3~qy zm~yukA-&`UJ)}6xM+Q#M=IZrKv3^*V6H&Ff0XHTOf#A0`Vm=bi};!5-utFDh{wh4@`ESI_>87sqK~+zyRUTlBT9IOuvt zkZfg-J>c2rel3MKB`>&Fa}h{5i7Z*fB~AA$dML0>GhGAUaILe6_H=-J zQY}3r+`InKh;*p)*Eu|@SyDStbLMx;ZxAzFtb<8GhUff2W#iBrQ!4xU4XJawrp z#=cU&Ar$8dHjE(?iO^HHhDaI;L%Iv~DRn1)7N&~#yyhFG%kk$j;a;+6@>~ZA_Jl@1 zmZEcw#j)?>$gquJ_XH6Lk~Xmm^_ z#v^)fC-jA&j7T&xcGhcha@+|^x4-_K7}xi4-%sfFsymm9D?Q|8Cv@8{o-RHvGC93N>{L#OWv8|@vPaN>ua zH$p=Z22`G`E5@V}PDmAoT%$*#D`5XA9f0$O5Jy1FapgDw3+Wl-S>kzU7(9D<8Xxkz zSDlpv4dG|Ny&x%+BVLFi(nj7q^`;umMK~MI%v?Zx;`~h4D8_a8vo=#Ur-RE}31ebNA`Kdg;aD1m zkvv2W(SwQ@BZ6bTp{Ua}p0Sl)$BOXGjd1g&X(;NJQX^cLnRD0Y2i&{^L_RG9Y%;@o zNIU7CsbM!VqM|1haLc76VxCz#F9_|Wre!SZN+YOwI37uzrl7YoD=;Yq#kpGNL_Oa` zUx?J&^_6b>#5h8C`g=={?096{Evtm+=T%rtFA9LjLdZW3fFK4~h-r$9mL3u)?qmM{RXXNcc%D4hPOt`SZw z`=^>Ev`tB~TxsIGh!{^a%%0cttt^v;nO?US2UQq&VUd&@{<=vG^!xT9Z&qmPUOpof z=ppM+q2~$5Ghygks*+6FNC$$4%H3~uo(U#;Vqzzpj;SCBIR@cYRJe&Czj*`_T^q19 z{=RV-UL22?_ud=dL2oL5x!?Ya?|~je^1WdoBKNh7$ex}t*TT;CevfB7@yX%%Tp#yg zHoTU3PYiJg(lq8~^IIH9zZ2^Ig(eAU^g@DZJUct@IUM;5QztSZyp6nrh+bzB*^(B} z#6wv#3}gA=5yb9Ow=D$xtVAGOnIXMd>!saYk%V>ALO@j0cwDdNAPR?woSmiF^6aYc zS!@c{W^29SMe=YBiQ3eL`-fflC=>lGH}vjN(=sO3@I)#-q4dW3&|Awk zdR=F0(8ZrCsV8J|C%9NGbGq~J40CNPLp57Ib9!3RaVi+Y-sHr4GFPzl zpilALd@-E}wj<1adqSiL%ObI?3R@_D-RdaLWN*>|u9M#U6{4jLW7R@~AU%%wR74Fi ztYog%1AEgp&2lgJR8Mt{xuG-EOQR{aP1Mzwlx3e)XHiEpb2|;Z^MQE_d_S{1%o2tZ zYMWWX!EHK&$Y|7!U7W55btz$u&%EMX>1ytF<1JSTtSdS)IC^RfXgX0^TP!^--LX#w zEPqK)BXGQ$2E8#zgmNSkzJY0B-AaDT?5j*fGN~JV_Y~X1dyc6-uj~DCofu01b_1u` zg}KFggh?l1kcq(AM=Y`5YJp4PdZu0ypM4RsR8S7+`64otMhz+^CgLIAU2l6_VyN&4 zL-kvn`PX~rrJb_29HEOOhaOi%AJgA223#jSn5ls0k9GJM_v9avvE#eD$K==mVM=G> zo%?iN_vy1ukMV49o_3ZY>5sSxI(UxK5mH5&aa}CHa4zc$m`yU^ zo*CD>tXbkmi=-o@8wgovJ^{lpmIvG@CXl4Qqmc`tIhP?gK5Zk@uM5w1UcRNq9xoNt z=?KmFK%QVv3G*dtC5&I7$4gVQdIot#xvoM%@TEKSVA2EK)!ZX($J(WGLq+VH^no7J ztgI|?6EPVM6R2@u6d25ZTpd^b;@Dt(W`f_~j3R7V&VZgJLf|l#h(THk5gxp!=ZT(7 z1PQ-eZ2YuQA^7LyYNqRPVl$Hnq`LKSIpIp3Fo|6f4z?rqCvi?J9-NjCa$en9DbZMg z7){g`vN1m(m0Gcb3nPuYZ!|ja`{jh>EO!2JyA~?;7~o|)>Y^cL zg0E@N;vVIx_?sv}zVG#{Zt<%iJJr#T~_FqcoV_h&w{@7FO6USh7er)S}jYYf{&&v_x&pn`Ad25|z%88+I*P86g*_7lUc32Ay2OEJ9|24vva3IRp zNTB7$J5zVBJ&~c*EiPQGu z9n$bjlPd$v>RVn{>RvtVeeV0!%#sS!O*<}^q1L0ZPxFJ{Z@l2L*r&w<=l|G1R1Wox zx$y`nT=w>i$#2_6O#ji6a?HoX&M}N6HaU_2>CdPlNR`s>T$FUM^%_+%HDv z$cZuknM-7Du$MqR$hy;ex|8b_;#jQXeL+c3ih>yuq-Ajl!G@CFXS6Pfk8YX`CXCuv z6m$)?XL34s@M~S@tp1%)(cat9@5D)dab9eXRDlo5d5QEpeGT3=d{^BTw>=S!dz++c zW93J%O}iux?Ml0bR6s{1OI9FM<~w6wA`bRO zTrVh7%+vBqj}K+V&tvL6zqh?FGT~Z^=}_FKzdJb;bzwp{0do;h=?rz*e9ISkgwf{j zOoH9z<=BMl-DF)oSAWK!eA56y0}}V*!jkzfA+VGnx)}=eix9_W7iNjY)+V0)w6Nf2W&MDkP@Dxe%w=lE*qno$XB)(!5^N%<^)i(ydFSchhMg#3>U(g>piAEtwL|%|1im&IfX{^8{l^C#AHuRP$?oC3Z~3DPvI! z%L?4GKl34-1!`7NQPIu3S%^S=;T!)b_U>$$(Y7Evu%P2l2L-HsaMTH?bOW1(F(NdO zaR!V_V_PbF-JzO%^PQybgdn;+cpi)Mps{WN;eUBLCGhtGM zhf=%|ru51a=F78$u%e|A;Bfv2VM z&5Q@`^> ztKSf*`|G}lVYUTA+4A_kk4sKoj^q~P>9=0PY#N5{wIq~1(=I=P{#fa7pHj?3}W5v~*mm!%>xhzJf0=X}8s zrzZF22SS?&ng!VP6%Cagy_Bh*owZ0sC+nT&>0f_*GK-B9(#yDIW`?HMYwBqcRc?LH z>?}9&&|E@{(iyHl{=Z~xhCi`DvUi952xJbQ_ca=UE?R8BAZiWo|g^MPgwT8-Z43$&kxTGYqv6SN^Kg?tZW#o5$YsD zV2M0pWd!Ug5m_{tAtn#>MqDINi$1gTLeACvus?nkV^(<`>5Igc`x|%bQS;Y_>H)#r zqG?%(6~GUp(Tj#;8i|vIx}D)TwOVJlX&T4}AH)w=|LCtDlTda@vU9`o)Ngl7lgc zu6H{44ZV|<>&m5KZJCPk92d?!GB7G94>q5A(UaVEoa2r=5qo@3qYJ@J!~~N{X@wP7 z;H=DJdT%*jei|;DlL{8S2Nh_kA5cNrGcqRKnxPAQaU719jyf0X2*zs#FoI zv-R#w2L_xzvgLZ#NwyOY%XI$L3i(Ovuba_NRRiO7y0ik5$mD^MZiz3vR z3K|hrt^2OD-udYuPy84E%yw07Mqu4(u07lAuUYL{@nibdH3l#S74c6iH~G90LVUUR zG;+ij0j`T{DAzK@wep$EoH{EqP8YoyE3N$C=(0@HF?B|{k0W{wtXYdAT9+S??ZqMY zTzR4M;+`w6)i8{u0UwQ?QT$6*H)yBlagg!-=!1M=*bJzLgps&&ha{Pxa=R*el+UDE&_PIF#eQY-J*p|6-PWPam%MkuSOCiwX z#aln*Y_4EXImUAOS1ZhaT`^E;Q{geAFCu}5a!AV#4I3FzB8PyQ3c}QdkZC^5Rh;^is?)$^9{iei0780kV(4u{+|Zl4RPcP`M9~8nA9kD! zJeyP7F*t+^Nhg2wMNcYaN{iN6uy4AHH0h)c$kE(F%3 zMW~*Bg{7pjNH$%x!OgnMk}yRT1#)6 zp!cpglqFB7!;Jt<1Z9bo)4PfY;ivH-O|!voxpZ1`IX7c!5($??@LPU2f}=s1^foUl zh;drSITw@vAWo_L;>YsL2SR)=Qzzru)j3Y6`eQOdAK9})fENQExdF}m-Q8otI?7mz zLDb{|DN~_M!v_sz%wEf-vBVG86%X6lwiNeqzK}M?sy(XD#ICksX;R@y`SsJxU+U?a z953pC_fC{+nP#jQJt*mXeTHGA1{xSyzkXYBNH*wS=5k;%I3hKtSE8M$ ze-kw(W{i(JpKnKLNOr1_-G0$Jx$xSpvauphjs2k1))dM1YtC~M9}(usL_Aorp@AXy z`#u$%^zcFJ@iy7}=wA8nfBC))^bR;d8DHxnX#JruZsymRXsKLVAse=Bke`0@=W_S= z?~$?5F&P;ek=*jq>CFxI=#Q^*fw6 z9#cWgE{HNhU&DDhZcOE9B%%U(SW3!^-K0(>6~*qD=|=h5GHGjMf}%x0%Ll!zzGy5c z$(JpcZ<38W*Gtug3MW_@>$>&YZBldoI+NiXr@KNeW(M;uOThCohp0~KeSda z6+8nxLxf8qK2rH8Ktb0 zb-Oqi?^wIJ+V!p_qNk%*j_y4skKXf`Y`(DG+2qP~)w1dQO>+JJyIyX1?F*!C(^@Bh zva&LzwXs!N8d_wi-xIdkIoUGUH{`;12_dasS0yid^NZ!s(}$#`sntzp)Zf!D?af`% z-#e`7);!T;O{y?W1mXvQT3A{n&wBASQdCmtwqt_FMk6vZI404th)d_V{w*lXldhI7 zX=`d%G28CON2VKPwI$No)Dio%^pJOSJPBz@EM5;DeyUM0rh zaNnqylYuhEJ@+QQVj?e3=+2e}$^*w+EE}^B zT81@`ddFOOpAN2ziI9j!@biQ`rMxx`dC>v-zhM}w6zZcbg+bS-J6)YQ6WGAneuj-C z(uoE?h819w@ad{@v)qJiycqvJ-r6l?l|_=Nws$t@rOXNhrQ>9~jE#)CF`wDtkZitq ziv;xGMP-G$ty{WUyQHtHU&?FCB^U~N6A|e)!fo@53S`a38p$chbG@9{L&lMwIC4_j znmZ+5w{N}pJju<=bG@`3x(}hj<+YV6So@@^uF{QJ9qb*D@|rStysqYU*?z@#6`xc}uK5O;*O0GbL`v=|e5vpD7Jt~q1+_jb0luPB>%Cwoq*JrqY zL;@@wqpu~$l_B*%HLnY}fMdai+kib-<473ypfQpfCd zOruV(DLpl`@;h;$SzZvza{IGX2tBLLu<(yU3*Ayo(1KwmZ3t#s0Ar_j0+olz|2$Xc zJ6)lYaUqXd8^woorW1sGKHWX$T7(%FnW!g~kY4s-DJMkc^J;Qp-z*N42b_FTvPz1= zg28yYWnAl!MOI0VmS*T{skC9V-;hTqWZ%-B5BT1Tg4(O$ztdrM?;0QzyY%UCG9-h8NoPZO95jTk#_l2H+o_)hG zRu|X+hQ{nH&35nS3Xx+Buke3My-VXEb{(5oq;>_{K@;ykxp$#k>Xy57)0$Sv1Gk7Yo`CymogTolL-NbSaIH)HQ`|BxHA z=#8T!tT)s3mLfz6&E+?$b!AkqV?xDePC>3)%4AH1Y+hcD9yimCaUIvRj}DDWmY#=j z-;rSzYk@2`Sr6k@2{}eE5B2p+j*8X^&4=NEVHL#Qd@9Uf%Rcx6A-9BNrVAa;)^v@H zj;e?ra_7k{%y-`hG=I`fppw^ZjUBRX+nU&xrGs+BY|@k;qU2Hy%L27Gc1m`BwiK1k zK6z3)uY)?h$70M2T9Eh;!MVnbCCrqHoJ<#T!k92-aO+mZ>YOGuC^%_D<18oQ4UR`I zEwvBDIf-m!yj{Ev#Zl!Wt%N*OD;3g?F`%-+;Uz4Uo9?g6@!!TATIa!&qE-!eNIOUP|q9~$nVl{?RV0(c24#D|Y$EY0bkDQtn zpQUe}>>8E*JrP&;9?%!_K|H0~=xCrpl4JPh1V4gx$m1!lERu?KK}&n8dMufX4ZsWi~1=l`o_lzBUZs#m@OgoB_}2#D)=HQL?V(~5R|Me zO-Y7|(upy5oIqew#Uf)9M^zwZNP+$?BXit+$FX=HE?~JBts};U-R~zS)lNo6+<7^E zWNb)+>J&!DM%?zEwl4QRto4*?X}4dNo@abq&#C7Lgw&VS<5PbW6o;jtBv(c7keobt zLJmH9$h*)qg0Y*?nBZP&D5DgY+3Mu^LPH2+gNJ7*b1MZCs3``ch2iUQlaqMDju1@# zZY>VU1tlRV4Q08WJDfuH$G8#-Om9NWb~>kXDTM&eb)xV-w$XFCkRJBs20MT;z>!7x z`FYRwbcLj?*UEr9(y5L%(vWNLm=F94F~E9t2qynDXkrAtFHVib zP?qK+PH8ZKdYlFKoVkxlfskdbR>F1@HMEc%95Z@aDeuV``ng}h{2|Ts*z!sQano@9Av_%J}Wxaoo1pvHbU*x5@8xpSbT&zow?a z`)zwurwY-i6qMzuDD@gKPw#vBHyoeum>rj1(}I#5so7j9qiH!FlKNtK(<|0MfX1uG$bwZzi+LW-fq-OP=tRPvvd(P1IF8BKJ(heRFMRmPk%6eQrG{awUU=-8t(Opg z9(U{tb(U|h2*vFi^FFhQ7eW)Cik?s!W`XTQwav$7Fwx*l!=D%vn3JC^JuN*d43j_p zxTbBnItJ)n92y*zqfZ`}=txAW)>pVN;<-TDnM5g+8*;~^7x}Or@8Baxq~&Ovj17-D z|92^2(x%90#H~G>ofnF2U3#eY7c+v7by3$}uvXfRw(C7?a3Rf1n&sy=vo7hQP!&S zT8wf0887YiqQ`bl66#_aYFvn=ex@Q%b=h4gq>-`S(+R!BgkDh1V%JK@^Pem@Rq=u> z5lqU)dV@q}&=97V=&p6Pup$~J2npt%aSX<7BH)>|nT89TKgtE$C?nV>KJy#ODd(W~ zlQFMaj9fT9J;l1&;R{ms8=s8LK&>A*F5|+nHT(M_?wH9yzMWTerlZ06pXu>tMdobD z=d58ED*?y+2x5G!Pd2pOh~61mE^3++`+aKGV8tyuLig#T2Q$+w8o|bTkPn5bpb8 zXF2zp1`8@%THE}xN@YJ+6TxH>gjOMt5P<9Q0@IeUzLI>MgOYOGn%B4rkme*OkY%EF30Z>Td0k!qR*dsorFc zqbe9%kG9FW^Vho1iwSw!p&~7;=O%1AU52dD=t~*$>(Ir727<0wFFp+>j8mmk$|gxV zhoK|j=s;BVx-k&rE@U^};hPGfnmB3Q>QEBmM#1%+O6)AhOEo;J(><@G6-^OeP)Guf z;3C@nWr5jFhsPOD5!7Am%gx=5j*mK7G&=co{ zSxva!)O0K}h^nQ?+e9FY=ORvl-^>)0%+U~M72$xJfQ>zA*qTY4B1&2A=9tNMA>P>{DuB=3l~u zeQ^reIUy$;5#s)fu+vmSev^;%9_FhkOBZqK(4Gh`(c`o(eT%kvmMu(~m6;e=| z=fr8cljLJ%%oiLqLe6l)j;SH#FE6$yDvFdD#-1Wl8H>tdzJ8%aEF>DUwNNhyFW-saQkNts1-6olw8^PYj}%KS=$>tR+% z|C)F_-Hf@h?~{RsDk>#@J6Grljc;*90AerViRfoUnQt$q&N3MfjZFy4X1%6lD@YnL zwigHGlCo@Bqau-6%V!hXoQD?Ud0cWrYI42F=Hfw!6Ovx9jw7=ppC6l;jv0Oz!E_pi zu>`nijM#hZ2#sM3&Dhg3=4{GRf!(F?kEpmy)%Fp=;YS%I1L6n)h*+XG6@dmv9zX7U zEf^b7ahjLy!fO$2OAX7-Owt0d1k`S>mg34n_agMU<3xx09;v;bBZDI{*gGsml|}B_ z(h15=b{0eYyw5}}8K*IUx`g1cAMA~|F@E?}W78M`LLx6M&vuR+p({)p(xNX~3=s@7 zEhoY^^aLi$pc`j~GaK6w2Z!NQQ(K(aPSRJ0+&B&O3>DC7^<$~n6A2LRu`jfIxaCY@ zgRq%x++reF$eb`vLaaDGho{u>5H2u1EZ$!um32! zh_u-I#fBjY7nO$O%8IaTDGYiIh4U~StT|e-woK}_)kd3&T3;j(MOa{bQOL|V}u}w#)ni(EL`66=7 zI86dsVk{=~wDga8y(cUrkAY1j8kjhVF%*0?qQB`?Cm3g#kQV%43^u==`csUX`eN=p z$$)5}6(>>e2gl|p@xCpe*-j5la%V7>6P$L!YY+8B-1=@u)LB16$mk1!ML5*da<~vv zS>q-K%XA_Pp-b6aEz+Ome7GjAh8LAft%@bqtz$;++2B#5x1M!R=L>g=o&$PM38m(R z5cQoKL@eTQqdHPd&{m*=Em6Z3n`rn-(p;PZ`*K*z)O{@@a!~)0AnXXCwJdpbr4y=W z_VlKbqL5dn;z4Mx%MZE`b)cO0#)P|H`goBf@`iD-(&ZgMU&A5UeVchDW9sG=~oC4C92PeG$9^%adV{#EmL0a>u3 zF@d_oP|g@5wYzK7ea4r(bHCf+N7#YC9F8O3shq<#}aWJmfhxhTe14 zYG*;T1-4|qAXHi8{;BTB8TNo4%$=p#vO^ueR8Q`O5T?vd4F#ik(NpXfGxCMu5_S0j zH^~^|QAroSrT3#;l{@2p2yLEAn4V!6D-)h_o^}2r0(g~aJ;hjd>;<+XktrxuTjBj` z@t}vVw!YfE9~l~T%d#NU79!B*OI~rF4D}4jP@k8Er2u=*OtazqY-u{s>?Uh!+;>uX zb(`mRdi$`pCoex;^g1*1@PG0D0 zis~Zgq<4>bJ!XWc`nQOCJzH6s3zR9w{ZY<~Ls_oJ3SqEnd#W@)DevBrP>X+!JYVE3 zJ)`6^jSKWBs^0QKfa8)tdfek0dru23_Ji>*2wHm2`EDUF_LF;yy&^6C=@F;oiEeUf zaCTz>a9l!&w-g0k8H>+r-o{@yun)c6dBH5#SmJAO98S`Ojb#iijVXLZ-YhgP48urI zXrv|L#h)_(b5W?afo<3cYygoh*wV1;;q*EXsK3}x-!3XlWRqW-??l&d|A=JiIhjm} zP}u2a{aq|bN4NS7(MVL&6_hND`y#>h_O$lOpbFyhx-z#`X<>PR>-p^O8g!KhBDJ@@ zPlo%3Wpp^=LV*#abQ?C>#UX(rjZ6^FM7WtG^esh&X^wuA3G*l>ndgM{#IFc zUagemX1U(e5-o3~TIntYS`-|VQ7aLtgXe6j*+u{cb>r0S+yf?A>lq#Q!nt(=Vod*c zy{l8fE>TgcNs@5PG2wCMH(jcNV><5)s8kVso;~x%)cEP(6pRBs&>L}+OU)PT5CYXL z-K7cQ_@tgg>ZzxN^CQR^8bLZ4>%%qjTi;A!SD9g$8H^3XSiN9JuvJ78(QtZxNytT; z_@)p$>JOu06On97tPdTCvd*SXC%!`AkmMHSN^W7!lFh&xjf_iIbGKya#j~d7Vofr{ z@+Sz*3Kgf!_!|$srQMo_jI1oz%ghY4=oQ55M0CQ9Uk&GFyL}O@%-B0NG%5wnbEC-}`Y3W5kT51>y1A2M^0c{TkQmSR9Q?PIa-Xkg1%P zN}w9c50zc($b<_W>KjWezz}To6tKoB?|FPNKmH^_{84|v{k~~v+|4o`^XJaM7q?;k zOAl{}zSzcCXV!4#?|k97*gZn>d;yHeW->0qcxQs~y7VqjXO_sLUG1Z;G0-rK)e9zo z26XC38mula&vrdj>Bz*ygqu8wF`0gEAz{Dx>`Z3F?6gxwT`EDOwl{XD2n@T~c4yKG zgeHe`=OzXjBih;ABY7&mnan5={=`YiOmswk(UZy;+IAK9#bt%EVduKow<`4H>X0@aumvcDobODfg*VyqZF7YxXsD)MO2d!lR96;|9RdP}lf z^&uP^b;n2-qT>@1SdJz~;}pXa8PeE2EajJMlCJ(y33YVKg*)q9V+cjXw{i5u(M~*< z&{Q;r+dE_SrH?Q$^xSo-h@ed42qb@|rGdsDR$wCSdlKDlsuNu2(YP z!rG=2a6bH=wQZRkiSe6Lj~5Tw$>FG9h`2~}{$!yM?!4lVD?^l7#3sil45~uKDI$0# zIPY|O(5w55<`F`7zUeazW932pbxB#s)r)D38YaY-KPDA>+TGeC1KmTi_PiPiE}}?f zyyvmqjZ$4-Ex8GLBU_KQOLtq3WQRj8T{XHN;(0a@r!1utF3gd#nv#_E4Da7{NY-wx zb-mBbmYgnRW(FiPE1-F*_3=bh>&7g}$_l!AXJVXLoX>7RrV*9~RI-OsI;0z@@2CSI zBO_A+fskYdymi5&kue#MCN~?h2~;C#D1f-{gq$!7W+LeJ$DwK+8F#HKIQ$ovWvA67 zG}eQ<{177EGdAgZN)NUVN%UlgY`I{Ko95n!7UZ^(2{(>$E}AUcLXQ_h0|Uf1Y|wHP8N}%$on!9#xu7KI(wr!CeW7rj+!KyJo0#%p zUr2KIv*7+d+BxckS3H<|dPZpw+guo&lkFf$EFr{9_C~rWPkcR>?4oCtrD%vsIaep6FpSlN|NLWTS)&Q{)~@%!jfF0w8qzEuJlsLDOzPo*VwqL|4Q?6q^+S%N~+7G`AC!0 zZC&TYE)5z$o#_3{>v{Ly@y}#h3sjpVDa~+&M(+hSMyLud-)spw5*ol1JM zm^`U0JN3&tc#B7$J*I6YykFo{r#3Cd+~ApJ;s+>ov-E1^(9-MF4#u0w1je;u;WkC$ z%;0Q2qCmOK*NUGWx0#9@Ve0$)#?(PT0DEJ87{`ccB}|mwug1Zsn-HK!#Rg8oQWqOq zCo$ucb;JtKQjVrKRU(z=ftmPNR*LH-4{(~2c|K@_VjX1m4XXb%muHIWdbDHAU5^j< ziu+KLnQdTW*@E0%rOn=*n8BFvZbEGFCs%usz{$c@zpgf}h9Ov2>8*KOo0V|CEl z$=X48@AXCRIibCjSHv#jon!Jc3?u#FxyQEAkcC~~Ri$=rM@hC+hE*8o_kCmD6U}U? zJly`$9)+RwjO>{SL?L50OKVFcK;^KAV1HYVv`O9hYuv251KoqJp?jcf&`oeuwZ8n+ zBu-59R9IQ$#*TKJ?3Dh_0rwl0&mmNIY-CKr1v$>})@-SkqVfXwyJSHWxOnKrrPf1T_uhec_A}iYBTH{Wks4XQ%Rd!xZk#vD#$1XAzf(UkX(!QGe7J{9B*3hIuA&$mbq0P+D-0@) z&i+y9?j4q~(QyfDWjvQ@8{^?2eCZO;{hX0odeCU-! zfcT<%so*;3LG~xm$-^TtYNxEZLqev(Z;C;8`J zgTKbBFIba>UAgD{n(Oh$xKak`ZSnKYFpTsB>p-LZ`uw15P}{aXKi~u@jd1u!_)FL; zYy-omm{p#AXxzsh(y&jv7veT~%d!0Qt4JIl8<*m$#b4udSVhzrGwYUmGwWt$WjS$7 zxN+H<5(#FX8D30&=N0Bii3-(XUR6aB$_cr#tc+u2JChsDHKZ5cl5x1*dTz$Mdj89( z)_0Q3!S{E|Gk-t1yQ*fVJesvw^WBC*uDm-Tw5Ve-1~&A-Mm)TpSNC) z?l~+2eS^}~+AV!u{Zd|CAy3}-q%%L4n6_WqE?f+!!rCT-48Paf|L5?42l!k*1QoX)L zzWkN1#8Q@iq*wETaR!La6Y6B{?~R-qLq-n)&OT9f^o!uY_PS8Z)QVHUIpZ92ea6{; z@yvp;5SRh5YD0xoZ>n@JLSRaYbER6He_nw|MB0w2A#CV$lQPVQ9<3ZGvy@%dnRGM1 zs*pjPO@)fZ--^&-mg?n#uwxt+92*2mD#|bwDh)|oy6T=&VPuGQ1kl+zawawUqxu4M zxb$!Q_*k#E*IOor{GFP$OiO=!z*{#NBhRE*=NE_E@+?e*w2*q6b0L`9^j;;pr>Vsk zimcb_iD4RByd}6!UxaH|Q2WQ0xg?4EgFWSUZqhk*a{YV68i|Hsq&Emq#3$<=`?GT+ zIBC?!F8J`CarX6vK{wuXlm5l7V(W+T z%inZrzH_ej#!l($=y&hw(M^_#iHKCJVP@eR+4smkIdP;(c3iVV#bZ=DPIkys5AK$e zN1LRyvP3JpNohQCLWTy0WXnaHrKPb|S{mB^fA;3wE-)78~x z+RqGT;0K^UfS?6Hh~$E1SGzhZQaW5|b&hxhoz;=F61Aibcd_7NmjnS4AP5h_%nSyD zX<(+kt?KesS(V;Ai6WyFhT@4N4Q-~WD2kJ4x+06O|Q zBrzYC@jGKG%hu%fXZIwZEh<6HDhVBx`RRFCo?DX7KDchqjYXMBhK}Ag8UAcomS>mE z`g(;WsC5j+R%Z9es!6@ndn*O`bRj37E#^(^7eru575{>EOq8{~j-P7Kmt(U*8*;%p zA=2wXeN>D32g*LF>q#Qe-q$MKD)8FL3K;Rr_^nBqT}MNp*=FpK+VUdX4gSA?BCoTKz^DW1roCH=TF>%2M7~#P^$@(vKk9 zkaMz}W7)2D2kvdr1|!Kr?Uqm7k2dCcV7=#lZpQO6o!$(F@7Wd&$Sw4 z^hHczR^3xnR|Z{}csT9k6X_>sk2c%yIDxU21T+&$2*^^qZj7i45L@@@VqR`67ewXw zreH7($3kX?vhQ54nK8Zh>8RZKU|8;ccwaIp5;Am~T4a#aTkqd9Gl;H>B^EUZ#mZ7n z=C6%Ner`dUbbGIo;OwYwm&};$HUe(G)%w#>1@8e$5pJrKgF-pNIvdu{tgq!gVIN3T z<+l3Oy!#vjivh%q1p_peQ!LTEp3f^W#j=2fp;quLd7Q+{t}nTQ7YE)PkYB8s2Lasl zNCHP}8TY*zAm_BRyBYU^g>)mZ9qmheFcT|d+V@EQMB8mdhAk{C>Ib~t$el@jcaa_Y z@6~bmNG_*LS(g0Q+H}F~xa(xQ+d+6QoSl6p-4dH#%E-4c1d3P(hJhw-73YxlR8s-cbmNS1Fy;#|Kb-Vs?QJ1 zjLn!tOfP)ldE=T~%d-oeSRgI9BgWNvw7@|j+Ea&?uSg%dih)S`*dmb;Y1VT zEbj{Uo!g;OV_{)&aKJMJPm`0z&3EVW_o#pn9LU&q;~%#REVbmSz+$);NG>I1;?9%= zlw|os0ew!7KPW7qx8AvHTvQ=|J!iU%&Jk7eNt`P#s+(~Yqa&0TwujV_Rd{w}&R9R^ z#vQlZ1|8NR!A_#3H8TfF$tGwgLc(-GRcf_>BVyDo{RmlsPcPr zN3#rnI%4($*qrX8Q0#A-uzfv+hTJ0_2-nNx5)c{@_}4?|E474NjY)xq;wDV$n8&V%bAxh zN~2!;(%hn)()FRHuv~lZhP?8NH)l*Ekr8@4q)0#PiHss45oKY>SFsz8;DWeK zU(hd!#e}iUVtpi062uhbmH5rxpOtg3o-yBd3XTW)Tv<%Y!sMcyxHMo0FDT>-N>Y=i z(QvE9+R1NR3zkS?Tvy9F2eGzH&R1pwhQLnToi=q_aWUQq{o8{a~Cd|zx4=9Q0p8Fu2>Nup4A2%^e2=^N+aOj zf=l0CfKGw?zH)>;0oOUJ*9ixNbnm>k?HsPM%6o}Y3aKJytr-PaF9=t{-GF;!l3+pa z6Ox9t`?!L(M2V-=2(HbELdjUxfTIvJ7=pba^rynoiWHMMC8&P!6H`*24W^&tUJ3tk z2?AvG3*76G8JeZpWeKR9r}Rv7d(;rW_*_C#OG!ii+z}S59_PYCYo{PLMoQa* z_*uwr$6$vNy^V#TiE|xaFji3r9oAB=V>2X|)3Q9XECUz%%4?nk1et`Bl;&BF#zu5Q z2qbO&NWMTaAAN67f*X~$zxtL8oa&d})4ftEQrpzo2a7B2x(GaosSD&`UhjhtK!|!J zw_`hMvC?NmKlA&u&%a#$wfJb2mRW7w+8GZ64LwRB$Zfru8$-N?|`vDYv2Fu-A z1-A?IOO#qHeJG|hBiF)rlGk8eAsg~;391W-8)N~+8jGcf!Ts~RvB0C?alh}(Wvg1oYXMiq z>tB8)HU(ZICzLFbzzA1cu7e36crk4h)&{4aa6Gof>dtZ5P9K^1+o{iu_nOmDdl%&u zvWk_3{zX4#J(GZB33gq}?;^={Kg0eE?0ALIY>WZ+>A+Z~w(W9i! zSh$?;xO$RdwUPuJGzv=cDD#4)FBwmpkebecHZ!jAPa$74mOe^~#OGHerDQ9SDH`$@ zRX&2|(K*z?){yP%967T^AJ zz2Xn49vcgr^8n6?wQ@58xn0bTEy!AF)wIV}M6(iLlZH&=+FK0}W#VWD zE^bU>w}RIfFwcrIGL4PI(LRzz$U40B&}qp*Lq9+sGI5L^LAL5zaQTH-%b)5GmY^O1 z;HrWp!KnxM2`UfD66>n?FGz)fgT(s zUOyUZAb%z;1zLRk3exklJ9a>meYz)Q;CY=~d_{*4Zxy@gdq&j-A&m@z{m9x%w^><37 zIV{CO)_gwLHfo9lLA2=$3hli+7Rk=A=lET>GO zq}|HBtpKSX{^Y@9uGW=+pi&8eiGiKR@G^D?wV<8}s!ft~1VFcBWQ=x6RF zs)R?I#$T*c&FfQQtBc3u6R*Gg*^5!Y`OQ~e=Wc^LrbmzI4}>1B zH`{uBx^2LG$5pe^GFA=wOu-1BuAxq2u_HkbV>k=$uviK!b$EzsP}k=Z8Hp(wWuh{q z@|7|z!KT1NYb#4I9B`5r>9LIExD1ZRd2q*dN$XC5Eb1I+f9x9f1C)c|9^Cq<`w7|v z`I=ZrRy|*%_OnnlWMnS)U2DDA^+xtK4R*c^z|cHqv|L4;}rJrP&RGfE0@k0r(r!cYqeiwTqQ zH!#>Ko^sL$6pf25K9gZ}uTg11hvb0mG!gq#s)UK8idS@BK}$YJ6y@$pUiwtwdM4&K zLW!c~@r4!X8tjnCyVJ4~&qy%fm7!+`%xpO1wR5oDC?Bph1%>vmx z2yiY=jdw;$mCi?@IqTWw)saXi3e)PTwn zN^m)Ey93DN+!XaAugK?r_zfj@F(o`6NhOkU<=a;!k;=%4GXv6hvPa(j+PktayC|1m zeny`C{AHOMnU>^IT<%^SmP{rqU;GPSknX`Q$)t1g@t=Jnqqj%o!sjl?OJ9CTx1E#^ zzWJdchn$DHoy9`+(jGXdF2{cwU}C{kQ_?ZyAsjub!B|b15wzsgVl&V^un=vh zmmsgOm{LcTBIv+5j-&inI~MD(Sf7mg9WH*nAS>8fv+tp!J#h6PcKPkGHzUe znVex}u*@s=6j<5ZcZ>Vm?yJPR*W!OX4s{K6AFfUx6u{NU6Ro)|$2DLO5kA5R^>bTu)v%$B3 zo52{%$+?(MKGz;FZoOpb#j@&>Q_f1ME}kER6zeMBU+%YZytS~fI36HA9l3`0Sv6!; z-pq{6%H;hiLl6^-DS7kvzb(B3oicQO(2&;C&!3jh|KMx#{+l1lk`mR0sd;(ptMAI? zFI<-AU%xDW_{)E!4=i$ zy)*nw+?~hLj@m5s1*z0i>w^f(4aD6*@ohseLv8bX1?L+|7vI`bz zlvop&;4ROsnCsYVrX6=A6VI|vS=kIcZ{pzTKkh^rV-DmPg1J-B&P=Wi?XmXr?&&TEZ2RG!( z+gBx>Oc_nNtG`1oz4D9%f_|Bwo|hHfW@d8Ekjr$+iBCOoVMx0Al*H;^%9-SIc~jGq z@9jOE(%R8#zRym~OHN5J$AUP5^?EXdXXXLNSGn2KVkU^mHBu{Z|0Q-5;HR}>fj+^R<%L5XQMND#}y#U0Fq)mkPR09*A3)xkZq;fZ7Z82Yx zm4$@Fx?|-v2M1iKL(OC*UU^~y08qy@pUxQz?{2}-kxS&8z?FBWU_yWpm(g|du->rm zF21K)n;stf-EaMg{LU}^qGVDD^L-}>MWXWKfBQd?*T3ic&38J)eSNu(J7~eP%|4?L zS~6;}-(XOHlv1_?H*!dAyAn^=HTYJ%V3Nr^bdwBtq%Z6-goBbWY>!Mr=ulpWL{Y?b zLKMbyP^2W%W}t_2WcFbaCXpye;{K#`K6^sW#(XBp(_X#>+{<2Nz6A(gCY7-GmYb|* z(4zCy3|C7Hb z-}v=!7;EXJSDkv3aV1h;`}bdym%jWt>F8}o`Ih$XHYKi)>&4Tbai z8*sf%0ZApBZfZFtNnK)HF)3v8#`ToA)=FSi<=kYn<#!VN)S3;~wG^^Iup!2#&Z+)3 zpmNCt<0|7blZbv5L2hRJonl&vHXcH4eXZp+2MY*2R(7%-lgMg4h*OQv#ii?H*M=o@ zXd`6LX5K`|F?1Dx!C@olW>2&yvX7F0OmfivOv-Y+|HF@bcX^BY!@d*L({0i6Cz1d2 z-~C(C{rX>*aNCo0foes1aYAO^{dM_&{;OXuuMrtu+&SSrN+3HYr8X=ZvVKCK7iX(jiAq<*rHGo+vr5Hg4- zRx9Xd15I8txjJ@ zwFa-xkPBi?f8=lf1+(3^e&^35KEEuH=7=F;uYAwTx?fxpODppH7oIcQ{N~U9w*1sT z`bl|kdmv zcHQUx)lnI|(D#t&GR`9-$+TW>`W*vuIP+j$hMw);aqMhkqmvz(q*iNdo5?aJgsCrz z?(#@DkTo9*btD!tiIJ)SYdG!G*4t8Et=1n z@5nF`VM0pD?CVShIK~2;r?`4|Qu!ZSg6iNO{lt&UrN8!1%HMX5>%Z~e$UpzJ-zk54 zdI&0t`%9|Oq1ag*T|hSQ>Vfd>;8wF;SyAYh{~FW?>z0|w#j?(WYpa*-rK`9oBhD?gB_^AWdX!cfmSpcXplVs3~a@;GQ6-TqiND zOH)gRh;d(J3igKs&TmRkv5>OPa0y-b+ouPJ8^=NGL>az~W%2HBd%2$ec;&q)8LzRG-a&GRiR8(1|iz_9t7~{PZ ziUy5KFMyOo>IW}62DycaC6m1Hw6+02&fJL?OdbhW?lH-ao{y+xa(ie8Av7F{gqV0c zkrZ=LWi!*{1V*{CtrGQ2Q1`veOl#=T3-Z<9`1jIzY4?pWcm2=ghyVIdmcNO~=KC~& zOup1N;#uwdX;~aL7>pQzAa=WW?RE`_Ay&*v7pF>WbZJ;RAs<{n%QnYP0|YJEczZ%# zGmDLa;Ka2)YLm*tQU+wGB_JXFy_C)wfk-VkkGCxBbmJXdh5=-dBuNl&YGrayyNRpZ zEs!C+ApRZ_`236f$RwPgwMgM+jh(5(7BS z+G@#Iu~th3L%vw}jH_yib^1HwGepa)vJ-jbY|DC;CrWNg#k>i#;IAn)qlDET@S5Wm zi^L+Xn*Du#lKvIV{_C+YwkUTW%*)jM8S(1aPU1VfuC@|QkfqrX-S5t{Ax*g1A|G(u zCH$jSatgRl$e_9Ypii1rzAR10<=m@hjGHhdo}@%wCpyh%*LvEZHwuv-yFMXvBXe^4 zb0_q?VI`B(=Dv4Yas`r@Q$qLcKYUBx{-by0-qrikf3{!RdfH7}^E(Nw94n`hq=ta< z{mF%fgz)<1%3D|E_y6_p>oHd4#Q7nU(Rls#jUJCoKA4s}SMN%DPltJy?z!|INw1_0 zA$9M~aXa?Q)V*nw$v3&2lvS01nWm_O0$4Xi#?wwt4TvKPB9OjF;yyxX&_$}jy*G0U z>wpU{`4;eMLce26BrAWb;p@Nhi{(%C2TM?o5HSBUpsr}A^XVX9N+>x0TO1)U2r*cA zRoEQnv%8-t%Crt*lqfl@>o8qdmuh640UKwVayM6D&>+!RNLo6ZjSyy5`f42a(xRkS zot+uY(kcecm4nC=G1UFv#LM-@3h+$V%=J%8Z*vk&H^`&5VYp6MwQT)#w= zq%J5~qs@a=_NxSs04p%Dvc@8;3-_ARwzz-&fto=%*&FL(&VmqNJ8ma`L%B^9)!z2Ro!= zuw9BOCkbg`!WJTl&f)^u$s=ZhoGiWkrH9~sgznF_kms4MMbHQ&4cI8Z?h^) zhm!>pf>mq%Zc9+h*o>fF7-*CK>TkRzfBj1r{(A~yCQ2$qn2aI&FYZk$ zPYfVL%r^2{AR3nOskp4fQ<90NWl@FGbY7K=RLKnb>^GMyNlF`AKN?{`k|Bq5mfKzb z;X8zhcd9ewF=QR0v{jrp3XCIIc4k4|2nZEOGl_fXAGLx^jICYG#{H-kKv0|7nw0cA z1eIptc52z1Z6n%uuzEX5peWH&GOkJs+UC?{W?e-4)@VwVB_*dWxV#H7zi|y_{K87i zgaYI?rq7^D1w7w*UKSP>76$=_8{=B3BOtXU>j8Mfm>A7+DMiL?te=%}mL~R53FG`Y zWy4Am*D<)D`Y>eGbv0#TceljU#mZvR=$j-?+6>8sq%?O#A6|nCER19e#;Tjohprp_Ey$QK@ZCFB) zkhFHR$;WSgBv;=4Oy(x%<>rSslvKuL?C!YS{^X9FxOh_A%A?=M%(~IL6ViLSUy4dF z-~HM+;|I<_BiokA413 zc?%0b$5Xw~D1Ru4D0Rm2>4~jl=G}A;)mJn5Fp}O^l}ajSl}Qe z491X^yYYhDis$8~{=Gw7qAs5;<>dNu-pp`cU&`y}g51z;676jg9_$wXYDt<@#*sM5 z#OdgLK`=X%Jkida1MMx#evJfLSG9Y=k!4SV}{navQ;b_fqQi<)J~Et<(0-|8)Yh8Skq-yCY6}p z)^^4s4Y(b)<*wWWS8R*b8dr1rBK>eV&&={3HKv7yg++}BDDle>vruP1*Tc;gD=Ds` zSQ3HxKDJ;!lTU!G0IP+9cy&DQ*dm)P!~0G~S=@Z{+06|?MviPE5Fw;}=X#ALaB(_r z+>s&Q5Nt~B%#O~>_?;=e&!RN9H_5<-KI59qK3HSJ(Xf)yq}=@Imi+O5_!IfAANitq zy?&V)n~~D$s(j@qe^8!#{gS-%wRg?=u)ucqcghd`#P`eM+_GHw+;AB^d~UcJ`AvpsVDLXY&v zRQBt0XQCpQSMsj=)n6~(85~XFka1zK)mTegB0>3=|L`mFGe7*CeDl*$xj5KS-ogUZ z>b|!T7?d0fl`8FJ#Tq5m=)$`@w8b_+oC_q#wSYgZp#MQ&ATtxXkR<1U4#Z@Loz{Vv zK1!e0KgJSD20EIHft^^2XJu zoRXAhLdnr~>dLJLuZ(fQ(L+3PVBH;d@+vOREK8JfUV1H9m($x&vLM}YXH2}x5(T6&>*z21$W!@Y@Hg@6W{0!~fVdowbj z`+5UDV=2WgHXIE-CJ#BTaVu7GiS-m~awHltx-C{$uh((0ZElH5CY3h(_)$klwzaEO zLi#%dn|R~J$tCGM-BsSo0eT7T&1CG2<-F=I#cjzrbav!)XV4?hooq9?XaM)xq0j%s z$xb)9VlB<&-u$y$(yNg##4&-9Y=ddD&4wQ3$aB zVb$2J71m;Vz=eZ>YL}1mNP}Bn&acVsc;3tk;=TfTtZWjja%E$i3^jS>*%rTCR1#Ea zVPt#%P`BiCC|kHQC5tLNAx4ngtBZN#DoT4hR!-;W{0m2dAhGRko&(nhA$DAc4<5|c zBGAdI2)GJ}9+utBfPTRYv5QAzgY(%;3{D%mtws4qj3ApoUC7D%b2+)9GPQZI zQr#^+4An91SWljGFQ_+*9|dE)%NKu4H?8+BwG)P=OPQ zhNZQ$O}_Upf4BU}Z+=rU*_<@D#bj}INk(ps$a~-XP|m&N)SYGfhZ-)z>M^Vt*I%xm zBvw-u1dmOh*t`P6*Y8WLwMkYBs%I=GWKD%C-b_p;te+rYA|3ta3@T+KydIBawUhkBcVCofBp@H& znUOy2xWD)I-SSqC@HEGu;=)L0+$oHOr2OLT{3sT?0gulRCjDIRrDpaE=m0mCa}rlF z1zB8Od(vtL8Nv&O`jnI-VIl=LQeq#OU`gxmSo0b@jp7M2)2T|v$gDIfSzA+~jq5Pw zPdJYLdBR%Cv6)FG-jn0)6zq8zkC0jHmAQnOseV*@O|L}%=YBRL%@h>B{r){^8)!E( zv+DtYM7u&}T=Km=93FmW1HGRWk8y#T_Kil>cRY@{V@Tu5>It<>8qK5hu)F5X8>12+3N zzClPSxgQ9v!P_XQ!hnfhvb1OKOqklEiN&PEXI6}>ELPKe&WV2|=9T!SXA1&+oYwdRaP@u89aZ&5ZD3R!s#Z!+3u^18xe^y@~S<|t1ygqFxyN<#qtK*0Pa}#uZ+m)7fw8pL<2WmCQoS7 zB|R7Q+K3aTzn;=|!X3`1CnaW!wkzm%E&4a+cU*=y1Io{s5>saJA*mJ?78dn@=amSc zXJvF)qO4y%0oU5)*|@a#w;5O8{Q`;Yu=0_Cy%LP;U+fN=_Q6V+PAl0_NgXY_MQ4`N zLVPTVoVY#+np+o@?Ww0qY^ounWg@x}*q)e5ynMO_J08n6@A3EyNlh%pC9$v~v95Mg zx~5T;39Pqn5~)I-az3KJ1yqhHVdLC-9R6}0kkrccF7@NwEF7n3YMm-6RuW3t9QT^} zT)7V@nu+Ajfp(c}^qUZ-O8sLiFtPhaSHO5hkGC(nQq7EUHB!WHC!2h7LW$QdhQXQv7iWTk zQ<2bVJLjAknKKy%P3*~FyP0v`4N}V~S)5&w&NDqGK@{_@D%XsHLNnniJe_%zZIH)6 zpT#PQ#glE_Y`zH2&Bi8d{of}B#m z1ozq9j{BQ|$wNqvJLXs7S(U}qxaJooh6&H%drP2P_OJrPVwzBn-k8MalTuJZ3n4^y znbI`cxZFn|?1ZO43W0lX*;LMCr*G+wJq%%44_tGn@6Q?1+tL-=R*l#F0@saoxl`pc z;2P7Zyxht)$O1FD+XNE>v<)(rP#JUvay)F-HPLgZD~{I^`VHW5zB3XRSxU(HXZrMb z9AizMfAKZ*dp)9V2e>T|JbSYI&9PJtMSSM>5hX?Ifq;FX zHDKIN_Y3jmv}CE#rnk%NX3#^e)$)%DCxMw06aHwv_;C<(ip6s`<8>vNJziK!$%;Q{ zu7x-(m2IY63_^~xxNXX_=npF~4th*tql5}uzv>dEOi_88jetu#6Ec*bL2#0x(89vP zqMq=D?vO-%lmS^Y#9$xA!L~qbBbjd7dRt9Gr2T~6y2*4;UV!zZG*;#3RR?98@l?t2 zs!`dArLRY2g9`{XiJTe`D*722f)&xL0s{{q?#E>MMIoq=Wv30K#eBhhuC$<5 z1IHty@zApacv#%X`%yHh=0Cbp@2dN|yqQqX&j31}>C;;$8NLXB-(Y z<4U@8>S$uotmfhgNhIqlzcY)yB^lF3U7y&((;SwPl0g>?)SMgT17ZyMb-^Hr3k|>T zM;s*C1@Eh{j?cwSCdNopKw6_g2}k@!*Fm6FeA3Yfd=*DoZ<*eh}hPzFM= zupy0<-oa%W!AAcC{EHmK4VolXSelKSk?sen4;eS7je({3z8;g<)mw22h!-t6wXm?L zOQ^^(;~5j8o9O=s4yUk_p%@qSsf=pZZW$64BgG9@Gf zg9uT}bSp){ryKNzAGbsYdfTPA^tfjQ12BBDwlySNVT0glMb#$qugqb|<^ z!B^RaOo&m}lvtIGVt^5POg!Kf5YMm4eHD`L&t>JUnT&B0!$Qt)78Vv3^$f<|$@G)q zAN2q{Zx}^0$&9mNA3yg(w*X2^hqj{TM6Jc;{=!%Qc)U(&>Q^)v;I zClN{$8GW|`M7R5NmyF$-kobJU5Y_8%-jHj5e#5vF9|jPakVcYD(O!Ec2AMRj6vs^b zl5H8QDPc4$wVsRTT+grs^$6i5b-pc34Sc3{AH4vGHu5Z2TD*wk*K5>C(7S;2KMJTPa7C7 zo;H`zjldvi2H+#A1Y;c}I32>oeLOA7JT9W#JM#+0-DDl;+hkDOE^uF2t~NkdNLfA( zHTk4J?99fxrBJYDlZ5HTHb)RrHU)y^-Nt2bCoJ3&R>`@=l#GlnO0ccTxaHOY`U2_T zw{zZFfJ-wLQrxj7G9{VU?>Co~R4OSYmZ%ya$q`?J3^aKSnXd+{y|fj_7*zrJ;e5_m zp)D*dERG`fGeO>70Lx-Q$ri4x)v&B%`wbo2Sw@oulYDR|-YZNXuXiD>298W<2wO6- zOl&0KOAsf1qm)edsZJTYF=1r>c0ktJRh3yL236%utsoY)|6;#fc;lSpGI>*ehWOTl z1Wk@b6BpY30Pee2$~hgbE#*xztK;gWwC`DbPqj8ZJodZa`V;w`U;0JKq;{Tl7jpXJ zfBQd?*T3ic&38KT{iUS&e4K&O_-bdsDAX3t43%-~EPp?WU`)ZC2CJ1jywtmp+%C8; zNE?5tp9Zkhy+4Fe6Zt0iaH47kyd3}AiCOm1{W0$r^lLBBL9;rd*Az__h$ z21q7C7njz}E`w*5U3Dyu$Z`lbF0Sdtgyff#GT7fDU7fX0q;c(7Hp#5#lIPum=Eb53 zDdo6i-9?aLy??x=9tR2v85u9NRF50txF|nZ!BI-qHM@m{g~gEr;S7{Ct5;-_SxGLX z%x6ej$3UBjO=b8c%mPw`Yz0UV5)tABLr#b7>J?025<6K7HRQ;S)wRKtL1FSepkmgQ z;}dtLB+?p@mX2ogy;kJ2d0CuXHnFMEHsul9)C(ZDSKquYC!ZfOIUx1|xCNSx zg0FqB-ITbg1UkVtr_x78F8<>m`R?+k`oq2x)Sjl0g#G(h06(oAX8Hj9@(&v*%rB{e zO-!D}@dBg>g2EC~0(G1?DuXm-T_{CDA|mja3O?LQE5~no^`+K;Dhaj6fI{v`YQ&5& zdIZ%|F-zwXEBjD;rJa9E|omgQlIkm8` zI4*dm#cyh1*E32=Of!j$jNh1)v#*{OuM$+oc`~$8W^Mg=jJ(5hi7<^mCE)HP@m4@5 zVbb@0B5%THwgO}{sxlrAC_-kKXcO*zzsM<`B6}T&Yhhtwu@#zpdzT<@zd)%OGTmZO!g|VhOro2O65}xD^BX0^ zt}fsG)s2N}b}EiS0UrMke?m$EW5_h&bBPu>WDNI>rui}Ch_ z4Yg4zWkY)rlY0N^s0@EPA`6pC59S`WKOsjoHnYVR@Wf)VpXI}DET~`k>7OZodJ-m9sZcUAEG)Y@j|+$? z$DB*$Bv*Pm;+}9(B*W--6<>>^gVl11`{VS8dEId@qMj)(Kv?sLS*#{+ut;vlf`}D% zUdb5a3JH(8bbmhM#yg_F;RZ!5RIido+>TmxskLpX?M9pj*58Hfs>whMxrVqgql^F| zK>^lUEQ-0Q1szPAq#+WN&QN1bLj4Kowe6IgaV@xbCG=WIRxzi<*-2jEkpC*py(Ns> zmR=NN4lc~xH|;v6&u=Hks|7o~RtpP@qYE9f&A5%6&>^fQ*V4TNoEhXS+QGd`c z1>JvTE+LUJ!L9{l!pE&P919sjz8x&ju1K>INmD~y&ksp%>uE8r(xcbLWqy3oEKAdI zQ@1sf$eQz!)G41S7;+kE4oTO^4zoX*f+5@Pweyz@!z4k>rSdX%!#QSRwNaKD>)R4v ziLd|4FP1;mANJkF*J5vgRS35`OHhw5nB82{x6$vK8;>E#HuuTGlQX%Hq1QTtMnS0+ z#6eQl1Y+tYKXUP>lNrfm^OB#Km*A;hITiI8H(wN?%>WT2EA6-*Hb+?pA0F zsSxaK7jMufJ$fzFdEE|Z4JO`p5`RilLL6_$@m49X+yjuw_mmQUVzVaFB~#qHa%SAZ z!op%VV6+P>VM4cQCvZ{02!r>B8&Bzip%DVfWPMV9E9iz;?iiBRk4hYu?fJVEc>{Rh z5JI)zqL)iS_HiHEJs0=sW?*06Hno8BU2O52GH%-i6SI>l1DKp-LUuE7TTB$U2T4Gv z#IAgw4Jw@GM(2&IZCh`vA$%JFIR-IZnpqZ~lEaqH=(ah^%0fa`<`dF?u6NV;Rbo{O zIhFP0C8u(KO*>6Y&l!n5?z+1b2s;{!=j29m`_JdF@Knd3TI?RYTNm_2XJFanJ+n9l zKzQ+p8Q_m2C|HflEW)-Ds3y;~1*A8;H*Qw%&SqsHnUnnFf&@?Y$jK&RDmTtb3%Dk% zvXo!REvFdkOA| z`yhE0ZeqADSy)(D>=y1=N}N7%pG?44<_ACyGMrV0W%xXfY-Ayk9UKi$m$ZH+zTOQ> z;CHf+Ljnn#VNw+9A7eigluSb6abi#?N>Eo`><*KTQDE6JzdnGP(t0Z?Df#zi*msnfQxf4)Y?9;_3>x)+dAuCL70?tkQvMxTp zD5YFpdd_r9qo=x@BpDQOpSbDv>UOx)?gW!}rj46$TW`y*IL=n!dC;%uw@zM+qZ4{) z397}efZ}_p)o(NSJ_UdSAdC=F$IV*Ky?wfnGlI4nu!J&;y%)eecXVt~gqWT7sGM!~ znQVreH7PU0*Om&VG)qG^FRSy*Mu;{C9p#CAT|wlQXmJHSNuZcOYAIU*aB;$u%7itT zHTwh3b3a)q*99)G+(Wq6z7L1Cu&{Uv!I;?{^6A*JW{6~~-y>1|yPHW_h=E(ygTqjB zn*awU79pdMI>;)7n9p-06k>EsX2hS*%ZY(j2?X}XEfaZ8E&wL^#?nPYt^s$;WF~T+ zSW)ylLQx>Dl`X;65b>KzdfbfoI|bo3WM|(|2#NyZ>33!`<~nKt(vUXCt+-8PBbLz) zJ$FvY=W3}WJ*T^z4J#0zjVnP-OG{78SW%Plw6yoNO0XhH5;am2N@8OjQSoh(c!@Bd zso@!k#3IttwKti3tAX$zyt%M!)897A;ZTpLXM(!#F{l>12U*y9Rk=Q{RyB)9;1MHK z5d_R2Bh2@6=IXgeM|~z%A42BlC)f?JmSTZq;T#;!^?Jc-$Joqo$N;RCoENv=&L+RC zlI>jy1+j{IEo4W*oj;3dT>2V zrgHx~0U{LfjohgP+yk!IK~JN34tV)cXS}&3ESZ^Q6Vpjn+)9jHpD+`{;b_P_6GC6& zGb@r@Oi5Vx;}PMyn_9^jODitG#DZ3ZZc9m+Y$7Ys_NMa6y@lSM$y0IP)Eh6?=s!ZJ z=NMFLlB;v@f(LlHb-=QCI)D)1d9y&8jtjW2&5}?s3qGCM8>ZD(%u7eaCl^|LW^$*N z47n&aU8=NT3B}sl*%pyp{K@!JZlP7TGj3X{_^_b-xM$!mN&rg@r`<8I`t1UFh7M8biG{+?x83L!+p)ezRYsC%$ylvbRw3PSP@dKM z37+UOC1e&W+B}~vC~5UdFydHCnJ{Q>kD4+wO)8HcTp2ax+zN%FbPaWwEWP75C(O3h zK;om65?iwE?iYA&Jm;fiH9ab@1hvk=Ak*r9r2JYu9YCh&qc?)|xPTdLXSTe&q6*U6 zOhdmBCO#XLFT8rv2vgdz77)vcHS*3%!IYA@F}^HQ`RYPvt3$faZ$H6oEpB60ELvDt z90fqMb{a#uUm#ge|Jfcn@$8_iDbcw3?j2K-B(3CTx4>e$UBf3tmNr_>J8hLItr`-v zU0@}}ZJ3E)7s%i-66&dpTW`&+6YzG6*%&a3;{k-*W}>ZJX=le^W_a|@v?&S>xvxANf*)%m z@giyZ@hI|My`*Wk|e}9 zZU*j>(3XP)7E{8)C@E7Z9w4m5fdX#VaZ1_ej|15ufq! zYpgn%&f(o9w+h)NR@kv7<0gD9QIvRln?(9LWc=2&sg+9EmP*jZ@!1tqLv?;^QC|G= zW#aBZ4%piuz$5Tz6yA^R|?K7m4(Go0|IuZKuMUMSeCVp7P*@!KCIbERz1qw zKu{6PkmQUC#;LiKOd|AVmL$KJl2R%wp@B|0ajr`)cZE#6RE@9QfrAOFJMn@{J~j(( zVPRoW-ylmmuAliMqSHs)A~JNTUrt^gG_jEAVv**MoY3$7kl!TiS(sch3-Oe=sS+ni zmRVn+u&pqY-GFmki20-^3Ol7 zF3-f32x}k9>~Faj!fbq&MeQ4G(;fj-tGl=nm^>n#dSy*93yY%w+<&)|fMGp|L1}FX z$+KNyIoaeDjzrer+ba05l0xKi6Z4WAo0I(1f~;n9(trTf#oOL2-KV?cq7qYz##1iF zoq4WDaA_zX#PnKNSXdktm~mMDNflOQ>i&#O-kUb{E{`H8UD9=;!w^+mgjsxkuM(*y zlfb9Bqq$t7Z2!k;h$uNrDzOzYCUfwOrJN*I8zh;?NH(1lU%)RP{n;n-r@!^4+`m3z zNNHS2-}Mh}$*oWB8LMhzW1|#Rel`~Js&9HEgc#Mo-9f)JOwCH_`dta7k}? zWcojr?EM5;{LwWk6H!_09V|gT0>A=<<)>DlxNa20j;lO1xoRc6^#HQJSsV{g1FhO% z&Ok&tn4Qi>7E7zLlE_Lp=#_327`VP(Th7ZZC8x95qU4s7lD?zjFBTDRTU2~3txe)> zjY@M@%n;M(+Z?wjNMF5+8_E!ziTYGPdd;;uGv-!ET3A@rKcc0g%-l%t+5_p)SZ2)e34& zV09Ew4{d6(1(<0hcAzpF+fFf^Davr7@R*-}y@PwVI36%{M5!^b4%8TJFDG!=F6haN z*)_QlUzJan3-X=moP4^NGqbQ6?eB@5+L^Sx7!we+0o?ELP2fu%M#T1uMu2hK+?B8Fb4lHbD^=S#9@B5%=B(X>V_p_U?A+ zJJ~C1`deF9tN4|;QZIJo=BP<#xLPKujcdAZCM(TBzxb6*w)M2js-ARiY(^UN*#(rq zZwJV8bl#;>gLH(v;#+Z%i1F^oq>ASYAWXpkB*D zdGDQp48tVMB1slxc<7k{8B_wy($U{0O{IcF9@7842T!%h+bEY>{PJpNKrY7oazPio zz52smvrroExi-IV`eu>8%Q6icZ6>769l1XJ{IhB{93YF~rN%2Sh5<1;09twJ^;KD73f&2?y0W-k! zPM}B}1@M|YoLtgt#wFUdN|R8+y7pUGSkybZUp(y)2_3G;!iv>x_>&PCzB(qiKfI^J zbVUL>UZ++va{sfjeRtK|4=_rb`k$GSu?!vrsGsVdiX04BNNa&hpvkhEpmc}@A{1UO-hdEuHKYSfA>$NHx`tppx2a0^fqpvW^mQ> z%9ZbWg0j@$HRRa^nTCtnAH05#__dF0mZcY$Yf99V3Ok?Vxc=5{x%=@08M-ter=A-! zWZ0uRi@SuIg3{d6Dy!Qh;#+~+3hVWwqcDm7hUzOrO-^ z>5n+GeqGupclwm+%gw$%Gm3BWuSXfwC>@QLkiEu#AnI1X$!YLWLpd{dP zr{si^f!6M3={?nH5)$RJh4Pm55AF=>9^9L}6&wPq`>7_Myso4a63V20t@;WR{g+!E9f#$a7QZPmbQIx<#bQ6p2UGuO zhxp1*|4jLl#eM-7KFDQcU2Uu509<%U(;d}&gM`DwaZ%Wdm))ZNz-(=Y4(6R99YA#< zW@0ywn!dsSO#f*J_{85D6^S)Tq_bICBff`j#kjsO&>v-x;gI@qSdA&T&MY})sK!$z znbY+LU31UE!op&^K%Y6Sinj}7f(K6)UDnsr^!fcVJvMLR3hN!*7YX@>lSLWTg_14@ z35=h(f+A3n6Z5&P$#F=xk7~Oo7Zb)UHrCZ9f%aBO%q>ekUy#!mPe?ACGZxg|lfBa3 z-6mZFohBTnxh*DP%F8Gj?NwqLiiBi-a$ZVGO1p=8Bs$Qm*HDn$7WHB`0_3^BEW=%j zLi{O&l0;Os09W8#IxkHX^;OAKygVB>$+2MDo*j9Wu?IR8hA0-or{H(gzO9C@|H?0xKh+-^ z1_uXC|J@Bi{ilEMX8DuFegf+c9d74Osy;!gbIO1;DZyYMVu5h%;N7%%ih%{*DI-JO z(^cc{N{l8#4xNX;M*#>qH*+_iskH|2Wby{3gW7R?XGWEGC8?wp!rJU_Fuz+^SXk6M zzSI+{$vbLgAtAHl3v%{_ljSw_31U6R(?uCpU1qC#-}?#lCUhw-0ok$Dl|Zh#nu1D( zoXOj~%1-1$L#C#3X(AX%xGEZ1rTUE}$_Q)I75n=)X((THzshl@gEXiFZ zc5eOE?Sk+d>bhc_ae>7%xtuZ?eYfH?on=5&UE8+l5Cj|=q`ON%y1RSm8bG>Rq`QZ1 z>F#b(YDhssTIoibA-~~%zW4w9n7#K}*IHMc$H|tOPukCG%l)7W(Ecn}X{*$0h{h+K z=GO+4w-0>FGdnYVNz; zV(`B7pgnFMP}q}F6`f^2m017{55;;L@6@6$(=uW`&G(-pD|X#{Dp%1$CJhFxHv0pq zE|Ni;Y6C6K{!&XxbAq6xv|0cA3XW1-MEV)-U};R_MQLA*1>l zw#S)#+J+LH=tJQpF~o;_G6nsyF8fREj3I{J@a9fAXKra=)go^hC~wT=KR|xj6)$A00nDA)C9cFXnusN z{>@VL%OT9DjB>o0Wwu5-Ou6P73GKkA>eDJcmB(pj08!8fKrSe^|HWnsh9o6Qtiw`)O0tPhw=HL#E0)~5)bH6BB`z)508#o9`pY zi0|r+bT1m;F`h_d5g*2Aik6s&%OhJ&tb(K?-)^Hgb>Wb05==`!VPDhNB~SDLHM|ib zl?6b<$VWb2gchPweO{7P4T6@bqxFKgO{DM^Zh0uhIc_8b}}g0kD3#N%HPWt z@^z(D+e6&s+}D)R5}9XYHni)?K%&kTe6Gz_2;p9FYOIjWxuRE9qOUWm(G3HJFI}0; zFDzwkI8pb`&!u(uNo{GQCentdUpsH`GfaJ2A1nXIJ5r0NDaS-(tQjH3e8lXq$@WYV zS#P&HPYoqr=y@Y!t(zsWBjj4vA;q?n_c7g|iSxfqg;#h(wrnO|%2(bf1*oiu8A5_3 zT3Bq6SR}ibP&iT?_ol8IW>0%)=laW1vW)(hOlPy?X2CrX%(vN9!&B<2%ud0xj&09M z_yF`}14BGQfRMH((S=n+OnQpWycK~E$l60YI>D$>vyjXFZhilEyJ!~!1N)Vpiqm$; z4{F&pseezZJ!WyDw8`H;&X$5asGmRrl%LLblH}D4e+&C>b9GWAEj2S2)N|}8_t#jz zwH8{2v{n>#r{IQy6tBbl=qps9iPeaV7UD@^ z3mkM2aG@YWf_V(_%!0_Q^a%Kf8+ z?OXsnftI4bgk6>X1QrI6B06$yv}nBa^m^V+IqI+oEGBr_|b+FRD!`Qxu#BFOTKEfA7tF6a% zi-#pQ0+nqMR()rJDfT^GNC3F5eQ%!azIor#a%U@8S}0JJ43=vu6sy!%>bzJ$heU{; za;H~O1EEuW_j`rB1VsjkRmcBF=BYuxMhh~~<=^zZ)I1~T*#r5~uy9h!Kej-Qu$} zDx&3e-gKOre`1uvQEzWt&a9Ru+zt^W6<5?}k&6`LH5Ezn6Yk@_L|Zj4oBw7gT12EO z%@-VgZv^H7oIk1;Kr!BhH(U?{CcX9-9L5otJlkvky0I<19^`N)6Zio$E;_$476B$i zw^ru=?5_Re@LThP?2&l1jjF?kT#*p6mkeL;JLTv(`f9;cRN+N;oDw>Ju1bGh%x0LT zY7weNPoHJz_3zO^cV4FJ$6%CAQY8|2K<%32?CcEnOnSvt!D-4tGBI72Z|C#63Us;u zB|>?%ARQ!Zr3|X=GWHHE_we$T7tP_{8@0M{+j_ca!}fp;jh`}PDSr@K#PncA!-%aO z7Kc$vF|#LHlG1w-2GKKSujRHM*7K~~&5k?jKo=)VHAeaqBG#>JmFtSR$M^KxLhNn8 zk6eRa_jD0jQ?DslDhKn3Xw$*F#4;_5FDu<2GRot6V$N5rt>CV~kOp^LN z*x8y&)f)Ea-}_3I@jle~bOJ0`HA?cEG5qanFH)$DO_F}mMT{VPC_*f(8j6Ce*kP)J zJ28Q^(t-EiTiSo0lQzz>8w*wdDH|)*PaB#XeT7wgPDD-s(c$6mWo`1sSYR5yle4jl zNATxM{^vP|6a}kzPdp5J3s;%!>>tBb*+wSG6weTfq=DuqYoXBVm|=tBRp?cna{T0& z-j~gX(iiRelGJvBwk$TH?>_9i8T?%#!yw!ym zBNWW-A~H%Zog%Hs9yj#Cu=g!h6R=#Nr6r*toA1XgBy8BHtK^BAh2;|cKyb!Y*Z;eZG!Mhsdz6r=8Zreab7ZuvoUX7CvYaBel24~F9KF@ux2f%%$Pj5 z_jh|*o1WnEQ@v4i_tzd-=I!*~NjMaW2cV@OL0D88>Cb)00IO>oRw##20E5I4n%k?6 z_exSFabx_^-5X!fB?4n`dX|sRz{MA0kqQmcpsbtk;TVze4X`LpDEYynJ)>qrM+4 z0=^yRagFxm&B3f?S0A)gl%$z_ht3<0*$5g4N*&*gS32w5n`XA1gj}pPT`@DgWsLUy z8#^5>R`Jlr%fj?F8U|zD6MM=1;5|qSm!2jyU37cy?-sL%Nd;c~=^o2vcn_7$I{H zS!4w|7J`a}v8Nnt(DrUrZkNZ{ zV}#HnxQ{Rkq`Ta;oO@^7t4440{P6O3Z+x#-OuJyJp~AAp-`@-V$mElGW4jqark^e2 zie};l`FL3CqjOp72r@PNFi5)vU*#Qguka62bAw0asm}&k7Z6tE9NTtBHoSrF_;*-?mQv2H=(v$!sf?`6!7RO z5aK)V#Sa5l(?zqVA>BLzxEpS_aI?>bKw&n@-N~Y7305kCK0E0uKwprWMXiQ@=4WZm z#f8yTgVoO(rG2eD^rrWXXWZ!En4unhx#2&p^UM!N&W}64?RTcF{coGu3nRcKX}L~i z#oE;$zTG$lXwtLYQa_x@{yCPQ13C$aq8-%wNQko7^o!Iih8jlH=!LL^ZO<$I+h;ki z^tGLR{&!(|Z{m9**U#Fwkon=(i|FW2FN=3vti3{kr;<%|uJ29igm5NbX~@4fbXX}_ zvFd8wSNTFr5(RQo?fm0Ab-KiH5v7x7yr=z$lkc`ddY_p*BYUjPpQ7pO*Ar8;iiXEG zxD02QpdYS$vRh!%p$czABKDMCY_=V8Lst1>TCkjsxNp{A`3x zppEji7AYNeWCr=>v~UoPowEe>#Vg^QvBe-NkYvuoW@0{m=1gu`XPdPMgyR~B>6+h< zbX%JP@I`~$5Tod%OB-EP-uT|EJ$)p-tahv4x|J2#{yf1@V2`a$SD8yjW>3*s2_HFP zzz6o|aic7uwG4TW&A>?2F-_Qj-cZzKv&WGkDSYVVAxcjqY?6rdUBVU(g|_gPKc8K` zZpQv_9^kH=3(GtQ=45;QpZW#~bqBXFV!Y6yha6AIpPvZ7$n`<1P181>ZThM6KTGsL zdmc~49=tDF18>INPOx8$NZdQCrd@nWDHm0IsnWLYd2WM7hM(IRUF+2H%Ym#1P0Rxk zIy3ooPh>PHH;uDZMxPHtJgz+UF-o5L)j4?sWA7i92&WDjGkx^u`3%xd2b>^;iroCY zlxHlJwKl*IKcs=0Ij1$gDAbM$n2#%>@e|I78b+)LqU}L6|93lsrP7BVp+Y)cI+G8#8G*SDg+XRarLM8bwXvH%=MDO8T( z-DjpSH}CKbFn=HvyChRyQTk!_?&MucA@ungb=Wu!bD|lynI6m6)q1AmX=-Z7%Jqx> z^VG{V#pt-HpT=^x0c{QKO>?_wS}=^up7^{NsAmE_KTDerGJR0Fw%@!L%bgx8hlz^e zd2`fri@TtM8NT^n;GjVacil&I)HTb5_O|ewXGkGJ=lJD9of7M`rxdW2{)@R|fnm z6qGz1xN@VNilrHNP@C+4wgN-o9K6x3|0ps4SAMO4Zgl4NSa+a|6X_9&^svEM(e)gSLl!IXnfYDLRa`TeIMc` zS<}M@@O*^y!~IB9Fb>L$KO1KooTyrJSEA;cdoU^}hA6_zpx9CV?N)uZt51`n0I@G+Og~dTqjrx@bSFrQjRE{RgZamLc(D85hxBInCnX!++~Jx3^g-%Y`MzccxEEWMxx5&nZO`>zMX~AEe6YQ zqgP(F!E$d`*L1z3NIxv>jxpGwG4FV;E*{O;w>b{NV&3t+aaM<~>6`PCiFjoNX^8&q z&@ZsU-uAl94{m-g29tiZSHvRd7jb8x{UA<<5i?D5`{_8Wn9n*fp~RPx7CW7v4w*hx zR9xgem<;#qSImn^WL0Lz=7Yv1D2nJvIq9rlPGE0VZh`%w*EXg84N4`c8P9#Xz9&i? zMBdO8LbcJ`r?j(~+J)^$44cwDX>77t)?O(Qw^OLQn_|1bOQ(C49N*|SsD!}nrodX? zK!>E9rHb2q7VfU_^&3O$-KdZO@HWnbs*E;tZK&H>1GxuVG*F$~h%|XOL6$_+vI!)2#~0 zqxer7BnptHW9^k4qlkLRx#nqN3>t6#ufSpnApZeK~c0U z7`rT9JypRSTNDl^)v1)wd{e`X)~5K)9C8X9BX{i(9{}C$=6wb(HawCBIzBf&Av#X^ zC=l0C={!>L9cfGuTG`s^FY6GCDNpPv4jaaE2PMf z73ysr=N0Z{ENB-BDfouI`_V2i>3xbBO$9$VKTQ=t(CO->2qU)lo=0A01oX+i!xC~# zdP?7DBBaClH@@2NuEjSC_0O@U8GGwK3pgW2f(#U2j%er#PG;w39ndIwEAGZa!dtb? za70JC?BpP%aRzdE* zrMDJ*Q%28l5?H2hrH0>%?5!ft%*|zydK8Kp9ZpUgGfa1FdrG~ZmZajA&Z%$0c8lKF zp+Tqqh`=<2+{Q4mxnG*PZ0_d8kQKKS8aG82Dl$8}LC4mc2Z-fWu(639Y{ibR1(lR` zPvT6tR3EC&DI2pyi7l|sOnIdZ$)L9mrY7CUPn%_6<{xT-Nz@N&25ZZIbxfAw+KPBZ zvJz6f(w(A;N1;40LadvkA9~sEh_E2i zrFN@+D;2W*=RE7f=7gKbZTMb^O@$hy0>+wp*&^I|#l;ptlwK+&I&aoJp@;oX*^uSo zwu7{q#{rI)LL=xDJ~}EQx64h~f7qM#mkSCjCkvB%Rm^=#{CWQecW80otqyU&P~nkZ zyb?ZvUhML>>TtM-5-;T5vIR0HhmMyd*f1vCoo{8uTvMuv^&TdZRWZZbyy9M^u@bas#IQOrUG*u$DBSv`=DYjRA4@eeExZ zxi2v+u-9!59;JS`dpw|4C>cXtqkp4_nbCx-ufAqaH4-KvK@T9#QrV-`XJqzeg%~lH zU+6{GcDoUDWuIk6FSPQm^n4FG))(4}1Z& z(zvd?UW7c+r*&`_N`Az~xaYmrNPg0|rx1K}r7J%rYAR^o1I)Ge-_*VO#bgn7eGT0nt<|8b z2&ET)(R+dRXY}6`JX;;?A0;J|Vwd=TUBQ{lg82IGrrE^#5AQ5;jF;-=r$JCtE=Kxq z-a^m&Uot>;AOpZSlkJH^O5h8}7nSFQz-uiU-}=z3!bTHMiC|Yg)a|fw2lsciS<+0KYgqAdkhnPf$nWX&+c8 z`Wll0Dbq?h8xYRkcdPE3=)Bpi0tzWtcir%~OF%1I=P{(@@{s9np~GGAk>caHSXhgP z&v}FkPM0)>dy6o!{>7Cr2U8KXl+>)TWEhWzv$C5dc*SwiECVoH@xf1|Om<$DsFT-% zyK(K|xGuUFEgZ3llb2!bX${~QeZZ9+hoMNfpeQ5Y4sCf%0?Z6`%0O?<0u8wj9H4rr zm1(JQ8)O+m1?$OaL-;9h-_+^#EErX?N%E7x>pwzCX_{a{BGYIGSbcxIUWA3>^QBa$ z_jO(L*~c3)n*uZXkd~87{e=6xr7ybSI{zpe2&rn?9AW)e;qO(#HxVMU<>Rh-aH43e z%nHBJ=&Yh%3qNf#oCNwYa!#z+nJeeDpMJ=rfSnv|N9ce zLb=7K5c`pV;LMIY{e&{PHh4I48+oiQ=0P0hwLr!&7Y6yV+4&4#N*D?Vs3 zCeR4rXUW=maGEI(>xb;UN438Kc70k+V0#ZsUzERn4Uxq3t!52y&HeWEV14D81$D|7 zWg2TVk45p18*&zL0e<3$Wfc8C-460vXIC`p63&8R)%k3XD=)O*Q$iC#4%0gfXQEAF zN5}L#O{1pDIEZw6Q>u#>d-2Rs%C?(iwjni)gPYF}Nd27+<}PFs4;(-tRq8)isNeU0-U9<8=agrR1NBscvU ziERIXX0BTfcdG;r+7c&&rLWyQ$3L}U_`9cy1GLkP_1Dj4em=4Y=KU~sSlZ&T*q>sY z4A^jMWs*zkCGv4N-iXkjd$)nGa%D!_DrMTXca*xQR-U_lK7IGJeT8qEb&(&;<{*a&uB%$THIcaz;_}Ud5ekDvBCNE{ooVt2On76U!i?Z^6G53m%g(KyH7Uv|H7ok z{egRNY@eZQzHh|MVvM*3o(4^XV=~V2Dm}X?Aq7*PUIq9c8NP z9lvyq{t#Je)35?^{PFh3(e?P`t@G$>52Y+sJimjJ_Nn|KUWlEyVHOfcUTjQYLjZbQ zFOkln8Kxt;$XT=}=@WHWKMHP^0#93m?v@c@&K_(q$XNs^@BzD@CgryLs^^`%!=_zr ztn*b2D7Nad<|Jd@=IWkEjLwtA&oHOLhL2N}5y?$tN5Sp0gC|VHeqLsJ zf(KsTh^E4R0GmEFVQlUCrmnTK7{*TnkvsEF*5A?MjMI|)9U!=7sP6xkrG(<2_^7k# z`KsZl0}KVs`4a1rH_Ev?>{%-3>>2n7j@S*b+;KXNNcGH(6H8Mwwd7eI4yfYuN4Aku zv|8w2+}LlzM`!8r%UAF}8RXBKkk#wXHOD&t`16O-v9EgIPr>s*&W;*EWHc4oKaN(x zh2AY#g#ksvx}J}i;TG#&)h9p#7Xt<`Jnaj{IjP>Bp-7rjT*i}T$5)bV03eQ;_B~v= zH>!hc07GU`f@S2FBE?}X5(*%Bg|g?F6CH8p8QOPY9O6(SeEMo%+=q8b*DcwMXJmZ4}9nI+KnVgbP+t{fp{*29+xa`^s$B3 ztJiBNf8P4Sd>pqiCorTK^~Kc)E_v{6FU>x$ThvdEpn2DTvSovBJe0akVw=f4_s3Pi zH{DICDF;^rKn)fN!orpn_o^33X%>m7$=1>yfNCUA*Y86ko#Rj#l07uq)2V=`rv+1& z3;m=^Tq>-)HNL=6U|H~XhtPyAuu2RTL*l;qL*3-mmk}@(Mi4QXS&*9=YZYSG7iG_} zAKu!4w`9xA?4X4B2MDHR*rF#H_nkCL{MBSn#2=_&!}U*XvMkH#@w>v6QQpNsNi>C4zYNy)P#wKa(( zAh1fbe&qa9_6=@<^oAUxXiO<_{~K{slK_DnHYO(;y5E*HyQCZ$2sM{6gFdJMK=Eqp zCmcM4g1lhy0$(^p-J$g@!w7$IAMZYov+!?P$f_DaF2xN|Zk?u;L|~yw#f7B%TezYh za-(J4ShGLcqQ;shzu;y><($L;YI?ldc_u|ufT-L)Vj7%bkZz)k;j)I7b|jT0iKqoX z@o#mE#3PRFN)PNK=G?8|T=(9VGR=&WbT6O@X?|0DAVrw) z5(1&$u*{qPzS$6D&lgIE%Q(`iL_IiRRwa&pCmk;KChEVXFa<9VUgCtZtz?jeV5PF^ zN~j+?>qIYqjd5Y42vIcXx3nFsYRj-v{}>rTp=xeH^Cs&P#ZTJQ^*aLNt# zbUPUm@MoSVMJOh$D8{~pqMQVw;^O`Q=9r#gW)JH5Mek;(nAl-^kMm%WX?LM{Ft32)8CYs&;n2T|3mC(nikl$rBa9ALv6$fa zym2dlS|tVBnwSy{0Wh7tU{AP=NRR{`{5P}Syn)*a*t`>(v^B?nNJcL#qacpK&%@kp ztys)H+*jtiFM-y2V?-G#T0!CXcTC4DJ zWXqg$0^Sa1>#|%dg|Mn{DA{`}Khnz$5C7>)t0wyH=E?OEhH<3&E#MbQ3+lC+w|~!C zkgof5Vh#^duFd!6Ol^ccuq$B;Ph!;bR@+CTsOdBiK@qyG4O!xF`HD+L1%=YlvQ#xc zQrb1GT9I0Av>*R*GYD}6;WWg(s9Q=?Wn*UA&=WBM5uJgi(H`BKUbbi0Krw{zUWhy` z!|;i|;MJG#4)F`j?u~v;ig&+h~kbJ>@U&ls%7Hf9Wl0ffW!kv8`;8gde<59 zv*{IQ_nS<;Por%S5o4wtL!h%tdV4B{c|-BS$nDJ?khnT)old|?KJs7EHhOqLrd%9+ zk_}ii&*>)Xc(7yIDKZzKCJ@-)CZ#GNn9nzuN*mi6)Ye1BK_uCq)8-;^S+rq3v=X~@ zf|9n!K!vv$-kRIhJzp2!C;1WUHi#+@nct-Rae~HM|I3I}O#Ky;-HztPdo`v}HeGFb z)`$IRYYRWRGfVTe&bb3+Eq{wxeJc8%HO#d`Q`}+vnbqi*NC08P^~a=N zDa(4U0Vwv2<}o+Vx(A{&OnIRvyZ*hfvW6v^pa73$kM=b}6ne%FC8zV$keLAgrd#{3_DH`9?M{T&6|6}$yj=j+ zTmo+kZ0V9Sa4!!-PDWbu&X_blq+30yt^_t^u6*Dk90_s(8)sW;Bd*>oH}NoOu`MD$ zH2!1|YANXI6nhg6#&aJs_MIT7t%*J3Lgo=HSv^Jf$4bNfb13O3ZzS%MMLcnxIHZM! z;!zW`l1C+~n5m3!#oo@W#Qfg%(mdUx*dg}6r(BWp|H*VBF-tmnAVK++_FpMhziW35 z|DU!_;qJ|jI~4x*BI;0E{`TVu)nK#{E$r{z)0YU{D2snO6d8&ZB`~%s?yQzT zuqOH<^82FhJbOw4;v+y!RJ@_t%ALXLR_#Gc!nzmECf=}&&H@AVjYv%jpBFeCl4;jtFSKLXBv-hgq zP8}co*isd+wczhvKv{l`1?%5&W!84q=4EI6f`w*@UM|*$k#`_2P#O3)4FG_nw!IM5 zyp#Mo;^T!q7HFp7*C=Avbl+7M>yI2^XIBRDn@9t1+TE*WD! zoLXpFlwA?;`p3Su!q5=-M^N8qxEmiryW-YOP79Wni9|j{u5Ynx2VqKAMQ&>f(3O`b z;0YkZQhoP{qu3u4u`22y*WYGnB2JH~?Nv=ZaTwNYjTHbTw+W}E*O3NwaVBgqjs$fy zi^E&tEGkcaqPRO{O=86>4QiBWj`pcN4OC`IbdRWcve^eqZcm;f*^gMi<=wY^M%#-m z;Qlh}@ALOk)k8^_|Au->&SUj!B{jqXKb;mY@3`d6ImEYx4N(XhY~#Vg?nH3tSqJl2 z`zGj0kOVP$=p5<$zkiWey>besEUQzQGIZ{g1#;IM(TvAENw3$L^`I~g_R0|PJRNX& z_$LlH^S?f{y&BJM$8L1uW|$N#FX6||vP}ua`I&;I2>~iROEZXC%|`wV~&6{bd)+$GQ``zc3mjMF?w(8c^RUr_<!}oI--s-Et1-eaMR`ann|{LB2c81+TmpGPPKn|j_~g7gfI4!b4zlIZ z+WP40c>guD5e?%(e3IE=TJ8c!qFX1g**&)F@U$(3Pb&>&y(fw?6BQVD#@dNZ3nYAA z08GHH3iwGQKkG@q%19Vcec@KEXEJv*_(nLH8KT+OQW#15G?m)L6ewobp#QmmnseX8 z!PQ9bzg35#7KX@EH^(7`{#OW+oH^f8cm2QJ()h&D0v+EVM=d_CvirLoM!Oh%c!>>P zDc&oiU;ap*1#@<~?WVy19N6+~BgM|4ta_>i-DX>6vDE*T0%-{-u+o;43boY|Nr4}F zcya;`L8%XDMq^TKPZB4FPe(?bDmMX71s`ny3Dhgt0IGV+p@7yQmLGa@;gT0HZKCcj zU{+v`{ZeZjd~%X1;C$`<9of^*HFl#SaM7K>R7t&n`sd$*Xz1+(-7svuy4F)QlhZI} z(N!+W6*~R*DS8LS$$~}ljMUi6X**NFrzv^evw_&8v5MOAsdz_rbx=|DduP@il{{5Z z3LV0qYu_B1oX7xvMG*V388b2!0!ZqSvR%WP`QVbp-)BkhpFVKf7EYfv=(Uh-_Z_%) ztmS80i1Y0)b7)OTuQ3g+hl|+NXTKPICXu7Yhz)?N6Qz}x^rHH6H;&m$SN{4&{ZZ)` z4WIml8c#2)XX_07+4DH+1^mSG40VR;Bovet&Q*-BQ@O1~EyEcgI@}AaN|Te^S?rDqsuG&F3aq8H zITMNpoBYi-!5%%UYH|AT{#7 z1w1F2!dlB9ZCA0XAP+n@WOt5MxL#)o$iY9wnIvvB1FUh_2|=I1|0@)F&@jK<&62bm zqTr@jt8-zvst+OkZor}m1(&oI&=RD;+A=^B{|_){SuG@!QHZvJsFu}zm6u1V-QyvH z=9HNgOgT$B`rDsFBgveu!pR8tEGZ!NH4@%n4Mv{Itz)t#nDpx{=z6Y-(y}Z$sZUfN z6PqTLwb7%?hU~PGB96csCQ@GVHKA5GMV3~&Pem2jkx1pg4vHoYP4gGMx|lmT{fb2o zNIqo%^^`TwTktR<^_o4qVWn;+geMYj_|&5VHw8?)5^l3X2WK1I;qRNa$RQz1%1v8b z2e7OHh6)m+9Kc}jdw`cxSE?RXxv4O}x({pdXqVSg`rX0o9G)}o%TqW8I()airN--H zv=U%V3*1~gz!E?Qe}=D@Ude`;SemRCJ;U0IS}5cnB^C$ClYCP@Y+d=!>hJ`?s(g`$ z-p-8yW}VH6M`mzTWJ&~%D(0_2BmiZ{g7Lgj>}oDrsx;rK_nr2kgNdP=kwEo<#gE02 z(!ye$VbUi6$H#tWe3K}R@78I7esD~5{3w9~u;MgKuA|(xSBwDr^xT{gTz`@^g}!d% zH7TOXo?FBD!zAMynG=~;TXV5ZKlGmvf(Ln;rVkX7Dx$vht$3LS!h8kG;`Ror9G(CB zCrG&aj_`@Oarer6zn%~X<}@2=O9g2vQcKLKY)0={>nQ)6%j-4k<=dVshzCYa^obSl zp2zr%uKnz%ovYj~o(uC##GAE@?2UhrePqKT6xKV07rI_r1nmywvMRHVb?Qw;EqZ%Y z`TXYQA#^AN@e^-u!=``Z~x1qlouF%k|<*gco(Y4+>NUb=|wL;_XG*^o@ zJ7F?2bJ@ge*4NDilX`FNGD2tYuii(kb9p+;8gyl=#6yZhh7PQl@D*nTew&yy^EM2F zM>+I?DW^#zNpqy*P!(Kc{r4(g`?WT_X$8mz2Y6W+V{Q?Xw+ zm*zrfWyek?gVNB)!9xQG@Kc!0bmfVN%ct`ae$DBH?bI!m<_5KU;M|}r+tu~8^=Z?U z@&APOSjyCGsKODMqAQ2|jqa$tOsD6*B|u@pXiIS|CFxlf;>)aPUG6b0^6*(MQh1SI z!8@R_tv+9CMC2B_fh7DEK}m>JP%K=5^DSBG6JY})7Hb0Vc>q}i+JG4$tRkH2Kr__ z3VC+Jq=Y^`x^k1^X$l!&h@A%hJBrg}H7~n2!p=zYRo&QGKZrg+lG^RqstDv@uANKD z8lsugha2G5{MY)c0A(=xj0C}Au|Z7CPr{qAOULJ~&c20wf~){e?-GwRS+(L8?3QRX zg~O{*eWu-NQfxlNlf?56UrQqHHl|xql`Y8V-|maC0u8g|zd6pG=n4PugIgT9c2^Nu zE^inuh=qdG&*1HwQX)4G;BS}b-zUK^7-`SVBGwmHDi2p0wL)gicLv(Xy}^Wmz3-w2 z4&B*I&T9pRYWcG}S@2;UHWo#`uY+R~#fyoZ zlnkpig;W#bFf5VXj0%j6_#DLw#%*Nd1%6{NW)K>;PrxdRu%X4ls>q+WF!E^+4FMQ( z6Jv|~GBw%S#)C*Yv>b(L@41UaZb5Z;lDlD3ziv)Co8j{eJ!e=5I9m4tB?7zdRmwPO zu~UcNvs~CAMs1)vo?^j+ciDlz-&J&-8`!7|E6UGIGB~LzG%zvFEJQ&KA(x!gRsen2 zKmyD$uM$>ovukAIguCKPEve4T(1F1hbVlIsTzwu*&<}$Y$~qmG?Ty}kpQN&_$f)GU zz(CF_f7cbu>{8A>#1@l17FHOQ*M=Ff1Vr1)x=BNuEJo_saM-^Z8~J$D{r#v+0$uLIw?UOS#fr7x0`iA$%K zWxUlGLU@k`9q)HYC^`fI6ILFUX_ptYO$>!_a)9&-`O(706Ln)@V{=akxE$q0q6gT{ z^|I`PSFlDwYMjuNPu(QP5b7@g+5=sEm-j~!ZaQn9sp#+$^v-b`3|X67gHuQesD-1kpMnYba+_JLE@TJ9v}{B4E9~# zzo!HiD=LqSvG=As%%iN^t~;akIr6+dTF~9FyP`T3No4rlYD`T9mL_rjHU==piD2+(GbXb7gj(^=Z$q^j5oF zSu8VSg<_qjj;Z?L!&iNvHsbdKiSLcM%lUdjpIA(ER;*zWYX!b^pV8J026DVhK?N)A zl|F!A)3ay(9UcY8A7{7ma76KK(UoqaRi#Av5&Idoi06m=N;-RO)kc~AEIExdb&|E* zi?|6Mu4ts8u}8-Z5n6olZ5!gK_dwcqtKR5%vqi6L3fn@&8Au|Cu@(>?<%<057IzVCds2ESjj0_~l~hi@-zY zFHP38?WvI;++LQFZqOoEDKkD7j)tx-I}4?Mof{6m%i{5B04m}5pwxAJCi%Zaldq8x zILGo6T`(0bX>OM?m^@fiV-z2BwcOEi-KMaJv!nkgi|yM{vH2wxVoTSv`$c%SZ}Eph zTuSnsn}J|z(gQ-@1&2SPqpa4nmzenE!kP=SS9nE}c*#R*YTqh((xM>Zg`}al=mvi3 z!05oubT7i~@DpgumI*XdxE7|~{i#7P$n(7QH!I00cFPSAgERrxl_TNdWC62$-lZjpO1` z9j;r0mXpX~X&`yl4!ASgT@RM88)gQOG3UuDiY}=DI@~mBK zPT!z9^SIYvZf+9Dldb$fs)_x-=%xC<)Xj4|TWF9QQDm681GzVor|Rf!W5dS`MM9!w zCh_9n_pUhty?q`3i!5Y_8R&q&BNmRB0g@}m7pdDR2jVlo&+ z3pj~~8Mz&-kpkiYGZ}S-e_her?V8Y>F;OfnR0Qu=#_aQvTuI$6F=hpR(*Ff^^xWJH zkUgxH%RE|Ti2itTgH*~OL>&I^o+J+KyPh%@xIH8;mJ(Kx zXD^RS!_%`)(j;RjJ^Dvrf8@wTXA5)sfTbzOS=vt)9C33ROi;m}lXPo%tQS{KEtkT?t;g}k2{cD8*< zr~d4_{QB{7_3;Awxt>{S+uPsWLjiR~1u2}-Kr3P5!2S9s;OjfWjUD=uj@?c5|M8qn zKG}bU&;GVGYn)LPlXCFaeS0*X$w)RLyhUTdNZ?gzpY0L`g@cE;0M|TP1L`^sjJKz#^FDI_* z&JXc z-1f37_utBMIZZSVXjcMm$(-l$v$>h>LzUb4-r&;mU&n=5ZfBdru7sQ2m|DqeF`I0b&Z4 zLGf}epL{6`XyZe2XHRLV=%6#p%n(J=l3pIWXHj&zm8_6K!R(#D=OI(p`f4#C1`JyG zZF@qAOv3gTVJ*q47PjUM)lY8-rrEmyV*ci`u5XUW7xI|l#7B1T0VE>v^b%TZ*Ln*1 z4Z=A;-SJD^zXoieUWB4>2qL8^k539xa(^xLpxnOv1$X*%Xt55ud~LHf{0{WdX@7K6 zlTUlPh(SakkmkO*$va}I_XWI-C%2umYSnh}h<*Ew&taU;zKWt|V`094Uual@x)lub zepiHD1C_aRqgS+s*Z4K*Yf$0w+v%l52Q^>#F`R17KP#lWB{W)ef1TQEu5C^GxrFtv ziS*e+8b*Dp(LdXI=Ir{aXLg=DnTnKCZjV%f)LSfveqcNAIn#~M;WiO%u60 z4PWl^gb}H*RVebG4tHF)9dym7r6OHrD;F<8aN)eizR z^;h*Ew*@O6Bb|Bfs^EO$gR=n1Lt>$}Onu7j#0!h>|_%8BDOF{ZgY+ z$yK`i$3|q<2`~gi7ItyJhA}}Ad~A6YsR{y)f&R!fBd&Ce@7#PpAipKf`ohV=!2UM1 z{p0(NdsN5Pnr$a;;}33~3qCq-`AHCOF_)t!f49H$JUh;LrSgmR?!&r{@M z{fZvy+eFcw)xMS>pm?4fWzgSn;TQSjqPS_Xm`FGw`WFQ^AwmWIs5tj;hJ+)Pp`XLQ zJ+Dri+qT8E=e{DhpRk`g*|p3wqW;$iO2BC_v5=q+9LmFvv@&;?N=?vWJNB^l-gRp)H#cla( zL4BZ$mQ$dc+@ddY!jJ8n6u?4piI0Vk;&j0D62aC5^}k)eu^ZdA)tGH; zqp@wXL1Rp8+qP|+jqPciJk#I*df&4gg8E z+?!4=QafhO4&CIl9sadKrmp$E6OgLHgwHG{d~^n~zi6tqh>~@o%vg_C_aPS>jRqB_K$WaHp%G$ zNaSjKT-Ik`+DktAneIvQza+faE{Lwfq@8aK=EZgh73&^g=`_x zEZ<8uFXu&7SO+jI!Zubq`;YCM2~hH7H0NWv<=}6LJCca+BR=uy;7#ymkDq>Z_j?+x z%;WURPSWG&f$#H0mNlBbCRecY#Z|BsU-aEyjh&>K?JlYupH~%7;MMi#$Y3eCcWotA za)NY#M&YA)EW1pHLI7gPp7bVlO4N5(?!%YA_?i&E6DJKFDNc*6ZNyDlp!kQ$+I9>L z+GOQ_#SgTwb>y0>%AeRno;~qyL&UTY=R6M3`Qy~$WejBpnDCL8<-=|3fwXP~Y-h-+ zlt~0F`;!ZMcHE$b!!_DS2@>31Up4+}W^5DP909O*8EM|@N{oZz>W=Qr6rI~&j$l26 zGZ`Kx$}>We7*j7B^qvhnjPKF5->`sE*VG81vuvJq8p1sNzaqZ>Q#bne40>tgMKiky zW`6GW^Kx@B``+j3DuQC7=1Pu$Jr z`2K4!uQSfH^Exx+ARwCdJhm(R_iAlFAZCq5hcO3InS3x~AnXCX4i4C<5Bov2vK=3L*p%z*HqF<3OXdLjBuOXL=HR0R-0&vO ziQYA^wx>I&=et&$TkaFGVBxK3T+;Ogci+fKD( z)_Yvf8wcL}bNhY3rW^x3(3)`(^+$-NEj(RV0J1Q}kI%v`v8;bYX zLB#)VZ5W7Qy@LU>gvAZ8ZCT{%=!y3V^7-`*BiPkYMcm*er?>53#H`B{Zoela&(l}E zWR0Fj&bF87rn?_snK~Wxy5A+PL0|+&+-@!#SQ8Z$@%45c)4PvC$X{RyLn{R%5SnI4 zeU))SSs+Hj#F43~&RP4C7(7@0i2^M)GfXFe%^Dw!c zoP=;b6%d=%BDw7rN{Y#xVjZpe%+CcS;EDM#Ova(Uhg1RgjwCQa`ipt}_kHCF_5yW6 zW3UYreBMk4U3YT9%H7ylR3B5q5tBTVS<&S>fGAr9|B-Frhj>n2D<^BkV`A#IHWQ9J zt6u>z>P~z6kx4|gDWuqmZKC7)zsG=)J?rWL`*Q7Nzb1}r+;7OeUp(ym^yrVplo^v! z*QfHQcxRhzX0vnFYFsC*UZGx;s~9Zeg#|TYY;-B!^1y%TtmJ8R9@o>t!Q*7pi|y9% zb8FjJsp~O4&yQ#sr^oSH$7>|ckiv?d300z#6I<*afFfk<%1SAlAYI*Gv$lsbb|>Ja zH3*g>o^PvkdSVGOekyZBqBl7Gz0(pamx^oL$%THqn~WGc(T;_z)Z0aVO{$y%9r1y| zb0$i~suJf2_7|kP;3>Ct8R$p$G8(Q3R#fpxhi$Ey9&VEBpsmgF8iUZ|eUIWt(}%%1 z=$%DNLMK2Po5pH8FVf+(oS3fom~<1BY^(-S@Dag(UvzF`u5y&k8wN52-nIqf_M%uU zLBB3a4=>{J-M@w3;lYGcVpczzM++l;hk(hh44_bL-PW_XO>;Bv9q4lF8cJ8co;6<~ zx=EdMj$2Lww`aF5bhuf1L z`y!cB@g3UVo9b25i2A?DunPu#d1r}GxhuCbA3E-kt3t+y>VxH0h6OKKJ*kV2Hd2HZ zJ9g&w7agW^=*`PpUj0(|*s9w5Lg^i!p^1l}MjXhN%*xaWiXXeg{Gn1+zTKzF`(s}h z5P6X9@%~k*_-pydxqD1ifB|--=ev%V2ccOH|H^G4qEiCxrK!!?O%kfg=i-!wjS^vt zP%HJZo?sU}Q{&2$fgp8Re>$z2O629yrO=1C-t%39nyai);W2eG#XNxz!@%|bxBFSu zF_SDKyDOOap(rzvl&MIklE}TqR8Xm_rMM{~pZ9ZF{~&~H?Jh){8uaOh9V=cg9tox# zg9R;4|EuVz7#wieS$H%noGXB=?OB?%A;PE)@T-qY_@x)5^U$U){4u#O_gSZ$9bO)= z1VqFhOx`=1NGWrf<2MJG{yB_JYDnJw==vf+d1lW+(Ih5uTO~<%Tfv>8W=Z4U^52-G zgL1E+u1-~H4K~P;1OlkWCvg%E@V%_ddQ6G*o+FHd09`(BSqhVn)gSV0H+!Cz59JR! zRY?qF9$Wyr)(ug9?quFpGHwo!B*~PA>+(wF9y4SXFJIl4>AyC3CDx>Dj^5JgeCd?y z>Oy0H*#bKTY!hKuQT{9ZeyA(0iHAlGQpGv+?hD(9lo$5J5Z^eOT6 z0eTcpy?*Nku~NGv>09tJs(ebSlMP_2HJMd6u#{M>rtTN)+@O&%yeB5v_!MYCHI~Z7 z&CA>;sc4inhqOZ(ys+n@s~$;tietlV+qn~vDM~~=Px;>2p{4aAW}OQvv#;|*=iV$2 zMhSXs1qj-e4qeMiM-{PU zdmD%1oc)6`!Ia*zOp_%iYgP5Hij$Vxj_V5Fh-+n2>#I6>4w1buRcOiNaUgpaa#cge z(8XxxoC-~bc^xarHU`?TCoVAaUxB%=8i*1SUKvI1oDAa|{s5ko$tpp!m)+r&Jf@24 zy?ou@)|&#hWW${eyIbEbLeNv;F1bN)H=KJh%@$D@k?OF4pbk(t;7BcV5NzGH_#okT z{dI#Pw8Eal0*!04hsz9-p4~nJPQuSxom7N`Z+SJSx3&2bf^gSOMjnja#qNh58<4H9 zmz^U#>cKr5cN}phbLT5srY(;?8>QV*eMLqDFu?8G{#V_Pe~e_qTUbr&>F@8u@a;cq zX9BKvu(vleLf|%v)>;AexMIN>eEFb#QTM)fVqC8a8NIPPmD+Ii7@xkszEqBaGQCz> zr;xGW&f7_~i8B;sxDkM%s)}p;LF=Md=+YcR`LDeAL5{VbM+t!?yVH?FjciA#D~e{pD6-Y`H0nG8MYJM!BwDM< z^k*XEg`$aR+it$MFf%|r_d;zWevoVonY5}b+@ZPJV;4oEkGkW)d-QsQn0A4{IzPxG4HJ|I;iD78&?N(?6 zaHZV}^OqRtHu3IiMx+JhgvEu-yl zlxWV%M`43f5~%884XY@vTg%G*#`F#Z44#A8?VV6O!I+pSdT7@kt|1Eg)j~PbqaNiZ zn|^7WOILBW}=YQc-6oX98ye_^T3uFS|o?kidRI zK%w1|?d{jzpU^F)|Fp{j_&b5j*ym_-NzPg~zJg6!%9H@dM)lji+u!LvcZb<=E|3Cr zGg-h4(v(SQQ1js}jF0qevvUZ3P@y==l)}e_3P>GN#j29h;Z7KZxLeYHPWLGme`xXh zXgQqvd^*1K6}t6(IWO;gDmpeb^ppBH{yfqLVVa`e#{C~F_K-XdFYe6?8>cbd4Xs23 z-4Mc2M-6oS7j0A<{m3exueDWqM3LJ6~4xZQ60f{#Vm2a^rdMc1k|pykoK zQo@$|kFol5GQ`@!BPY)>da3APGH+?c+hsDHilI5kg9n#IdqgDsQ|IdGASU0;e^W)b z!Mk<;gsTSJg7n8NSdMIuN%VKYqVZr1Q5m>I7`W!0&7b$Bzc6-gpM%ksE~gZ0e)9#g zq%(|y3cfqJQxe8hZ+pWn1q`<+jMQd#!nC%k5dJF$GyGK>@k&RprYZ;2ho!mibo4B{KOpK;PW zPZj}jUihaG=}@ydlQpmhdq4o|_^Ui3^!ss6EzK`paO$ekEKH{|G3NVTC}v42SvC1G zgb4qX66UaH{0hJ#uru6=%H)NdbinIo{-?@3l$3xS$#iIY!D-UgP$d{687e1|F@jHIKkr5W6q z27Qu(gP*V}2eEFUK*i~*t)YHdF*(F5>s|%H>HS@2IK<7xL%F)!U9SGLvP@2!u4=4n z=4$L_$s3^Y%ky!8j|wYMwZB_7!qAorvv14eW>Kz1w@Fg)mhLOF| zU*QibFENy=*u_LXrka?<`{kpJCmpTb>hDUw|LvH*>Y;ANEcejkx2e>pGxVJMA7T!g z0_ER!f2tvaXNeKAad+45_xhvSAnHlQO6d%cwO z+!yP5Kc+`*6Q#(~mE_dmPn#e2-T}kKNm$VwuR)9=L zadXAyDISCMNO*`mnAjadG-|TssFWg5r+H!i+&guN@xPL=`dSA`Q4xI5BT)imPcEt+>W_GYQ{Wpa(w4A>s%stP$VHr^@Y8&y^IG zcf6FgFxIecDM6*tTC|q4*g%O5O!6Y1-E1UHE3|zD=?w%hiq7~mC;0irH<>B%n)g{=?u@4csfl%bwo zM7Ax{Tq^2zcYTd@dqzBzT46Nf7~o*t83U*?$wvAKZ)k95*t5B8xLovCYRjwVI<=}ytt;rU%1`{(@wi2j?V@EzVo za+M+zQV`mH7h!Pc1G|v&o<4N{vaWAcgg2khs|mEA6;xU<0})FAB+hkQ%PH++-L6pUgvLx7dnYZwV3Lc0N?wb0(ljM>sG z8O}mViC~yvtyB-^JJIk+?CcNH5{h1;OU7Cr*I{*m2W(1Sx}&sV!~OHDvvy~7OW@yD zq$occaIK$pkfCi~HrGmPiV?e%E@Shu(!czB41C3-8Pqh9|$YGSHbcM+uPO8I}2K2~Ax=()x;&2MR@LdaLx+yEC!#Pp8d! zqn{YWGaBB(3(U&l#o>aq53x{n$dFg+8*aNnZ66=FK*qCcN+vj3j4~&L@vu_p(j>!Q z%*lDb);R74!Nu?7>Zas8D+ktb<~qJ+_`GIKm(nl8@f>4vu#UPYwqK<<@K}LL+~A;A z9WT<7Acf)KOgqRNw`}bs+84ZcJ7ibpf5#MKFhGL=&z7}87Gm6Y?ksMsX)iE!O{+w& zPB7zWO6Vn8n1-g{01g8w*R!HC$5m{h9XS!tIJM`r&zEb-R-Q^ocl(A%5W!N~2M7IS zv!hPbehoTFQEO7rtcRQ`X=1PrPgDQ08}x`b=&{<(;-5QUymbpEV{se)lHUIkS z#O}u|NlA#VdjDJi1_IB$FLV5?H{1xB^sle#-AVwM`fT0lbhk>(IPZ17I`Pm$VUYF< z0XVhv;i&6-UohXim?9GlXD*ZjeSDDx>eXshn>d^H2Zx_kB!khq_;HKT0hq3`z^ej+ zZj*lpsGvs(_n`9$C^6MK@b=yJa-R_#38})yxk|Kp z*$qWxgbB;(y9GHBfAZ?K^rEZ}iSoZN;rRzm&S)hfXmM2mVeZ&`4*$GZ<}8*HL~#5$ z&ESVxdk9i#_&PMq_W)gKTu{In$>T#ZaGNx0_zayHU z+wM;Vo-ek}kFo`yj(!8=&4wVftQ6<-OpL|I@-Dw~?auKT*B9SCcL_cBZf>X#4miqz zR%yFk(5}nAelBHIIe+xPBMm@~2=~t?-}^T8JE%G5zuqmKGktoG@^hstyxgD`9^_&m zQRm*m2Ow&HdW+^%Twc)kynKw}{ZEb`4LT?{n+6GTn~l5vd<01j@nwH#Z(ppb4>aXr z`eoB?wgnSO3w8IY_?xQ@feeulU|Qzk-fb0wyD!SyKGf$p)jc7u)#WD7my{~GXlid4 z6@l-L`Z#m@@l7}1NLDda`x1)?xDC)jG`C0Z_YIJ zpIfuwMx!=wtX;E+oGa6v97(iLlw3wreq7dc(Kqa~7c-!?IG!#=&M$9KwFq|G`cWUl zZhXxmOhoPm#>W|0^MMYZru4_q4Ece@emwCpt)Ov5FXJ&J!dB0ZF1>$S_nQ z%%77cMK-r%1__UO^q!pcQfpA7G_1tf+Hdp3x6(OG7YRD9M&*uy4MC-#*oNako0+~` zf`g_kU!qI13#W{;_o5bWKFE4SM)-bds=Q7L@}-KsfTFtfbInws@Rg->dz3Cd{ihR4 z@6tRrZ221!-eEx*Pr1o}*0tq>WSt_YLxS9#IVaxWTPftYee$nytQ;Yd58>4~H8ykM z_Q=EbBchy@(fa{$7Br5vsGl-_Mf#8LE%DnPcXDm9JWj@?y+LT1_jB$Zk=3WH9iPB} zI(}kK@qALstb#k@e}J#+kBXh7S8mV`?-e^K{{%4^cA2{vv}O06+rYZQkL{ip0nclN zZSI~f;8IIbRYQ|c;(FiU3v|3gEEwT)%-Na*E5Y=cIu^{=Q0XuT*L4z9?&N!_TO~@A zsMep^AXD{neROXfjcioMQ5X}-ZUMYq!%wJ}yUDv^R-Q!o+BEYPXaO*5cHgS`y~zH|NWW~;Pij81ROL^hM zzEXaNm8knygv6CUcwZs5161^2cJZQtY3q%8Jpdounz2`v;!8*ODO332bjjBYw}o>- zx&RYrWOoo5^?19LLFkR!U$Ogjq?_V%$p1cPLg6FDU#$C$JSPRzZ*jMmB1HZ9{P`}F z)br{8?0xss|2FcD`*TiVZ0D0&h+GI5>!;lPu&>Wn6x|HbiJ-tGVAr<;d%%DzMJOWR7aC#P}M}>rju(RDx966A?0buP?PPHj7w`h-v?-t0R_s=iF?TB zKY|9(y(J(x@S!TxT1%t6t3#vF;AV-XI07o75R3x03Z;oI3-Bo){$(J)!5cst6L@zs zk}$WxP-u3oZ=h?Y>H?CdQ|o?RVb*qMd))@hZe=7FotjeizRIYpuc({e?B#Yo!}!~6 z0!URp6ouC(X_Lx z=5Te0;B?v)IqzPtb6r#FTcKohsbu+}oRcy}7}oihaLbQxH_H~`tj=mNHceN^3YtKH z0qa#u@M3u7m0swb9Rw4=;)B?3TLcbj5Jjq)UX-ENNb~-^i=ZR1=w!Y~Qm3@P=m7OR zKZSApE^KSi!lK5eY?4K4QN#41GZ{ZcrZ1Imqe>fSM&{qj-CSm8^gBgbQ3pU)(ALv70a&lvH1S#x0939IElE8BUn^_k zlWQp+2-Yal6x6C;txTpizIZWLcS7~YbUz0I<^X`j#LBAb@$g7$;2WH!UF6w3S$Gj< z+wC7Yv2rtNv%~2UcN0%LDZSV16xWWzaJ4L=jCgeHP(-P;oYcA3a~5@z8230~4ed-_ z5OL%v<|sY;blXTys!qc!v5|3xS;mQy@v$07hBRTtS|{nx`J*dglNG*+C8?dRal=?+ zT9m)TePFnhAaGL3(Q258fa^Q~Kk(aurnl^Lmjg&d4>-8T^%bJYQDuWnJkYq>tER`y zD%AAhp7`3pT@VyE5aPq6b4r3UpQad_=qic*KjBB!-vWn#fa9?V|JRGc=kC-UH7KPP z_;{q6pfJsqz&NAz+Z2=drLa2~ziSxPK1kOJXrFdr))OF{szExUe-#)Q^pcLhR922@ z0;1?9g!T-|Sj9|rq|e{Wa=lP?ySbR*AtT^?3n#ur@+?}r9!ydP-#_Va)?y#EhiT1r zuMm|mv-;om%;=Q4;;nKCPmDJCRYQVu9WTBZ$w;OJTq>Ta+p+qsikE}|#CI7hat=Ld z0@?|w+mqusl^+;s9iEr$w3QduYk??EVJp`Ok{lL$aw@lpPvoFkQ7icz#BV%WHsf*p z2*!x~ZmHNOYDBZiy)Ru~f7*%W8r$g7m}*SD59b^e3e8{&Lr)&Y^E2CZ{1Mc}jU#g- z64W}c+f1VJXkMr^lcO1x@i(`|!&fjNV4U~{e?@&A{oII!fQxDFy~NuU-&UW}2?kW3 zx|M`blf~#?fmB==!xSfrM#;0V0s|u&{ZHJ0BXmSyL3I^8N)cSr(be_)vjnQ04_dNg zJcEye3L{z;!H!cffQ)#4pkVQ1JW`LYpkhje`PwC}3m_GXQ;u@gy|xpXhYDs(LoTQ? zM4#A4@S?=Hkvf&!2uJ?vQY58vL}Pz;TzyT9vH+}RerSl+UbzhVE-;-*^sixPuk@Fc zLOqa+R;mhWkfWM`f)XuKQbt2o>br0_Zg^;O3&>VQ&v7;sZ$rDhX0vHdeJv|f2mQSh zD=x02G-3c`4rzRB9CNnkr_S=Nl4c>NbH`8IG^M~mk|IHgJ15pnsSadndwZd@ytGTM z?voE6?r)cqk0qbSKsfF8%DR+W7xWacQx}z^cvqUQ_qt&N-y&bfnro($_M-Hj%^dsU zbo%Ih@X$bA2!ekHXNV5t3AorPu4E%mx^{%0#oNr1_c_hM7Avq)S66_@>oz95vN*=-~fQW7QrfVRKrB%N%w zN5$tLF7?OGfT|OJZ1~*dbcvl`glcUV`=8INESO!>ZH|HW)$1*^lAEJCX-w)pFOGhE zbqelZH7gu4FTpolA2>B9hD+<>_ptb)>u*;x(e3Ih=gP{z{54m@p_+(*sr{wz)6f%!?KKNk+T>1n*_$%gINVyRq*cz; zP)%yP;5p!uxSUoCyyEdeK=SICdE{2V(0Q9xje})8RsxsHD%@KBLbM6 zLKA7wmLtt%gj{#|P6TY{wYql!|_Q`2X>MR>4ox2tbF9Kaytwf0|_*)h;q zX;Oydz@1|Yf~@qm4O5D#h=?goTwU8+o1zO4|C$qX(q5S6(OCMGXu(jH3KV^CSQ4rN z{Q)&ctJ60bfr$ToFb9@R3_u8H!e%iVii&Q$2dZ`6my55gMbl?~%l=NfSzkjFg_V&h z;TBoH=mNk%?{V-7-eg6=mHG6Cv09>Bfms^4?H>)yoa#LCl;1w=*t!30oe41L_HSGI z-=2Oox^VIR(*6{pk>c?4o{yUY%Cz#>jBgGeqt}0HqG*e9MXiEfx3J{Z`!I9Vox`=_ zfX>!dH159=3ju9-U7=c6l&dMo%YHGO>;Mz;QREm3nj%!6AS_!QpR1Az(jj4^^=_Zu z_HJ9*l_|d8q#O7vNAOkP4W1)MC}7vv=`d3L_I-Mvb9DR4uM`_Z`9#w9Wkdz^ZkJ{u zeX>KiYe`-<@t3r;?j=_uTzVxV5_6*$2wDpbQL_u>52HJ71f9=HB^1?*lMf_8>o7K7 z|7DX|+nQ4Jwt>6ojUOy%pk_HjwUq4!fq7rFlznKG5x_jlvi8$AhcPO%6iAIlRedel z^ck!XgcBZW>&EQXyn1@dUY|(LOTwvIBOZ61aV7x z#g&Lbr~Ca#WT}6>qj3m^mN8nAC09u+=iHpqHJ!k;F-eK_7wOdflT(o3`*431870Kj#osC_=%kbNRq-yLyFkzA343ezNCtVPiTOpRE?w=P0Zy)v)h1miIvcM5FMZF`&PV;`^HYq+JD7^g%ewc<;m(J@ zzZ#ag+7pk?_~c0=Xz6YSTF;;%a+lR2o*jolx>#D=jP^g|r_F^d)@R#lf^fTl&EA3Q zi_ckJ9jI2LHEcI;ha3+7Q_&2e=V*}}QfIa%)dXqPfx>QGm=@ypV~6P$Dv_DxHQ;~y zKX^@f=D{~)>nM9j74L_u#&O}Ax=gR6lP?1bxLe~+ZA8LTs%SJo{S$VO8X`o*^l1c^ zzL9ZoaV$SJ%lUoxy zaJRg&&AQV-cQa3KdHdXX#65a2e~}LoNu_*9nZ`V{L7cOL0d66#WKcTEv(5RidM(-4 zLFkSKx62)v{dVjBRIKa!nw~vK-=?dy{XF1)80&wT!RF+wiihXhu82PAXkI;N2b(-} zCE~9;UW3P-o;R9+<;YO{rVb~0?gaq_Ubrl-|8J-44fTWYEK=WUEj{x{?MM!JMw$hU zC0T{a?kB;+$)OslzIC%+nALTf-KoG9dCN`*HEq(AW?+t6ZcyB2gmxj)`j0n(*E>tg z%ZRbASmM<5GAv;D<(VHUF&tqR8t`25Fm(9+r8si&mVEATyyatd*|&$^p0ERZ9^FO)tY9DgCWRvb9a> zUwcOE_S-0mT9_q>Q;bi%1pw%MKAc4P93$@}v!}>QU5D#oq7Xf2=|BCvh3s@X+l#B4 zjoHbF`$5^-3Sgomt$`GamWy*~-EAw3tl+)TxOV2xl8*1CfOwzswYjo~CHQwn4T9AB zW}e^8Xi8ORXJ)cZvC_xcNVhCEd`T@jSKC(nDdhbLMzNN>?QW!B^w?bAd1>|e^6YsX zJuvwN;w$VgU8a;D9gxPDVVdC)aiVrj_@p5iU?CXF!Qd6S$zUiL>R(`;;Lsp@18@yk zaDw_$&D&qo{w^K*7Cmk!c7LXLb~IEpl(RL|m)Ez?Tz73GdEOsQdE0OEAHU5U^Y&fU z&4qB6(4xvJ+VC;`(z)fo3Xn1K9LP{Arz72QH>eC?4Nl(3C%iciJx*S7=KD5K1%sm#y$z^d5hco1G$!|UcWkd zr_GjJ2Tt&fxoq{1Gu;#3t8pvMI)5EtX}&XgfdIgqz+5DsnSK zu30O%Tq5*3KF`^AZ8*TcjXcD}(#%YmdApcF*DE@1t@1Pspd@~jr)LQyVPEnQvLb{y zdm-aN@0Jq~`oUk}_DP2UKV%v3chqz~()EyKmljgwnD{=z!!kTfWihx~NWhYWOX;yu z1to)*pN|1+;6z?mdeOxs!b_@_H*Qo(VolsU;Y&n9$nQ?TD9zCR5@L2E!Y4ngUQClD zhgY^DxSC^iuk8H3YPk9WCKd$H)eg(4;j6I(8D=``pr|{ahHrh2x~|_lT9u$~LDREq zdN==)rVN>?>zk+8e$d0Xcg-x&y*fTEXMRku4q2gU6MUrCgWD9~u@& z&s++~>x1mv=nhbFUa+@M~x|P>gLH zQ)%0Ai*C$h+1`m@2B`}4?1YABawHs{$r#xFh^(F}AQ_Ji_@BPV;&IVVV?IUqFBvlG zc<$vRpa!(!$-sbP_5wnl)DxT5VzY+|#%#z6)+9k}yz$uR1LD64YgX}N#u)=5*M9Fm zU;2pOuYc@Mk^T5xRp4j@S&-M&6+! z=_-rDBKy4?P4wYIL>_jf*SC_aOB|rbssE>wBuVH{wk9=aL`vsqSwZ7Fr`BA>pyy@ZbpB;Ly><$kmXIcPhz)ycHi%+ zcgccdP+7~MgGB1_$fo>?ba&+!C}slZOaQBkPGvN-TaJprNBlBfhH*sGBHd(H$<=_b z8uLJQz+0e@cFo8=p1+=ypuLdAq#z=8%%o^#se*Q{9<9-LQg~9V{USPC7?(ual~J1j z*9NASxd+27DLplloMF~RC)$xZb#0#=&_$f1@8&m3+C-A5^K#EBu2zsFE~@Kllws;g zsEjF5S5sB9md|vV=lA(!{rTE|INWe0qJnU~eY8(__q7cqJlb~NIn}?d;l+M;;c&B5 z9i$C7L^no}l?`4gU~_Z)+HUDF{yFJem$NFbn;efYfQA-g-SuG*RK9sl>RmhL^yYO# zJ@zIM>2kOg8EsB2;cV}}$c)w|c{L)#zs)%tR`m`?>VYpPn>aM}$nt^QbF(yD@F51L zI@C5~S&lFPvg)trl$llZC8Xapc?pp~62|-hLMv zdPAg?%u(fCz+-dMb7f^|$llkVL0m~Q=`rXSm|ljd<&Tmo@+t|pXx`frQZw1VL(Bj< zDr>onG+H?Du_hKK93~Q`v^Y0pb6{3XMj3W~!Qi}dTdA%cibOfAzmZJasq1N)sp z5op))nsU4Q4IGHy^CS~30Y7vKW_@bcO_$~y%h&lE-z(Os-D4ilq`6V$#(EmFZR*@f8{=(M+%>{|p z+aGSyjwOfIgS@I(%bA_N0z??QuS3p>0k`Sr(H|_#WutRN#pTda^{OeIxr&yJ;dybv2!-_}n$eEqRgyU^M z5HZRrn{J(OnA(}`if8)v>o~n+N6CO4M^~{N)i@?-IC|%!rDw}y(LXcWKUCBa{9!&F zhkntd89sU3j{@}0mB9~s4l=9=wDXXAXy1|Dk7k&**d!036Oc|c@toDX!GWubX;=GU zDvLz#g5FIbClCJaND@CNe-(IYzs=*ESR0>w1f zzoLA7-Et!(c@YsKp(tJf${G$K$Uc47?8}Em)n#bZum^U=%V3KK6z${QA+}k1@ZM~? z`(ikNWqXn=_I_q#E5tm?#VEq3Wn3Qdu8M>R&yK*r3?QlO4|C=I!9ajKIcuyZO_S!)pfJ zkin+;xjrLm5LCi1iM7T><}H;YQsIiz8OTVbz_v~fMc<4 zNrBx%Y_v3j#Lv)J+u1x|s3THpL_{$~h}K(+urW|y?wc)|r$~S;yW6yoZXp%+vh*t~ zbvQxV2J-yoB!rab@x&}jdCp}&=^h!NcggTuGoXY5+Qq_3ak^!r23Or|Q7$CI&Y)l7 zN~9!gkso2#E#MHZ$yLJa*~{5Ka-OTB!ceOEfej)$#u4n3d2DhQ6>GPM=b3s~D9g|G zCbKv7G-89sxcKAEwdDt#YDhj@s}5DfyH*Jr2sp)|Z#LWMCF_Oigsbl1;Hfu}Xu9mk z^D`Mo!N(cJ=LsP&oo?V~@sEz@2%nzRB*>=-!$A9w@;OVf;OSbHu3e5%wh06@CQ@!l zGtyfxz8&yp%O*m>ulnRh#rJYpU_5$)IIVv(ZN{lWL~rx=S-ys)j3w@$tV(OvqV*NF z{!Oo(p{ftG*dQ)7->AEjhkOVMieXlN>-KCY~s=PKPsh=g8z>GgxS0EnLj~8mY)-lbQ7dl*YInK8RbdPU`Tu& zNE(?^5?1OaYx%Ds*T4aOEr(5g>MT~!~A5sRUr*k3iqgbHXqv1OeZ2m3*} z77S-f_-YA@volTOsWH+z07EKD4Uea-X#Hk3_4rVPrW{hGgFzLCw(L(G*$uG#yW6Rv zD|AFhn2Mp1YG#|c-P+htbBJhch1i_8H90&2-VB^YgKAl@^RZaIM~_K#=QbM4YV!ki zEg#GXK`8fk6 zU6Wu;Bw21)l)Q%7yl@=+Me-?I;K;Dx67iTDzvRTenAR@ZnbF^6ehRV1{lptFJ`995 zDsX;lyUb1z&GX<9**sN?l>+n-MrXsXp=l9HCPT=aHmDdD`Dkr2Z~4{0W=WSx0xN+f z$kL!{W8e1Mi-R#ymaUE4G7YM+vw9bT2G8|gIed^gm|g@goq(Uka!%^zWB{uDWxHs& zVIR!e%7srp@bpWY4+wMXdm0>q|E+?d8yU^i@tm+J>kSfxYMuD@xX%dTMJqSE#uC(t zIbyII8~kl*Q0uMN72_)R9ePs-Rb_3u(nBafCUTzHK`Ss4ZjaQVtB&Y6AgcXxGP)1b zom?f4#hLPGioh*X z3n|)|TL^z%hln`z)6X#V6U&EmKGKOgV zfJjCvEMVcv%0zvxr8wjD`|?Uq+#{m!rrUDF(6bN9t4DgTRfM?kp;L%*U1Mv*Ull@y zMH0EY$FS=up`*hgcd8NSNNTIYzh@19)T7yiTbI*3LhsrDvb8uKh>ptDN9Jl~(EIHR zFXK-o)cdee3FB|VNoh(nFBoAF>U*%9>aTU1hzxPHC|EBitFvLbM6^ckfAD;_jPuX+ z(7XEmncL#FJMDPBlV9wQ)C%gkk;BEu4?pXIGklOR#6naEEC( zBP2Gz)@JX$j=Y2Q!LO{p@5YhpN>tW@oGqY^ zH+-q9=Bps3tBt#$Y;{M)zkFCNYif|&)@|QhMSe3z)eyddkFVYPM-E=^;uXNuyhePEZ)rNdxM zHL7DZIR*8wAtk2w-DYv3jHPJBk-CAK>bK8LvdEHUW8g--LJ`2nKwpi2qlEo2SZrrP zufUM{+UI8RP}hqFKT$hTm;(K`bmEsUQxTmDw_ZL_R;#l4--Lz)>ss4pgJI+dTLUQQ z{h~i|$<0uTl4w!q_o(sFHJGBU8J1j*8S#7HgblZhq2E|x(~~=;nYNKI>CzxN>(fM! zA$oPC-y9(^tdKTkWMn<|?^R1i-7V^> zNSpe#rCz@UIQ%RVoyPbDx|_t;7T4r3GBrr#UwsSnK+{nj@*B-x>XB1w5)bGp-f$ql zOq|?Gzf6k+7-6$csbZ<(Wk)C)r9~Wt3?I|rI}^nOfY5{TXC5O~1s@NE3h(adbG`vF z%5};XP&=5sdAWL$COl|RS8{Ukx85Oq$R0u9ST{urC97SAXb7H=V5btOjbhCvclSq} zQQkM-rxb45tjXEwGbYMkaCBx742aPO54;w>mO*5EO4>TQMIZx@vJLf*a0fuYwV{(o zCYjqGc*GVo3Ug_j?_(xh@l(bHqjs778-_1n2+P`4(nB`t(vgeajel#uRVB`3M?IZF z!cxL6o#H2ujjCT)=Y%cQ;_B+a3Ynw>AvZZZT{9odZ8*1LVNE}HoyQn1vv$Sl2W$H* za_8C%+wZ2o!&ik`K2N6)%C9U!+UxZ8_O__%7XT<|G30hN0uH!ooUV?Bdk9f!dK^EF zafI?>`f|ZPi#$o3)Oo!|!QJ%|MNZ!a9Y@115$i`A>PJIxE*|}$R&eXeM(+@mqAz+R za}QE**3E0cvMr^WccGD5%m>RwYx84l8=llaLs!E8vGvV=m3@EP*~ZCsoy^I$Yr@oI zyC%EIwr%5N+mk&RlPA~Ydd}zjx_^BBguVA#Yp;uMa$0OdA1v>I@1u|?3Dc(~ih@dd zOdFb$cqgOBj&sF&cPYQjteuurnpVpuPCewvct0ngzUsupPRFV9Cl=NoGRu!>+>i7< zNHwls;GmC4{*9Z7nIE4*5Q$%`z1lJ{j*J<=?fm%nwefdNb4#m&;SL_#C8uKpn?rShfpO zX>jQmsZbeX1FI~urA&eqI+9{ycW*aOE%U!7l{7&d)RK@6y*>9m*N$d=NGywerOy)e zp#_B~2e-?U!1?pZmWBMbd6NRt3{Ot*j=34_eV-s-v^!MPH%r8@nlblO&S`c8T{EP@G9E5|OJ}naL>OjlJu87u_JEE4!nEFycwqbSIKE4)BEn7IvcCl=ew6}WrIJfmE zQd}5G(fP8%EiZ}H?CKGjG*8|0Z)DZV(?+&+x$%9FG2*=~`yEd%Xuk^))A}7o{z+7j za>KD68&@o$TqkFcBLZc#VeOv)-_hJR$t0)Tqnmwyc`44Mht(-@0)oI?f4ZI3i-%du zuka7Rh})-wCY{KSRgurs|6WhW6C;k2=<&c7$7-3bdaI3{_c|dal=ZrCf;vk%o7^+8 zlkvsBoVKR+QEm`KzKnAepN1J_@Axl&=cw~WvT2hn`oUNa72rL_ac#e3_dWb z1RH@n8%87zKo=IX2(3Ojhm>@wDUUfFsr!$vrj{=G*FRpB8OgBmOovH2qRc$e5gG_V zzt5Xo!5$(Q61`eYoq~*$ABH~BPzSx1+j;>3AbV$|hRdtbQ{=>Pu4( zIi_ONHFx7Uvf|5ZC?71nm+9l$2g^3SPF0B zAen!rR{Hw+M^`wPgX6m>$Tb`KD*Z6LrV)mrV5%r0dNben3zCjZ;(Y~Ln+jz!nu8&m z15PyK$G(U^D-7La5QL7-d@IEf&bjd(KU%|7;neuk;XL@06^>r`Q0SFVvdUtZn+_m( z%scE$8Z&PhtYPj&1AAmqTn+j`GCCzg4+N#LGg!lX$Le1u%aQk81ji}K##-Kf^P?&Y zUQI^VEWhsy`!ASzVYqPW7kn=7{53?b!m2=$LQdHGi`u7q=AH+iVEj)wx_1$(u69+h ztZs5^SLYbJv{xPh{1iEeXqBn3%>;JgD1D;UGK@p*qZ`d0rpR_-t+>#fH6pSKgGjHm zJB|?n4#H?U`tylq3b?nohzgmaXDC=bCe?&xFHbD00!&?=o%~g<@ja?C#^SDa|Ksx^=s# zuZaWypzJ1oY!r!uCl65{1s!77m5p;$AN>2)MBfGlkU^ktA7R-vD60)6A0 z9A*&ZF^zD>HVpQM{2j`ShCIEQQ36t+GEoY}-|FjD-1Xf%+L*1-QYhquKu8ERlWVLK zH@fF+=pI=2A)H3739s;3M|JXfXsC!v&Fieo1B~;aD9|**JrcF|kS9=RrfZl-;pO)T zbG*(x9;m$#{Qjx-F*Xq$D`39d&xFIu>+S%r$U5Gg+K%%>FQTUP81g1htbTLJLEB4E zjWJD%ngZZr>71^0S*u`@|9iE}>M}MnkM0}45_MUI$}?BvqkHg(18vvqoBmERq4yxz ztNwTPN>(D)5GP7vZCd*@SvGQo!<>QjXZwKv;n(`ix1;1~$`}}7@Suns%v-BnWgmcU z_y|clt1?Ii5vYu$pYLGFv*+zMWLacg9^?xkA$WdOkL?z}x&2A)TArD|OFd6;Z^lMc znIiH#tGEY>5|zLoTtXk|-AvO>TAG`RQCe($8|!6#`9MIJNs_jnhZG9u-%9sI)8#>z zWb3^G6urM0NRZzIAr}EiGMD??l(9c^gi;f50g=9;nJn!nu^E3#SyUpr z6`&>F{+tKCbj%l2h*U1~`;Mtxfm-y@jXdF{hFIhe)?AN0Gj_mB!uru?0MBYJ)^fgvET z*^#YX(iOaOM|*dKP-<3ax6iQX(1QL{Dt$VmEYG;Z`J~qrKLcXOMA&-)y5v5MGqVHm zqQIP13k>XLKidD@A!U^xEQv3l-UErJOBO|x_KT0lbjraFF#hUAf^oZ+t z9S?x$aKDYajw!U-yK^(WmeGI3q^0cDV#CN-2saweu=aV)5{xbgG!f+vo8CW8IhaNC z%g_GOA3omyIiCGCtM$U4)`pAW?qpAD4C&~gZ>yi~3;VO{TfOciRCjLEAa_{mhULO4 zyk6`BgmWY8N1V+YGq(z(B(G@_KFlFf!f(v0fk(7o5!sL7S6JL?WHj>olr?_ZPLHIm zCx;KIn!0s?gU&e= z>7hf^Ly1&5jQrGat0|XzWxy|ppfO&&(^~;NqFDn6hwcyM1GP_Qo<(3(dK6v@3)Lo@ zKQT~NX_h=1x*a6$nfvee{tv3qvqDfR#$x(+T2P%y!EHxiXfMA>se+)9SPf+e)$o{q zJl53i!2W6^-^l8B0Em*6W_|qHZ+MXN*ZJOl^m^;vTid>iYHHuE z%}@(!roRXRu4FQhj-^^TRq%jpyuGu;ycot$r_I8!r|EK;phntyuTJ%l8eCFI9)s4;$H6a{=Z_DZ1( zchBxGQJ?p|lJ(ND*Nyl_ZhuCjS7_2tdV|pJcpgURSH1$*z4f65S3%=`oh0>FqgJ74 zrP+@RILu%vy^s$M1Ew0tCLAqpghLa#z=I6gZf)`<)=ZSXAMH_3rw0wn8*-(uO;`52 zu^N)tI%4gmaU=thWn`@~RwVjSKTRYe4?WLaO|$1?9c0lL{FxSYI(cMVzWqxCeQbjt5S17dc%~s znee_nk~`693>;<({bI0b|4)0KCsN7GEG@HN5ubjHbbEQ5f?9_B5>QvFUz+NM~f&Fw3P5c3UnkV`?=opFE9>Y6!zlsJft zMin=TG;mL3lX+E#o;-5lI}p^le8#n@iz(r$*$vgXc%bYs$k;heVnCah3N9;J{oMBg z%2ev44W47JAH-n|a^E10UI_BP2jE z^N3P6q4B(y1r^|fnH%KNy+;`!odRn{$F&oD>lIu6gw3FJ=%4zU7Rn9~ls-}+CW8U} z^KU<=f?O0MB-UFFeO;Vg?0^3q<_;#!qM(BwFs2b|vbIdv2(0(_`D(Wf3&ALT(|}G_ zr+I~&?y(fu=sP3~C`a_Zo`+uG9&tTd@cQQ8K)W*8Giu0zG%ti5o7bnzH8(N<1nSZ zfhq9b$WM@=`is6T-lF;X{_G{qlRwvd6xH!!@((LFz|)5_YnwOf?yC^}1lDF)SM61m zDt%n|9_;g}n4r9XTa03F1us9}ez_#|jL8M0()UctS}u7C?UF+uh31Y%hLvSSN~-f$ z)Sdy+rd3NxdR7sAKgu-3d$%tzf2R8sPcO$Sc$-_wx^X zDW#k<;y<|VdvL)YZP%~C*YoZbp!^iRW+Z`B{Qowv7+c1Ug_tv^{@-6UeYro`(KfSV z0H%KIM0tYJ)4a*J=NQMgc66}w!I#(sL59?kP{!J1fE>ZO9;Y7r7>OAe9q&CY-#K<8 zZY-hSiS5?;9XMA^`3aujMG$^)VVqgnB+1m`MjRBCHo<#Rs$V9$!6fhp&C{xY-3zc0 ztLDV1Sl|64S1`Ey1BceXYh=%TZbbXo>iGky^k5eGRK5fF&M`Bv-!RentyEgsf9KrL zH+K8|_w2mLd*6rlAA;LSTeNry7}_LBV~$3RDqxzMOot1@OPE=S2%RCHy(#1nJratQ8+Rh%YR8gkQ&4nBo5> zE}PZjMz*g$*U1(}7e=uQ!&dW}4gD4p_A*#E+eK-cg0SYFl4o!i%Nk0Qr0nM_RI-Q_0 z5jArQoG1;rFdDNd5)k3|STNjhd|r4>`9Lbh*D4<_u2XELQ?Za0N;5aziPPHh!Ekyh zAPW`>g8?a*w&8RO5kcIZ8ie*C%a{m~l;L$&J*j0l-$igz@&mTEAZ?agH{XQqAdy;S zOXT`oF2pQp@^8uul^!pf`>rQ6bJ)+rPxgr@SchgxeR$QlGWOFd`(NO+w4LAgaOxoj zIr=60OoFCmF=R54vfo6UC5z__`OFK0xl-O2$h=SOkt?LNbN9=-!u}*eANTARHJ3?a zSB1-C)%TAdmY)o`cT(Q2ub822LtI>>1*?5;w6MJHhdJ+s_K2YUb| zeS+j$q4%j@`WA3VRR+KpJQYr;0 z^%RBHRk0x(0P9zQv)hymKj%PDVMnoAZrpx%a6+snh;bVN;%21|;8p^;LNX?C(O&S6 zdk~6(lNIcRgsm+Tp>!t__Q@c9tJir!Lx1jFq-09;jWA*EEM=Qiz z?Q%QWfqjELFD2UOswCS;JlyzRwGTdc>R_opM_o$yEV_H(< z7YU6a*M}e4Wkc_Zb1-^hb--UF84DqQ0k3Ow_uiYA9K!g^Qr>$Q`~v9 zATxwqii2iqYL-r3y%pwX%R;a;4mW?I{xSWvnv6eTn_Y&V#L6}wy45a=3;r4zoQurJ z7Ud8%oR<@_)Ip`d5M*vq+ive5H7u4HMexfEL3jmY(`k{TC&EhMp=b7krF5LpCX86-f zMo^vQm^x^@wPWi3%xCqaw!T++b*E#{k4f_RCW(7cY2AoRL|2uSJ9$b&SkDBLxWcjc zz+Mr+kyTr=>S4ja!?SJ4Myelt7Q6trh|i#Lry(gKsncbQf(q`wHiE9!u%$ z>E*$u@2O|E27V?oRqsFRLcHifP-k*RVYpEb3B&UDz&*m>Qa(`w^UA%0lptE&5D(8F zosmEr5e!V-k4<+t4IVrXK{%4-s&EhYckSRqQU_yzB4J4xfV4$&c|5w9&C6wW0aT0Zwb-5e5d$@2B?|6+O3rifD6vyyOvY zJNC#Q6<5E+0ol><8^E!+b7%bo^A2_Tc|6vL2>V-{iE1#k+Pw@d1iVtd%ZX`$&lyhhmfabWHcx?_;yTcwwCa z@v?r@y8#krVM`i~GT(>YIEYI}uo5Zj#P@{El$|@`_~XEpklO&4F511X_O8b>>(C1) zs%dx_^>@RvlZ63=#G(Wj2dAus?Us5J?8@T1!%OrPswESXYaVZ zeKo*}oW_hP9{J&qxmh}^)eGQ=T_g`=O*KckzG% z+deBdDFax%fG~YUh#pN<4As(oy0|s1k$^%8)a0V1XYy&D*$^~Q>l_GpJl7SRzLVFn zDjp&H63dSnvOY@|OwYt5jUYK}RWiij;!Oh9X!I?%v+>#)tWQs~gOD{`nWTMK1in7C zOV{u-Hq5nV-*X*sEhKzpePAOIbQ!uK4~k1AGDP?TrlTyrOLz7MyaR+fo`uq%p55x@ z&YlWW$TlUM4PIxJ=69}{Wk4pW7)M73tXPj#U>8sDKb{Pc?3a-fQj2zGZtj;zZ^o&#!|wq&s|cX z?)QjLvmhvJs_tXRZzhSayDJh^npO8t2VUM+L|0L;IvNE|Zp)|B-89kq#fk1K?i1rC zY;2eq9u_^@k`uB~D|kGTQ(n*pAbd*=ug=$GA9dxiqd%{q0myQ_<=_2fv>)Tz`G^!X zfm%9d8;AJDV&AiOc8|4gQJ1&c;9dC)pbeW|V9>c%jQOiSI+gu@WO}B7B|0U;Jd61* z$zW4Rg76EHMX0zPR1V=TUlj$|kM;UbX)vfS{Ex5y0&R}Edn=F+oWGC;JG3-)O|%@t zqMK7#q9KHA1Vj9Mv6!QnoU20=LtVuzW^*?a+K}B%$sXqCVy!!OL37#uoUu4}uApxP ztKHIBhXV`EL!yTGt#ik4)A!F5$bQb_@`itobv%M#YtloRCNH0KhmG`7{yF^pw4+Yx_V~LB{UC|Ofx_I;nL}2)tq}oHJ?V@Lj4tSV&Od%OISlk(6S?LD|Lr0CPZK}JX$BFHo)m9FCNWVEIWZM_ z;g3_I1a+lVj+>&I{g|S?H9L0-BV;eZ=*1vEbV*gu#+0iR;rdHC*4s|Rb=t5^Ib=p> zL`eqVGbc*Qg%7yvP-0Px7coDQ#dr$2K%s%biL@r=<6Lrg~{>+?pY zz32#(e|6yhi$x~g^p-UI&@(KVP{4MXC66$*b58-sjuUBFR1!xVN=*tSZ=8NERK}zT zfYx+m%YNYej-5w{UOctsH2Jb65+$}kGaSP&=)V4Krx46{Nhc>(364VW=O&IACFB4@ zVlA+vO$3-lbPDGzGUP|Fu0+JM6|#MIP!bj`%M&vx0BO1Z#Gv={P*x4HH{@1#dRQLJ z_EwOq8)?;PAhF|vozOgX`efL=X`eqn|I~|=qJQ2VVGn$p)UN!tp8T|BheeRVbboR% z=HhrB3l~WOml0B#I{9`w*0s8q6}wa2G~vB|MGyXMb^js%g1Pw<^Pmb*pIT(nh0f$4 ztQUK??!4(_JZDPP>5D8}Qz9JQ8`kHN!tR;C0oz>7qHMjV64w+o`^Yo7)-KvJ4ZS5; zu*qy#h3`X1?RQxhO;L7vJ>fi$`%+_GXv>@IvBvxp88BeM0jfcOFNV#Oh97S1h*TWt zCJ)2yM~1&_2g6?2GB}^c7=8RCyIVTgINc^A5|~Z4;th6yq^!-|n@J<##9oVnI3w}h zwX4w3g%@2TLF%iqhK-Gd+oO-(8tm4h zwdX7&$b8O4jt~nxvq=?dT({#AWS1A5`dJL05<$%X zh?zf_1(^NZ!n?lli;8>{>d3$8;aatEgh!Z_?~tz4k-kJ%$U)W&`U3Yk>$Fsi7)VND_?*f5&$FM9mRO!P$)hZ8`abJ0a6XKEd z6P*IB?Ktd12=G*!SI*zU2=}#6RaVflvzv}xfmAENiU_mXC~2nko$JO*3TwmJx?w)j zxb>FWA{H|oX_dQNPKM?Z!e!GM(7srqJ0s)XnNHANar_kK!w;iIKf~)j6c`~+P|i{R zJf*o$B>1&z^4)*^v2&~>oehCmX24mD3<9&$Hz3z{=mB3K;wys6YC$wHDUt34K&~GK z65<9yTRZ~bHB+A(zV_=xSzZ3tOeci=U+dIPzF0k;0!h-Z{-hiWV?uOVYK4oF&4%HD zZ%*5=16{5az8D1z1Gli}szIw$}JOy#-pCANSuK&0IJ%#~j{;1YQ2l|4RX8crWQ z99{qY&ufY72Ga(YpLrJ8g@I|gQ7u5JNXM!)j}bj1Lnczxb+vQ2N(IzXxIa?q88E38 z=MN47+aTY!>pvj3J_hI->$OD!f;SEDw%$JM{dWYnz>~Ig;Eh3wBMZ&9PN7L4YFsZp z9U5)2+J&_#wpiu(Ye!Jmgj&x`{}^$K&Pu;$;Fv6~us<^MmLAsQ-ke3mUlxJXTd9}3 z&l|n{)*LyDlXZbr>xVtYi@K}ZcU)XG!gaL8v4HnVojANp;5@*PXn1LV9mY$9sk>+6 zpd{0lvw2{3VydsNKO?_h=CQ6~|8gQY#{|t# zU{p=WdpQsASmIozJ7;x;IJ+2#g}LPGfg-@zqvUZmSIpDaQMAM+Sl3^pZQGIJxn#Q< zQK0n2c}L$@%^ihYpCA!wl`iCCD)_`Wwl>C0GGY(*UV+FeF^2f!Rz@tH<$j_v>=Nx1 zTNdy{Opzg0@eJ!1jnz69Of6K(|>uCp9_FS&dvg{04j=KO} zvV+Z@0jXE17=C+>{@{5PeB0ne=m*+6-sfw8;k}5=hytMWMKYArFx~R zlYO@T!RJ4{dzBB5PuOQ^D6>{VhFsZ+Kd7653UB^b1=*niVs(j&_ISL1^gH|Wdu61f z z)-p9V+s7*WVM9fs*;cc?ldz`e$;DOtzvPQ{0mM{^al8ezyEC?Jxtr*e@=pO@#NkJQ z3EurZxVBe!Sn>_h;nVTWhNT1v_+Vj+fHwpaLb726b$)nO>*U&~57@{$yD&((OR&D^ z4efdw(qm9ntR+C7z_qPlSmrkr0`ddSd!ob9Y=a(CKFD+ghTFEo;&* z2RhLR;ocTS?VX-Aoy9(K-PQGA)Xt|PJ5wcequ7vJjtB|VI(UZM@ww|^@@JWMH}(z{ zC#VRA3E^%6n(SZF%JC9OWMZCqJoxFMt5i4{#3{$^@q3-0D?WxP$M5664)Y6C!MGod zUGHm;*^^S5jSP~o>yvCzVw44SFsX^i9KIHz?!Qh*IcyUFXBYGi$s8!f5|W(Fg5Hlq zV&NPSRctqfNR7~<^8QFOrZLLf5S#iUR37|TO_tmb@-944t3qWD#NUUyM$O5kq`_Xp zxK%7Y6-CG#lIKhdI?M;HQ2 z@c=vhA^CP_oQ}~8ewKG#Vz)HS@a^HCer!ryG4ZMoDKPvOpU0wp1S{|-#zm6VnH-0V zwK4)HFg(W|vW^^cYo(Zxdjxri3EOrhTct)q%l@6@Bt(+O#-h6pH@#-9j#P~2%>;eH z{ei&YV2~M=MC9$Q5LrL$e6;Hn*!EMd=}AqQoWAlKXT^e|>d>62hacDmBwW1m0sNEYfa;dn(9YfNtr375AidkME|$Ru%Imxyu`!}hS8z0MM#^7=$j&H z-2EH7Y8Dc0`$_OP-8KZtou2+C=~Dod7uNpb>yoH&LgO__$xAO#8Z>QIq9~!tUvzr| zjK(-$?WFQT@1|wi|GB6$ru#U38gHjxL8@u;zA+GW15ut(cVIh z*_otp^oO7F2B+o+gkRZ!S&H2DJLu;-)FnX*W>&|%7u3k2$@@T=uS_1rqr!PoPri3j zNyoAO>i;eY-A)A~qD?_5n7nTmgw4&ZVa8767s#LSe54}Ly0#j}U0uXUnRX~iQ#{EC z>g~n@Qsuc8h?$%s2o#IC`+`Bv&{bT6>R=DJcRW>Q(fl`ps0f?XsP<=yJgkXWHc!^C zk*#D7jk1gp(&Ddrig>5{#}*T2U-$@AX(63c%jS`XV!7W@#igA5Gwua#2JhH*0f_PB zpBZiEe^eXJV6~|z@8RftU8b|gc1H9g-L=J2W&u|FxAYxvGowtF5)^tkn6c_$+hLJN zI7E(@TNOy^6XYHcJjxoMI8Y(z%0;eP-yA`9X&JQs&I-N}buWo(k(U+r{~DX8+&Y52 z?2ljy`HEd{oDE5eVFbX=z24r2NW~=jiHAQ=yUAOe9EP0A<0%T)?LN#fog;~Zz&La*M0QbR7^-A*(tomg@!<|q)1sCOoOdiM}|72wHdVmkpkz8%lHk2GPv-2|%496PmwPb+^Z^u6l^<9X-`v z5=_uH@0~vj&IY_BbX9SYq-LE7+_-EOlXE`@d!{y3sW(osN~@s%G@YFMT%-@3Rnud3 zit&sn)yxEVTEn-~X}?;DJ~U!AoK>p+E_S7IXeS`hx%1Q5k9(#zIRT5Ckns-jv?xAU z@lkfY6#^&}NeX`|rcBJuXvq&dThrauM#O0`DFhSzwocuMzYNNNXEO0AvGg5$NjFkd zP9x0s8FuhSieu=_F4@ZzyVQ$Hi7JuQ^fm&Ip_(CWaDVb7FCi>qU(^V>(}H7!7t&PT+^$10g5*}Oyn7>WcKq* zsW)`1=Yr5dHvC6dR(t0eR09ooXA80o)yAdyg@3xZS0$%r{&6TiSmLG zY#Y-Hx*i*F_U6@KQ?{L;Mh?6a`X4548(f~4uKsCW1Ost6Yw&scWko%*tU|GsuG3hh zTUQWrweYel7vgbt?pp?UX-CnhLO+0~pkX$g%D<5nsoM}`s28}1_X_C*9AFLSrtu}&FS$Z+)E7=4b zNQh%|zsm;T$j8#oYPqj_8g6VJs^EC%7P5?6*R)u#c;j`>050GQ9uwnOV|F!NVD|GB zA-~-8{d^wnW|e$nII^0@qcoJ@-Q^|JkDj}KxEal|R0B((s`P{f8E+LMqOIb2_nSe) zs}DBJ8nawAd?NU42>W95#sxC`zQ|z5D+Wt)oWV_oStFRlwh~iXvKR;!b9+u{f= zV=;%-LBuwz9C#bEOFB$>b;g{s8>ZssuH=I02l`lLcq@}liB40i2m?ru<6WDG&=$Q> zTvn(jv>Tr@tpW$u%gKj^EiOvX`YW{dT#vhTx6a`SO195`Ar-#UCq)8o@hx? z{$H(v!O{TBFS4e1Vd!uE;C2SEVop1^?y2bAxyPB9t7o9IxLb8@xp&6ax&si)7*x8S z#{?|hu|$zKV9rK+HU{r=uw#Tu*p*4?TZ$O(4`A&tf_)(qkU{)8avBUtDgOYVC=4wp zzveFV_(zqnWK9-La0K4uCbe`;`RD58{GU{3jttc1x6`T^f<1Yp==By>17VFIC^4*K z(-4L8@y$~Uz)}2BDQ!U8QJb}^Lr9Eo?7ya_b_KM)V$vO-I5B{gSx6|;!x9rVn46Do z!Kyp3M7@Nb(42mJp4@T+d)@WH5lv+8Ne384f8=qv3n|5C3R2krUqTtYwMY>@;_+lPOXXIE@&&>yFqq zkI{#B8CEHq1shvyKA2Xwdqj<^pdpx(bihBO(@J#h{G_PgW=2gjXm4qq&H;)UjuEff zcUw|@PZcU&uz;zZKv*EsurI=HlCPv~za1+JOeHHIxl15v*+ADR!u) zok#Cr`g2}*GLk==SqV{=mVg_btFG}O4wz%8{2zHg17cg=$DTh*!O!*s*-SoaIVmmZ zv2kNOTF#bk}@emXEaN&nV_^FUZ6RQTv*mk*2De3 zf=pVSahpGcauL43C+E;PhJmC_f$Lr@L1Uv;=v?C(^Zrps6LAqUAYXQ^_&wBV8E$fX$>EU? z3`WzfzyqjSPjN9q8}_HkZ{`T1M0Bp?OB-`G#y1h7)%wf^V;x3l22@+V(MTY#?%W}5 z_EI1$f@-L@Y}z)btsxcMD1Hbh(*d45po36XovHm?o^n#;4(S`Cy?bi|nh5DXgLjC5 z9h`jN+vEUKLr*0;kB5BEAdf$)v5~+dL)C9h|rfS(4S* zZBjjyeR&5>O)Pm*F-;CIAL`FDGl{wDcTR-$bI&JmO=t^pA^{D)yy3LJil0F1?`WBS z=eicImEZ$%;j_9?x!yb{ieHS&!Gl+^fDwb&l)ZGuMv!Wu{JhE6mDG0KHAC^v(LSYN zc~Wi!2e{wHmJW&wQ%uQR{Je_2#%)W}ieVZ!J4&**1*hlOC8=K%zZLOdx0#2pezOk; zYXpZ{k~ySQ^9HPSke;tu-E#mOn6)dsK4+>zs9_Glu zaXg~`-vMNaM7WZx|Me`hNDy)ueVAa=eU%e8%d#-)q4qihe6^uE+WpbD&ol29pUBb~ zqdARdeR#vNWr(mCEGyOsxLW;-Nb2eU8b=2~H0OwyYn+=fLUd{_7ks5xCR2nkX>_Fd zp2&l1U$ez0s`W0q#m$?GCx*zox#phpcF;R2AOIa~36EYP!-5j6!2S=Kcjz5O2|X$z z)D0nzWJ~!QMfT+f7H>IX9m!^1Vg#ItiIvGULArXo>7WoH%k;a2u^>yvxF|gumIMVH zZc$Z!1OHUYt`FhL`CXVtGbvqM)^J0}p#3v>LNevoIv?pj&mJh%&ec2mobFl`*OOVU z_ia*!|G_5bBhhwIqpir)l7R=kdk#Nv-I%ug zhinlWb^i6zr=D?x=0yq9%GQ>)=_Kv)rcM#IaN4J$3mStZqLyW%L!|Th~Be?bZ z29j^|(BpkC^h6ZB{etrfe`E4hoT@nt`!diOP#WRZRQ!(y-67l(`T?&C#vT(W0&l(; zT0xM9rpog*!G$KAZ-N*}-y7^j(D0;UbPj6Jp@73)*?%VwX3=M!ZcUyB`eu=Gji_*Ygd)#E4h{Zgvl@=@Di(OID zjc97fs)`i-hxAX7$)ijJJv(P9-2!nF7%~f8TFDRSsWrYShzMXK^$<>J&yVzwhDlMUF##3pZ}+*+OQ0bm=z`8O;^9T0wMgHrSlOHw#X9o9T`B+Bdn>-GByaA; ztljSu#`MWn-G}W+I=)N`F`O>iVx;q%aNi$-7;spTFv<~1MQNRod5Jr0lxT~KfQo7? zuys1i0#@aQYeCq0%*R;wG=i0wY*6IRun8CTY3#Ux#(J%$JOY z?^GazdjTCm0^$gRzt?2DIW^{)C}*uw6ofT$38O?65q*nA9NJ-7bZv}@PoWJWLyXl6p15JV8VbOkLR1kT~ zo(r{Vy%deglSJEs92TbxSm3izD%o!!@ioC%JK^Wya8uw}kq|vNxfU{c9n~{)ionw; zRzM*prHV{$RRM4D&h9f#g;qyTgZ^yDzKumF#FW-F4+gd~QxGThg=|+zk+t(i2%jDA zhwEw6*YyJ)3^~7hS}+-ASiTw!i-G)4gISwCdlIC|-B@uWpR3)jRMZFRxLCj|EoQ)6 zl0{gSGbuwtHd>1B@m03{q4g~M+r-)Chi)Dd19oV(J4kUzsptVH+RY%pV}|ubSUO)v zC9xYZGf&1c{JaU<4-L4~VWzw}W`0}d98W=je<`9k$E$b7o|vnGOW#ORJi%m2($x*_ zmjOq-Np?BTPO8S}S!!Oz8Y3m?X%HdFbu_e?wxS;`V-WSd&P}?6Az)+?pw!BQc2v+# z|L`7g`I~FPNfVBbB8wq2+41(f@aF>;9qKL_V_jG=W-+4ql&uUR6lCRxLk1RnGTgcD ze0KRG9&L`*n6{tRdY~Tl9m0l!3q-A z=Zwgq2DG`AsxlSH?A%l7(0TeGBOI^Nk{)bBFJ&b*Q2GL8oc&}+7in^r6gxLJ@Ns}2 zfr)tSJ7QvP=&Z4>x*jA2=xKuSyWeAxpiJ&JF29OHzo_&c+g-G{US76m-yV$`!#A_g zvU-&~-5UGM5e6(@vAI2;m9x;nSYoL4Lyv?l^K;w8D7j5L4ki;E#L=wU!twohXLMR6 zqslPZ5us3frcy>P=}j|G%dyg&q6qTRr8GY{it8+Hln@ex-T1pzoWDRV-po<*77?K} zxK+s3$XJr{rv*DwqE`Z6tus0S{15KbKYCQ5+Yer0Vra@J@=Dej0Hto9^ zO7BeY(z(IMhE6=n1bont3>IC^Mun0}6OjK#nA&R4QgQ8pQg%;0;{ehrE&q=f2GxYF zXGowJfr0kEW+9)z^{Zs#7tgOb{~R$Litqy^GE5plD@D`@Ar(t%=4uh@%%OH-D?>DR z54?$Ttt8~CQlG63?&z?Q?oPfK!w`cIh^71tBhXHyvM8mH_4!-ksT%_yj?nC4f%*t} zg)1!GC&@C$()7&$hng?novx^Ly9bcsGK`dRpoYymebF#oM&2;p)Sjy`&S?U)8*(hM zf5-J59|RsqT^?{mV4Q zMN&gmU<{Pn9LPsrS7fPCrB}v?=^J`Rs&(4<+4}E3>|j$lBi5`%&+1^0r*>1f{34VD z+zD19hyV!rQIJOicjQF&J%6VN+#v+y!TLku4zh}!r>z%2iZIHtPm)=255rB)P?tp-%re?k z47A-siqI0dbw0UQm%(T0%V0YlUValS^yu2^i-8||PV_y%3#h!21uqljCeyDu#Q2gK zMl|N^(9<2+^$Xux#r=^RH-vYTjd5YHdIM@nbwmP(L?iLqn_4%zO)x^lAR5JO>|N5} zW1}lMXrj15E;vtL8t>f|{OU1M9!ha_kTK7>Ppy~GFr(VFa}gF8x)PlT<2157$pjPU9#QUy~1&D_%Hr8;6woEL1|-VaR`XZ`Wq~8IO8Il zL@cIS^hefHK;EJ6P8G9>3}w?ex4Fxni;E=TmJPAP|e$0<$SpKNJdE z7?a3~>cXk%kQT;7>&K@(?7Dh$E&eW17rg?sBY+qWi=hw)JuSENI|F;LsV)4l_+aLv zoH||au$2Hi`5cw>6ztAWx$Z?@~vi5 zuRQXXDZGE%(}!9<$E|dZ5dj0 zHLkr&_|0^$7#le^mFTZZ5e-tqu5GFg!r=0>2@+Uvh<_*dVH=e|#8rW3FLo*x@AS7P z54;lv(gVxazlb%M>^Olg06KE$Smo0NdUJ4UPNiYzEyId8FU8xj_I5Rj?{B>f;cCP5 zHGLR0#=_+ejfnr5Rv`PN--OwnLw33{;Rj!1)gE+)%qQs2jM@3YlH(v|1D_Xo@_<#g z;Lv63Sn_ylmeNuFa1v!Nnyv#o#2AEszywkHEX#&5i7<4qn@*QI2Hsio#2vUgPfLqB zGpIeVRL+qt; zL9|=A&07)@84cH;!!UP32lpZ^%56|%!I8LT7Nb4OC?W1nLW7hZ%`9j{;c-`l&I>2$ z>vjX%%srWDw~`Q6_iZP^JXb_LqzDj*8d((TWbpW zr1d?BEsf4j{tma7qevs4K8(Kj^}~y>_2=8S&WU^;_pPs0eOd+lDFOd^%VC<4f#vzq z$jyliO`=TE{bHH85p&Xg*C-hF$(sQ$Rk$225tcULo#)lcjelCP26M?*M$o?LOs&Y* zEcMQgI2-k^DT-J!Z=+An?{-qwJ>&*vZWD1R?9;a+k=Br{I?Gb1RD6b)oBZ&LF}X5NJD})Po0iD-tY2x-j!mIfp%` ztr}Goh;NCh=J6PU@N;gMavKA=sdQu@`Y`P}4wC2*1YU7*mn~Xw-1V(vPGj5FYK*3_?Y6Pq*mly`P8z#OW81cE+s1qLy1!?P@Avw% z#;Ie+oZDmw3c%p#!s2Yji*Nj%&|nTjPKG5nMe;uKA&FumK%O@wjE5b7V#AN=vg=8$ zAoGyq#*@&=a&2H@XPk6)sajKce7oOo;c8jjF^({Q1cO-1*&+U9sk0sceRAg>-}*qi ziJWfYhp_e3dp)|rc1o>|URZ^M zj!UkNz3P*}&ja{%D3CE3&%Y4M`kMihg}|HJJ2<D3?WJ6XQg`QE2r!f&?LGF{D z>RSs6#XA>7qnY7F^~V((r8D>nURDCmKYoK)PA1yn+y<;oe86oI6CH!#SHVPnljku? z>k|GNvW)X{Zift~MPC#=)cY6!w7l84YDP%E^gPFB=U`AbLZa8Qm?h?KOuNM#1eD|B z#lnO7WNSRk*yik00p-Zsn2aCTi}HXeR&j_-FSICjNYs z_BS*!7Xwe;A7PieydOhZZ3R=0e_O80ES&@Bb&-WY8 zic#uC0$P}f;aErEX<|fWd8prLQ34}sXsDDQ6kx!#RMdvaU@Sa!$*rGgAATa;R*ju+ z<5$`eJhkwF&M`KKK>da|!3FPsM|7k8`kk(#-3JBsHv3Q-ShrjC2 z{=rIXVwDRp4Di%ljSEzEZ}a2C<|NR(rTK4Q<^a2mqcm}2U3M!AqIhNx;Vo8IV@9($ zVxM1gPJbaW%T>absHh4F1j}&36)QGSy)uwv-Oq)BT!&QErquVRx(geSq|LEc)cs|p zbbygH&=Nmkjlc!4(B+vbc6;Jo6Vl=kt)=NGe5OMQl;0ZJ zpCfVTYwOVBA7v|Zw@-fTSbdR?G&4CJ1afXNRi-(K#x{YfaTwf7M#%T`G)Jlr~S!!A~$ zuc~Z**n;%#h(EP%2~iGdo|8DKb#)1)-tSi@RX@5i4Ns8kkxP!AyC?*le+eVIW*%-b zq9;OvuDQR8=dNa}LvZ(?9>OKbp_G!ub?eey&zlKhJ@Yp{PONy5y`@I@8uC?* zZ0jE3ghI=$;@W>8je)tBu6l1h66Z)iSGIv%OhaD4X+u~A`Go4Hca%of1a^qp6oF12 zrTsCcqy3>}te%nUPsOieCZxN++&YR`j)Shfgamargak@~YC4uTT7QHQ7P=4uw)y^i zpfi$A^g9h>i=@uC!Nfl8>sEc{s?K#9;I#NnC)r(;7l=~dcD3$=&F%brO5;Lck)_O- zNQb2`wc96!mQ112Gi^)WH4CJo9?p<>i9hf+D)3nFlwI4s-LZ3X3FWa-tdd-t-n_HG zeJTa@o{U|r=O)6ICg|BM30Lre9|V?FmvHF$QuHz-Se+A}-SDy& zzp-=uX6*i8$}iDXr@gGRlv5%z6S?q$@o&e}*93i)%FCZkg`H&k&{xJf-J4e<et`maL?w>$G02KwZG}h5dEt#FvObj!sAKLZjf3D49f;^;pqlg$C zVdI_ih7&&mx;MzDmAt0)AeB&8wH>tkD-nOY+9hs6=vn^Ee1``a@7$ABhKY(MpA4MIzW*7~ygZA9GU=AD6Jz zS^Ez^Nt?cxC?1%VmSpP9=6-6s~qG4B0{x( zVYie$HUc{*L_;-rd-()1Ch$Et^0Om5CKjm;9grdzBM?Qe&e&Ho>uTm?Wuwf6yOTsK zUC@$-46sUIiA~|djt-{!bA%Q9_t0-B^YX~dIu0~k2f7mHx0%QNwx^1?I-oa=%2=q; zO+h25d>z%v8NG%o=F1F7R?s(bw z;MWFVTo5E&wMzzD9!VVz=N3wF0|s?*p$k_FUAav_P;sxp8i;XIR8p z6SDUWBr>Fy27Qa(^kxkofus}*P^*awTxt81H6{k?C6Ke?eJmk81t{G}C||zPGBCBP z7AFJL*q*4H^oM2o?(_FPU44;dsOAl z-n2}NnADS7w`8kdb~2+f&aNq>cd?CC!`D(ujqPdwDukh{At)k4h+~=Cn2}-L6!N?E zhD?SJXwZR|pu`ih;x;k?Dqr&RcGWd;x;w}ROQ3Xl&K?Ui9o8sZO;Ux268{=U7QZCu zU^B{}s#Q~6>F+AMzhH8bw;rOAQT+B~>AV^bLH^0$k(pWj7wEhay*rOusZ1#|h>q8I z(*R+F0;D`(tRN~Yx0*gu;2(k?VCO3ar(4s;>H`zH7_g}@FjM=rLp*(CW!LEHD?7LF z9IKXBDMVfJl;kxzV65<5d*lfYYQkWkw}!Ti#?Rv{`p$aUzy>${`4e;3IhlM#t5XnThj*x! zHx6IpUCjB0r=X@x`t6j9we+iPAT(JoZ4}zWslxj!q#Yl!b)cE4ad2F0GXtkq`9D$i zem8aaRCI|V?G5+g@x{&tBsSj$lOdbo?UPL_LpMhwT7GZ?ap+)V5IO)=Ac@y8&CaMJ z^|}sd4)XAD3f>48jr@8}GE@8H8bIuGouV9?;Q7JukhGWL?ADUL(Iy4QPi(Yr1x+nr z8?9fG5dP}XvqO#~U#lu`U-r-5uGq>f_{DsOHbfyrR!xaak>g=)AL2Fu6iDi?N5_ZQ z`kg>kpZU8!+4udZPb^=Yu`6f=9A^2yLbF|_9G$}c@w^V9`g^G>>D;Kq#W>)w$rEx7 zLkk=DKqy9^melNnTZ|-!5|@;LIKSYL*fVF@GCXEEPRAm?IGUawP={r>o4>bdo zf2QumcLyjXcr^6}PcoebM*6DuAD>4IKPfeWD`;otmdIe-?sdFTg$|Tm*P}MgNr#F_ z91VHzzA=AnnNgY4x09T4Sgy2sv84!BHd&Due}&#C^_YCpim(anGj%*!D!aYpU)ESG z0KoIreCQXI&Tsr4%NY7v@N*w0NLuP58q3pwiyVuXF4cAaUO!aiK(4s=tddyX z;SjkB^6z-N=%Cof8H$E)LZ`}eQoS)~m(0lsq5QS1wn#b=6I||n{>L+_b75|~GHy&i zez|c3<;PP?sdAhc1OH3ngU;5AaWwi?;Ax9?tw&MTe`2Oq=30C~vP?T08$kov*bEmM zRWEcE=p8~(9?tJzlo)&7O5tn75M9TzlLs!^M(iYa+Z4Otpt^-$?aUV&T;7kCALif- zT9nNGr35DNXr*(Pq#nqWqG<{VW*jO>UF%BOhhMe!2|xFKS&x3yF*3BS*8v7JGmz(P zsn^6}P2gr<5_w_K387Vm*}rsc*M9HUeSiAN*jkxpl^rqI=0uMpV^m+4OoppBev)nY zC1TJ-w)aSBUV)Gdik6zIX7$`~8|};ohC@ibtn>RlyI-P8QRx)3?!wd&m^$EoO9?r! znp<%~8E!ef>I=I2&M1sy1{cN78cK*TNtq=Q`QyCIVWUX|p&^;BcGzpBI9Qw!QAKZ< z$;m)L`8UL^ChD$3g-(9fGmmazIG0I2I|}t@(!n+n9W;O2(y{zZCIYB%_SI&)s1TH2 zrR<`aU1pRUgW0{$+wa!*sGBs^8x8IE*l&?Ow`|JkoV86OOuT%mC2k{|>-1MPT^N^x zfq}^%!iU10?{_3`90_NYl#2J?sJ4*F%2a2mjYTe z(xAWLN{0(M5Ho%6V|B&F#k4wLxaN2KENS2ftP{VMwXfM#eF34P%%X(G*^c812&Ncx zIxnh02ovS58S{Q+sBrwliyBDvOj-)%%o37T2U6DTL;E-Igr4ttG79bP!^e7hSpHL| z<)%@DTUEN#;P)uv{oH4W3jsyv@X+Xs}OV$|va4fe3 zm(^&oX$;b**^$vtwSJZ8rSwd$uMwY)(Jl@A9>srKaBZdlY!#`fJAkpR?yTz1{_q^3 zHmk%n$9EVP(V?@PA zkrL4Jv`&%^p=rr&6>7NT`yb-P3=D#`#?Y(elc}NPb=wT?4)0dt5AIa|evGK#*^Cp? zHv0^^;>L~Fi=krdN1a>k((f8&FtJvk=o(b&>>hpA)!J74+CLG{($mQynjz5m(iP^u zD2SEI)qn8USQ)@;6(O*u8OEW^i?MUv4gCSf)05!JQ!c$GB}J~%4s39E`fcR0LYp-n zUWY|qppA-v&=oO6Uki8U+dTb<*fOyGq?X(Vy&iYq(6B&HZk-nY=$J)$OjJPx)_qXskIw@D8QgwidkKxbUN&qyJfnj?Vu7R-!N*f=?9_NNIg%XZc;ej6(sXmgEv9 z*tQ|jCEw_ss20H(CB%u@yWR$Q?CT-Pdtb^6N6;&r=d8%1pKNJ}LaZrM7d>k~Ulol7 zw7zxP-_R=MDz-1rK*<5krNiVqSt_0|<)1Dne1>-@CFlUXe!ymH4g?iH$wE5;+KDA_ap#v|`Dpb`DD*NNc#sZbi=7L5v!XVlG%rJ*f z?RS9I?dJHK;n-4~oDm(-NEkHA8}#-YnaL&Y06a_N1-L)@)!((sciZsR9%AdB7q$~# z_NaAVZ{>8YMGOS0>Sf_dixd5fiRzd!N>QNwk zoAxpl+S-Dd3??SqibIo{slhbqn+9?jHwH7hNSFN=XS@8W!VV~U>Mt{`r28|&x$Mc~ zN?+5^Y_LAfDp!qmhxPGy>>F(@BcUE?h6@1>8CgTQ4`m>4mK2obO!~4w!6i(dkbo|z zVet8rh~k1w2v?9|@5}K;*Ym#DydQMCH-^PU#f>9hamW4X*UMYBtox_s19E<>cqZFY ziQE$F;hWnUK`-}yU}+B6GaLmJ%pX{zvK@{5MWn z9X7)@E*j3Gr5iRO^?1>Sq_}cIs4*oB=8{{Emmc?>gGacOPzV`p1qtDU%V+tnbLkL3 zzs;F>$A#*0x3_d*@!{S`nTKu385MfBP4mWVPR)HW!`L zO}I59`>^OET}50tncAstYv@w`Psocb|pL(HK?Rf!>1bp$7M6-xFdlVvnGr9Vn%=$Ew6lCt$#35>T@blF`*{yhiJV^sXtZNDhyB9yo$<<>uk5h?#?O6aW#byULTG7i%8vc_jUk`C; ztQY}e)$->wtkvbNJ3b~-6K7ZF5a%R+z!3sXjccph0|`FV(ydw5&%gvT+@}iMrRL3F z3!)hJGoQI9kq7&6*P>(w65)$n1;0nDvPeGpg3})y2b3=BMD{a9WlE7pUy3LE*$Yd+ z`+_Vv?=Q495SxpL2z1p;+Z;z`;{a%FAb(oF^Ud*Oit~vkW^az+Dg#WwxN+&L8YMAK zZLm6aGzHt)zyPth|H`*UpWNT_p+RnCgTjt%;#XRUFc8*PmFBRL-FQW@j9-wfXgq$x zs!a)7pj}5a_KECKhFTA(m8qJW4f}v%S1!ud*Rnfnb;~B+Mtib&3oUe8S6%0c|MTC_ z>M$z~z5!D@HKZ3fH3VhA03y`og>5G;csz4uF(!5;@Ujl1aLiduG$CbCUg@RfYNFB2 z%ph#GH()VM$|p|vbs!{Q_*;;cG2xO@h$EA}FtyV&R0I}(zH+dHv}l~JhZnQCsu674 zXW@H#BZ68kd9?m*f0RopbKE<NHhSEJebILOOnd2H!#+`O}t;&Vo0z`i+vf`Sx&1;~cRBQwAjvlH`8^SdCbMnhxs~ zS%4L^@Rfh;raVHq*&EDP1%U8BVYZE#-LF~mMJ$(mmW)@<4j!=|+%7|yuUE%+wBI8K z*K6FS&;{~tkE0@#u_=O8P`uw5baCkq7t~bGV+a8r#Kn_zPzShszXOL8P&% z``4O0qCGj0*U>KpWAJ|^#1!w*1NpPtgcc!tZMiymAUP@np6G)fZS0I<4B}+p9@#Vr zmcn+~54*|(H-Dx;%mhT5hUxhB21-^2eXWoT2_l5IN*`80>&2spx8+Q=D&yYxXKtq* zkGKKBZLWJ{iXoXS!47yunijztWX9N`TDKQ?Mx)DJeNrnW_Pv0*Q!OHb!LR3+YYn`c z?|%sLQ3}}@V&cJ|Yd$X1WB~*WXeupxKmcD~m*8)H$pl*EVxpr0UrzA(64~5`01HYm z`f5Qjx<1YH$n6 z>K)j^S9Bhtr7)?`WN)L}?wH%NN1{8!?Q;45T2k6(Fm+;%E{FBOA_V2M{WAC=4+zE~ zS;8gvpDcc254A$Pc*i4O$K3I2=EUfa8`_UKW^lvjh=2V8q>)spB{xIpRz~!}8p`0S zVoJ)T3!M-qRjDN@pQS(bD(#kf)TYqSytzvKWRgJm4u=pE1E%B7cUoaP(~!U@gWGMl zsEsr=XlY|$rE7x9sZ94h6T@{7DpE?js<8UgsYDDILTJ_GfX%2)_{f9Xe@;R>liTJz z0XcN-=7(cPkY_%w%fqpR4f73uX7kBU_62F4h)a_MV6VF}oav#Q9WN2wVQUeZB%qzw zx59v)wM^$m>?myH46hXtdkS@>lE%Q!irV8Irp%36N^716VDMmn;)wWmDZ8dnVM)2b ztd#9UJe65CWwLN}L!je1kkKE+8$XkN%a(Wr`ntr0sc#~dh=)`@u7)TR0jJHbA0pTV z7D%Pky~pNY7FI2IF`8r;UY=2dw6D2fUZ1X__9$s_Mp(&m;gnthc6I@v@u_WVlqr4S zG<&M4Gfhh5IjOBd&o`nKiyeC(Nef3MDOwO}M?=9wbeD>A)lrg)1ERQc5qPPIyu6Fg zrJ=jb*;BJViH=Su%Y?YO&leyTe3#vKrv?Obxx9^_K=iy0l>tGoRWm za6I%A>VD+CFe4gizqWO?;e@K_)rlD-^{C0H96(t@bJ7DRaCwbP$!PGYdOsN!@5*hGr?Esctg}qMeIV7-BMC zcF_^$VwcG1jl#F#`TrGmBO;p?-WN2{hu14OM!9t)LcG;$h7`dEn-@h{@svkv)(=cO zU3O5%T;5m7_|d@S+};CoH}2LajqmCecJw7)_ZB62+mi!K$>512-|9&%?wLb%TNYBQLMDcj&R&b+g>)c87S*WKQ$A2S1{nHW;0c zHi1We!1ilYpEgx2;HH~Vl*cssPQG8t9Xf3ei58twV$d!tPZAX*wj=)=dS?ob-@lZM zu7J(UFziH9_Q@`}Q!hwF5|ee-tv0PUc-N#OzhwzPOJlGEFE zR3?6*zmLPig^t#`RPq*ff`k(CEBq%VO7=hBf1>kfm`o99!7014i3fmOx5NTpkl>(| z!Up$iCoe}8ch)32aquq0;P72GFWWJPnkF~4T8=mtxma)WS``x~s@(iMm{t?z?u+XB zuV$uLmv*kWS{CjEM7njK)voQ22b{A&e zm@rdUZGTI~;8Q~fyZGr)L4}j9=IyF=C-T@1mf_YiFRs>h$DLVYhj^vk5CL^j408#1hSImRG_=NJh+9A6Lr_@VRJ9XX6juiV%xTgNL2h63jf45gI2Lpm-XJl zG2>`aVk=7SC#x^ZmTfs)>t9fd4nIuF<@l>$dZbTGSwb{QW6!=|shbJg9z_N}U|hz* z+4S|YrWvy!p@pTj^vr;>YGySH=aB%CID2A14XYo=h9-MirLo8T%=O9T)|6I3WpJ~` zAtBUsk1Z;%?j8~?4Y9~bD684WFIX``fJ>d4)!_rBN{2Dw(?&wk5!>xbQ#0{l0O`uBEvD)B9%BZ3YIKt zZkM9@^ZxEx4Vxm=wlwz;rHE|ny`Q?g|K{Ok3*r>H-OLNFw%(h<|NaLHKLZ~K$^GuV zk<^II_ih=x8enbz!h74c@sX)!5=l*QR%Q)!)_Dp(uXu7=xXH+v=N&+tgOqyk40>@$ zz)EL*^ zdb($yzUl1>k*uP#2s&P8Qw|(BdgplnuryeI1YtBFw?TdED$IY1vMM`-E{Ne`gdHzX zB`6s7Cwm?vJ#`OiM>_oiG*95yck-G`=XUZWy;r#_Cx1P;YCTbHQ`znem}2$tdi=N0 zHA5eRf)ngdO@q!6g%z^3hyp@B2bW)q`wvpLtn}5#OyLdaM0rc(D{#;dCJ!t>pH2pG znG6DW^l`}7v-}A&Sfflv9Qp*qtMJkGC0F^fx<%NmRzc{@ z?;(W*vERZ#y6~D{xP)QpNVg`45f;X)yPwHaOeK52hu3!bvo;4H@0r(l+_W?twRK!E zdc7a4Q?!i8%RKx5IQA3!M`Ky;0XlEprL7<2+CB&L+HP;?LGwN2KepEYQFO8n9-e~% z2Vp(KiG%IY*29id_!v1x^p&D;S<7qwRf#7QsFq$k1^E(BGup*0KH2L0RS5?fGVq69 zWmOGf!r5jybU4*lhn5v-k(mI@luLmbbwgXo6FBTqPLhOGaJK1`I!@eJvp;O8E`{pU zFn|v?Ys&gwuXRL`e(KZrqqq@46?(ro;VF!;)tKCNvsn|Ia5|_(JGSLaRXw60K?!pL z5DJFz5M_kyAj45LXyw)YcMyF!TAI{R@FNLjr2pc;9q;E>3gYT!5ax0S`@ADcoKfT$ zGD#W%xKj(@MZR_r(63L;SMa+O@j=^p$m>{AhoA26kvlitlyl^r^2Dqp^mtN3Jux@9 z3U}2~Gebj(Fcj52z_Xtj4AO`H8|0xZ4p%-5jFbzqYMU*UcV0<%T`(`>ELNGSTGdEr zEn11a+yws9Z74c|!1vMn7mD- z!%roQGc@n#+AbJ0ae;&%g%`1GZkplX0d0%dJs#0p@h-;UlBq>4JlUo3{G$MQ8m=lu zyNS8x=KV^a(GP$@c3d_g0$b?=HHE`d2@kEUI0#P&cW#8F1+?F+=5d1wVrVNrp zCHC%L$L9`b@vVxMqLk@f*htW{mV#$%$uI41`V#%$LWTsBDfj743~DUvH60g-=Mv{N zOO~e4UH3F&%-lJy{n+>Zv1t7%G0Q`9-*0wrmvcub88 zol%+|b2<_>UKO0K5?f{JD9xNT2{1!6q}vKsywNxN|GTPP*5$$ssjR8m&iJXsIV4^; z1GV-x>ISkR7DCrZPqgXdQ^$G6>RBPw<{=FhCi~BbKyZ1x#7b#8ppcWwo5`zg=N^M5 zK11oE#ge&woPl1}SkCQhk=Z4>=Zp6x!xAa#BU1S*4eTEP4U4}#CJ|Mj3C$*(iB87u z!wLXqiyt}Ily?c-*GVtjLJ(mMZ?`v%Y^AV17la);ua-7ll!{RHi-rRd^M2Om=1Ckx zpQ*7qBWZOpXLptH1s*FI<>Ec!$h`f??ko5=c@ki@-d`mL#^N0+jqQx}Aqo#`tRhm-o3T73a2J-XiP8UQl-V`s1(&+%3D z53{iAoPssN^*}^Ky;n{I0O(jySvjr|m?vRe>n*EWI1n`J2+2y`LBJdy&b;a=lo}M8 z1rawv?1Be{wD@xoi=beF{30p5F(cKB;Qr=x^B@oxA1@UIT2m zdN?%T<3tA`)0~<&Jjy0k*| zA6mqF{P7vO?pnlE`Sz3IvEF`DO8v{Q!VE+9qi)`0pB7W)Lq=0~*orHy%ad$Z`TK*i zhY6hvjFO}H>Z>K}QWZaNt&DTdFs+KyQ*-UW-7OLgbp}^)cw{s!v-jci5e)x&`R)& zpPmahR;$k~D}?sdLKsr>YjYS~z4*!kr^av_s@&}u);72RWh0u2q;H^+026}bcAzaF zh;gm6AbVwDnH(k$Lzjz=r)u8fsFTf{n{ZN8@YWCmJ~leczZ!G0ocOWWD`k^ppy{!0 zsyA7B%?gy=tIL%c`IqE9+B7~3ykz2c1UJN=J#k~7wJpEbIgQEOv{z=-qgZt{zineW ziEjNccDajAM=@FA5c0u1j6quoQ{PcTNUm}gY{I8O2}70{_Iu1+low{DIv?nPWc)YO zlp&RHvBv_ofSG7TU+q1`Po#;jyD>NlL6D;eRf6qwk5E78fsq3%@Os_(TSJs<8M#na_Rr1e>N3 zsGV&kMpE@Jc##gp!jw#}ZgDmlTK0T9jd1F%7TR7YJIS5F51z0ey!n_#`ipeFt+%l9 z|E8xRou@b7qeI(Set2_cw!!eQ0UzEK(@^X~`Pd8UC0bP2c2)he)eL9PYaS zQxbf6F#g1{NHv=KDSG^}mON|*2P+l1A!U?}gSSLARjxCGAmL|mSjk35)WxE^7{p#$ zwjF_j+()2Bo}x?n79_}RMcTQ!e1h*7hH#-z+E0>O-^6$yjx60Tlz_rM+J7l_JP(aC z6laNC+6&eIvC*FRG9*-!Ij$lvkoeya6j^`pPf;mII!CJN)}a5UOvnReD#)^XMW!W= zvSfxIj6*Iq5|!>oWsBX4=^Q;ypA@D<(-yHn{D{EF(GmyIR}bqK58kSG6-mhV-YAVz zZ0t01?)9qh9)PT4{x+2q@*`o$FWkYFLzZVm=ug5=BNt8MeG;+BfD`@Z%V)((uzKI6p-!Jb$%0lLU$lc4X3vASB5#|n9`EW|>oe_b zJs`#_%oi$_kc?kSEBii|{HHN0Y|laW!4H2!QqbipB+w;DR!I66HvyGOGxUHTcB5Mt z%bkS_5(=@F?k(A204oO(*^+%jj)-$~Bqy=8l+LdodJWEbQCifV<1-V72cY(UTa^dy zb9XQ>TSF;Pp>ObgWTQOXWX=p#hj^7aw9yDn zgYLU}c9M}WQ!Dmfz0Ei3OHvIo$t~_UIg_TpwL>20Lf924ArU5x+N07Ne$Xobx+|B2 z7H4=GC)-Hf%O4f)v23-TIFo`jSsc9+z6ln^=Az1`wx;?LV-)r$0x0@Z$LAYewNF=s zGLb7rQwFV@3IaI?-q0AW1VGWu6I)b0v(7rgxTz9*1JyVWOGYfPT9M?mL3^4Sgx;zvzy*g1JtUF*Cy*Ae`}$LD#DV`U=; zpoh|(lLOe@tX>Db0n~jctypk`oP#80*IH8mte7o}NN!ZBYLU^TBdYzYc$+6SjHU)n zbeDR|oqjAG+mA9SYB|ar#R4TjJ}-b;d3=PVdxD>mvMLgBD5OJPd7YEx8-!Aqs|h{= zT2@!n(wFKBB>rhnLDRXJ-(h@&fmAy+WUJg;WYasN_JS$rn=2Q*NVwSn%kkINczr_x zN4>znqZPwp^ldhH$2};3P6N@Tn0dDdE?8+>^Z_y_!?X%T-`eI@0+y;xcq0f7W){sR z*_)B=e`R!S=V)6eM?p35G4-fYOf z=%2!F;kXLei#a{sV}LWxw`ke{SxeXk2z~?cXUg{PG0*8n7%YAiHNh(F z(woJUjU-#51U(C!{~4>t@eUCu@w(3nXXLNS1pl}lWXJQ0ln3Bodx@xc+l7jm!iW`d z;eM5Ngkm_j7gnr(BBrq}#zM^p$DTl4Zeh?5wSYz~|JS?ijW^FOi5TQ}<|{zat~zh^ zhdTrm_vwO)$mGFc(C#AIzou0Z7}<;bEB=x4f7FdFF;Uvyq~2+Vvr=9OA^OniVzbcM z`hb_3GmJVrv>_1-wSEl7VkF1GWr|&Q@+W^&<0qr98+}r1uJaA+-0@*{T0+74F&TYk z-EUqanQ+r+Hg1~31v`dL&`d)3V%`3)sQ+9vlJNI5R8_TfB)bpWb~iRG+onb7WSIzS#l>V0-!fB1eF1KSR7c1SRK!U(p}ntrbfI{U{KxfSnnT(fh? zo+wP0PQa4m=pPFGDF9NF_F)!_ZOs&ZvN(GO2c*T37cHzSkr&KBsP23rmiqVNq0Y7O zRe^|u=9mQyInPnD?UBLa!zCRZaYH788#SLD6Nxvp@&_7{%q+Ym(62j{m*H!DP>arL z0~R4$-Pu|9nsE@L2!hwc3o%`#-%0gsaqv!asEX}nt?swjm~N`v9;`S#xCSzjBL+uBO`D}hj0^x zcY4o}(7r%4{OG+QrHe_b+-7DSBK*sAT1mPvGdN4i?HOe{j)E;~bQpA!M6;RHNG_6I zmC8hbYl`Itb*0oO+2p`l@~_C^C0vai!>)y8*&r%`2vgPac`DBV}1-9BgWxulPRo zo((u}qZdUJ!mRnrcC=Wag{JnBby?vRv`oTgVQOgopBV&J@9!p+IyuV7OCl2d@bn?$7-nfLfT4E!S5Mb~eXirn$O)H+Y+7AawetwXJ_@ah zBgyU%eD{-&U(v5dr&8YR_aH>UmwoSd#$dN|`&QPi!J0P%_5=Y&T@Y;tPgLdZ0we(a z8a%C(^zn1u-f=FmZHTfI9pXBsN>Dq�mg5QaZSk+2=Dh2gH;@?lU&IXr=GmcJMKK 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 literal 0 HcmV?d00001 diff --git a/luci-app-gpoint-main/Images/overview_wait.png b/luci-app-gpoint-main/Images/overview_wait.png new file mode 100644 index 0000000000000000000000000000000000000000..ebe30a42d34400b88c5cebecedc414fe3a132421 GIT binary patch literal 138466 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
A^h0 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 AssI20 literal 0 HcmV?d00001 diff --git a/luci-app-gpoint-main/Images/settings.png b/luci-app-gpoint-main/Images/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..320183474f00eadd27cd7feed33b200c2906915d GIT binary patch literal 47850 zcmd43cT`hb*Efp&hyv$`$dP)KBUM19g8>^Ny@ZZ{igam0C<#SHK~O+cdhazvx+FkE z1f&KCNgyFar6wUnN)jNDT<|>axZnNCc<%e%ciemb$Vm3yYpp%!Tx-wuo4+}C5-iLN z_;^Kld3bpE4DZ~s;^8?|zW1^E?daZ_(5L9Ry{`koRt7hC>PE!Kdk2R-ubW=y;i*qK zv2*{(-tqCkI}X7-JpA4Jp96gXl@ED%;4y}`u3Lw@EEA7MT4TDnT>8UwlPgnp>Ay2= zem{I<;zyhp)K?B~E4h6WYV6xw<{0&;KCx`vQO?|t25ffZYr!dBzfcMzG-{9gF#pmACzuNVy12BV%K?gL~re{AFmhY`6b| z=k2q5`)cvrk$)z)|NU3hZ+oio@H{C!pZTll+r)p@V8GJRQKYf&z+SIH;F8)!(-(kt zqagf|%^Uhs<8{MH+aGJYl(r(r-1cu&2&}*19i)82`-(%~%=TG(y;sTyyH4+2xK{Ci z@xC~CvpD+9Vc^~|PaZFzEU!^eg&g0FH5nZu8A>+naTOm{x>C0lS^7?G&W~NXQz#foS4%BYhq8;YTHc^N)tfy|FuhS$3@MQJAsR*Q|&33EgqB z1C;d;4kpY`muZJiM2KXD=rf2T*!r#UdcJe_! zvCEcy3!8#eLY7xwmX=9LvX&*Fv0*OcIcAAe%2%Ew5IXT?Q8F>irLv(*fRQP;)bD5nbZ0T2-6UPuQ-C1bZIY1$>9x{e2alOPUk* z`o7+1+s&gjf7MuMc|Z~oug5<*+@`?me*CmO#xaykch?smO(A#m%fXq8V96v6WU>h-|gCEPR z`{Cm06=H`N8&!wO5fIZm7nFTKoA!KU+rQLm$9L?aYe^j&@39kcbAzIF2VP_)%=7DS zUrLe)jawsZtT;vjkafYglPxKi2b)k-J zI$~oHNi4&)PB<8gzkN9+;hLhNS{GoaHLK97g`iG$x!Eiq_Zl2H#+Y{EVr8&d9jQde z^%+S=(ooq}E=u!Id;jZwT$CE=q@+6CL5ur-bn{~BO z&kWJzi5dBR!weCu@BB8X!K|IJkUakNK2x2~>=9|{X>1zNP_n06?U!THPK!nMssf$a z$v{%AiO`1TP1mzQEpee#X;{f^QCp6UakX)XNx{ronNSS`*F~8c7D=|$SUeMDodtc` z!O{q*QP*IMeo~59n=dFFlNv#^4WSPH%0AI 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 + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/luci-app-gpoint-main/Makefile b/luci-app-gpoint-main/Makefile new file mode 100644 index 000000000..6accd6e1e --- /dev/null +++ b/luci-app-gpoint-main/Makefile @@ -0,0 +1,14 @@ +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=GNSS Information dashboard for 3G/LTE dongle +LUCI_DEPENDS:=+lua +curl +lua-rs232 +luasocket +iwinfo +libiwinfo-lua +lua-bit32 +PKG_LICENSE:=GPLv3 +PKG_VERSION:=1.6.0 + +define Package/luci-app-gpoint/conffiles + /etc/config/gpoint +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-gpoint-main/README.md b/luci-app-gpoint-main/README.md new file mode 100644 index 000000000..2c2fc338f --- /dev/null +++ b/luci-app-gpoint-main/README.md @@ -0,0 +1,45 @@ +

+ Gpoint +
Global Navigation Satellite System for OpenWrt LuCi
+

+

Gpoint was created in order to use full set of functions of mobile modules installed in OpenWRT router. +Manufacturers of GSM/3G/LTE modems often lay down GNSS function, so why not use it? +It doesn't matter if you use a router in transport or it is installed in your terminal, you can always find out its location!

+
+ Screenshots + overview_wait + overview + overview +
+ +## Features +- Support: GPS, GLONASS (works with "NMEA 0183" standard protocol) +- GeoHash (reduces drift of GPS\GLONASS coordinate readings in parking) +- [Kalman filter](https://github.com/lacker/ikalman) (Implementation of Kalman filter for geo (gps) tracks. This is a Lua port of original C code) +- Yandex Locator [API](https://yandex.ru/dev/locator/) (Determines location by nearest Wi-Fi access points) +- Server side (sends GNSS data to a remote server) +- Support [OpenLayers](https://openlayers.org/) maps in UI, and much more! + +## Supported devices +- Dell DW5821e/DW5829e +- Quectel EC25/EP06/EM12/RM500Q +- Sierra EM7455/EM7565 +- Simcom SIM7600E-H +- U-Blox VK-172 GPS/GLONASS module (u-blox 7 GNSS modules) + + +## Supported GNSS protocols +- [OsmAnd](https://www.traccar.org/osmand/) +- [Wialon IPS](https://gurtam.com/ru/gps-hardware/soft/wialon-ips) + +## Install +- Upload ipk file to tmp folder +- cd /tmp +- opkg update +- opkg install luci-app-gpoint_1.7.0_all.ipk + +## Uninstall +- opkg remove luci-app-gpoint + +## License +Gpoint like OpenWRT is released under the GPL v3.0 License - see detailed [LICENSE](https://github.com/Kodo-kakaku/luci-app-gpoint/blob/main/LICENSE). diff --git a/luci-app-gpoint-main/htdocs/luci-static/resources/icons/gpoint_icons/marker.png b/luci-app-gpoint-main/htdocs/luci-static/resources/icons/gpoint_icons/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..950edf24677ded147df13b26f91baa2b0fa70513 GIT binary patch literal 1466 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 Z3jF5^{O1b%=L-Di3jF5^{KZ$`e*x^2*|q=x literal 0 HcmV?d00001 diff --git a/luci-app-gpoint-main/luasrc/controller/gpoint/gpoint.lua b/luci-app-gpoint-main/luasrc/controller/gpoint/gpoint.lua new file mode 100644 index 000000000..a16f77adf --- /dev/null +++ b/luci-app-gpoint-main/luasrc/controller/gpoint/gpoint.lua @@ -0,0 +1,86 @@ +------------------------------------------------------------- +-- luci-app-gpoint. Gnss information dashboard for 3G/LTE dongle. +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local ubus = require("ubus") +local uci = require("luci.model.uci") +local json = require("luci.jsonc") + +module("luci.controller.gpoint.gpoint", package.seeall) + +function index() + entry({"admin", "services", "gpoint"}, alias ("admin","services", "gpoint", "map"), translate("GPoint"), 10).acl_depends={"unauthenticated"} + entry({"admin", "services", "gpoint", "map"}, template("gpoint/overview"), translate("Overview"), 51).acl_depends={"unauthenticated"} + entry({"admin", "services", "gpoint", "settings"}, cbi("gpoint/gpoint"), translate("Settings"), 52).acl_depends={"unauthenticated"} + entry({"admin", "services", "gpoint", "action"}, call("gpoint_action"), nil).leaf = true + entry({"admin", "services", "gpoint", "geopoint"}, call("get_geopoint"), nil).leaf = true + entry({"admin", "services", "gpoint", "blackbox"}, call("get_blackbox"), nil).leaf = true +end + +local serviceIsStop = { + warning={ + app={true,"Service stop"}, + server={true,"Service stop"}, + filter={true,"Service stop"}, + locator={true,"Service stop"}, + kalman={true,"Service stop"} + } +} + +local serviceUbusFailed = { + warning={ + app={true,"Ubus Failed"}, + server={true,"Loading..."}, + filter={true,"Loading..."}, + locator={true,"Loading..."}, + kalman ={true, "Loading..."} + } +} + +-- Overview JSON request +function get_geopoint() + local sessionId = uci:get("gpoint", "service_settings", "sessionid") + luci.http.prepare_content("application/json") + local data + if sessionId == "stop" then + data = json.stringify(serviceIsStop) + else + local conn = ubus.connect() + if conn then + local resp = conn:call("session", "list", {ubus_rpc_session = sessionId}) + data = json.stringify(resp.data) + conn:close() + else + data = json.stringify(serviceUbusFailed) + end + + end + luci.http.write(data) +end + +-- BlackBox JSON request +function get_blackbox() + local data = luci.sys.exec("cat /usr/share/gpoint/tmp/blackbox.json") + luci.http.prepare_content("application/json") + luci.http.write(data) +end + +-- Settings init.d service +function gpoint_action(name) + local packageName = "gpoint" + if name == "start" then + luci.sys.init.start(packageName) + elseif name == "action" then + luci.util.exec("/etc/init.d/" .. packageName .. " reload") + elseif name == "stop" then + luci.sys.init.stop(packageName) + elseif name == "enable" then + luci.sys.init.enable(packageName) + elseif name == "disable" then + luci.sys.init.disable(packageName) + end + luci.http.prepare_content("text/plain") + luci.http.write("0") +end diff --git a/luci-app-gpoint-main/luasrc/model/cbi/gpoint/gpoint.lua b/luci-app-gpoint-main/luasrc/model/cbi/gpoint/gpoint.lua new file mode 100644 index 000000000..c262b8958 --- /dev/null +++ b/luci-app-gpoint-main/luasrc/model/cbi/gpoint/gpoint.lua @@ -0,0 +1,252 @@ +------------------------------------------------------------- +-- luci-app-gpoint. Gnss information dashboard for 3G/LTE dongle. +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local fs = require("nixio.fs") +local sys = require("luci.sys") +local util = require("luci.util") +local json = require("luci.jsonc") + + +local packageName = "gpoint" +local helperText = "" +local tmpfsStatus, tmpfsStatusCode +local ubusStatus = util.ubus("service", "list", { name = packageName }) +local lsusb = sys.exec("lsusb") +local device_port = fs.glob("/dev/tty[A-Z][A-Z]*") + +local timezone = { + {'Autodetect (experimental)','auto'},{'Etc/GMT', '0' },{ 'Etc/GMT+1', '1' },{ 'Etc/GMT+10', '10'}, + { 'Etc/GMT+11', '11'},{ 'Etc/GMT+12', '12'},{ 'Etc/GMT+2', '2' },{ 'Etc/GMT+3', '3' }, + { 'Etc/GMT+4', '4' },{ 'Etc/GMT+5', '5' },{ 'Etc/GMT+6', '6' },{ 'Etc/GMT+7', '7' }, + { 'Etc/GMT+8', '8' },{ 'Etc/GMT+9', '9' },{ 'Etc/GMT-1', '-1' },{ 'Etc/GMT-10', '-10'}, + { 'Etc/GMT-11', '-11'},{ 'Etc/GMT-12', '-12'},{ 'Etc/GMT-13', '-13'},{ 'Etc/GMT-14', '-14'}, + { 'Etc/GMT-2', '-2' },{ 'Etc/GMT-3', '-3' },{ 'Etc/GMT-4', '-4' },{ 'Etc/GMT-5', '-5' }, + { 'Etc/GMT-6', '-6' },{ 'Etc/GMT-7', '-7' },{ 'Etc/GMT-8', '-8' },{ 'Etc/GMT-9', '-9' } +} + +local modems = { + ["Quectel"] = { + ["2c7c:0306"] = "EP06", + ["2c7c:0512"] = "EM12", + ["2c7c:0125"] = "EC25", + ["2c7c:0800"] = "RM500Q" + }, + ["Sierra"] = { + ["1199:9071"] = "EM7455", + ["1199:9091"] = "EM7565" + }, + ["U-Blox"] = { + ["1546:01a7"] = "VK-172" + }, + ["Simcom"] = { + ["1e0e:9001"] = "SIM7600E-H" + }, + ["Dell"] = { + ["413c:81d7"] = "DW5821e", + ["413c:81e6"] = "DW5829e" + } + +} + +local m = Map("gpoint", translate("")) + +-- Service +local s = m:section(TypedSection, "modem_settings", translate("Service")) +s.anonymous = true +s.addremove = false + +local o = s:option(DummyValue, "_dummy") +o.template = packageName .. "/buttons" + +o = s:option(DummyValue, "_dummy", translate("Service Status:")) +o.template = packageName .. "/service_status" + + +-- Modem +s = m:section(TypedSection, "modem_settings", translate("Modem"), translate("Select the modem(s) to find the location")) +s.anonymous = true +s.addremove = false + +local no_device = true +o = s:option(ListValue, "modem", translate("Modem(s):")) +if lsusb then + for modem_name, modem_data in pairs(modems) do + for id, modem in pairs(modem_data) do + if string.find(lsusb, id) then + no_device = false + o:value(modem_name .. '_' .. modem, modem_name .. ' ' .. modem) + end + end + end +end + +if no_device then + o:value('mnf', translate("-- Modems not found --")) +end + +o = s:option(ListValue, "port", translate("Modem port:"), translate("Select the NMEA port of the device.")) +if no_device then + o:value('pnf', translate("-- disable --")) + o = s:option( DummyValue, "nfound") + function o.cfgvalue(self, section) + local nfound = "
No modem(s) found! Check the modem connections.
\ +
Supported modems: " + for modem_name, modem_data in pairs(modems) do + nfound = nfound .. "
" .. modem_name .. ' ' + for _, modem in pairs(modem_data) do + nfound = nfound .. modem .. ", " + end + nfound = nfound:sub(1, -3) + end + nfound = nfound .. "
" + return translate(nfound) + end + o.rawhtml = true +else + if device_port then + for node in device_port do + o:value(node, node) + end + end +end + +-- Add TimeZone +o = s:option(ListValue, "timezone", translate("Timezone:")) +for _, zone in pairs(timezone) do + o:value(zone[2], zone[1]) +end + + +-- Remote Server +s = m:section(TypedSection, "server_settings", translate("Remote Server"), translate("Configuration of the remote navigation server")) +s.addremove = false +s.anonymous = true + +o = s:option(Flag, "server_enable", translate("Enable server:"), translate("Enabling Remote Server service")) + +o = s:option(ListValue, "proto", translate(" "), translate("Navigation data transmission protocol")) +o.widget = "radio" +o:value("traccar", " Traccar Client") -- Key and value pairs +o:value("wialon", " Wialon IPS") +o.default = "trackcar" + +o = s:option(Value, "server_frequency", translate("Frequency:"), translate("Frequency of sending data to the Remote Server")) +o.placeholder = "In seconds" +o.datatype = "range(5, 600)" + +o = s:option(Value, "server_ip", translate("Address:")) +o.datatype = "host" +o.placeholder = '172.0.0.1' + +o = s:option(Value, "server_port", translate("Port:")) +o.datatype = "port" +o.placeholder = '80' + +o = s:option(Value, "server_login", translate("Login:")) +o.placeholder = "Device login (ID)" + +o = s:option(Value, "server_password", translate("Password:"), translate("If you don't use Password, leave the field empty")) +o.password = true +o.placeholder = "Device password" + +o = s:option(Flag, "blackbox_enable", translate("BlackBox enable:"), + translate("Blackbox makes it possible to record and store data even in the absence of a cellular signal")) +o:depends("proto","wialon") + +o = s:option(Flag, "blackbox_cycle", translate("BlackBox cycle:"), translate("Cyclic overwriting of data stored in the BlackBox")) +o:depends("proto", "wialon") + +o = s:option(Value, "blackbox_max_size", translate("BlackBox size:"), translate("Number of sentences in the BlackBox")) +o.placeholder = "default: 1000 sentence" +o.datatype = "range(1000, 5000)" +o:depends("proto","wialon") + +o = s:option(DummyValue, "_dummy", translate(" ")) +o.template = packageName .. "/blackbox" +o:depends("proto","wialon") + +o = s:option(Button, "clear", translate("Clear BlackBox"), translate("Warning! After clearing the BlackBox, GNSS data will be destroyed!")) +o.inputstyle = "remove" +o:depends("proto", "wialon") +function o.write(self, section) + local file = io.open("/usr/share/gpoint/tmp/blackbox.json", 'w') + file:write(json.stringify({["size"]=0,["max"]=1000,["data"]={}})) + file:close() +end + + +-- Tab menu settings +s = m:section(TypedSection, "service_settings") +s.addremove = false +s.anonymous = true + + +---------------------------------------------------------------------------------------------------------------- +s:tab("ya", translate("Yandex Locator"), translate("Determines the location of the mobile \ + device by the nearest Wi-Fi access points and \ + cellular base stations — without using satellite navigation systems.")) +s:tab("gpoint_filter", translate("GeoHash Filter"), translate("Filters \"DRIFT\" and \"JUMPS\" of navigation 3G/LTE dongles")) +s:tab("kalman", translate("Kalman Filter"), translate("Designed to make the route smoother. Removes \"jumps\" of navigation 3G/LTE dongles")) + +---------------------------------------------------------------------------------------------------------------- + +-- API Yandex locator +o = s:taboption("ya", Flag, "ya_enable", translate("Enable:"), translate("Enabling the Yandex locator")) +o.optional = true + +o = s:taboption("ya", ListValue, "ya_wifi", translate("Interface:"), translate("Select the Wi-Fi interface for Yandex locator")) +local iwinfo = sys.exec("iwinfo") +no_device = true +for device in string.gmatch(iwinfo, "(%S+)(%s%s%s%s%s)(%S+)") do + o:value(device, device) + no_device = false +end + +if no_device then + o:value('wnf', translate("-- Wifi not found --")) +end + +o = s:taboption("ya", Value, "ya_key", translate("API Key:"), translate("To work with the Yandex locator must use an API key")) +o.password = true +o.placeholder = "Yandex API key" + +o = s:taboption("ya", DummyValue, "ya_href") + function o.cfgvalue(self, section) + local h = "
Get Yandex API key" + return translate(h) + end +o.rawhtml = true + +-- GeoHash +o = s:taboption("gpoint_filter", Flag, "filter_enable", translate("Enable:"), translate("Enabling GpointFilter")) +o.optional = true + +o = s:taboption("gpoint_filter", Value, "filter_changes", translate("Jump:"), translate("Registration of the \"jump\" coordinates. \ + The coordinate is recognized as valid after the modem has received it more than the specified number of times.")) +o.placeholder = "" +o.datatype = "range(2, 6)" + +o = s:taboption("gpoint_filter", ListValue, "filter_hash", translate("Area:"), translate("The longer the hash length,\ + the smaller the area and the greater the accuracy of the coordinates in one area.")) +o.optional = true +o.default = 7 +for i = 1, 12 do + o:value(i, i) +end +o = s:taboption("gpoint_filter", Value, "filter_speed", translate("Speed:"), translate("Above the specified speed, the filter will be disabled")) +o.placeholder = "default 2 km/h" +o.datatype = "range(0, 150)" + +-- Kalman +o = s:taboption("kalman", Flag, "kalman_enable", translate("Enable:"), translate("Enabling KalmanFilter")) +o.optional = true +o = s:taboption("kalman", Value, "kalman_noise", translate("Noise:"), translate("Noise is a parameter you can use to alter the expected noise.\ + 1.0 is the original, and the higher it is, the more a path will be \"smoothed\"")) +o.placeholder = "" +o.datatype = "range(1.0, 30.0)" + + +return m diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/blackbox.htm b/luci-app-gpoint-main/luasrc/view/gpoint/blackbox.htm new file mode 100644 index 000000000..6723fb936 --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/blackbox.htm @@ -0,0 +1,33 @@ +<%# + Module for providing data from a mobile satellite navigation system ( mobile modems, etc.) + -= Design and Development 2021-2022 =- + Licensed to the public under the Apache License 2.0. +-%> + +<%+cbi/valueheader%> + +
+ + + +<%+cbi/valuefooter%> diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/buttons.htm b/luci-app-gpoint-main/luasrc/view/gpoint/buttons.htm new file mode 100644 index 000000000..62c2e8e61 --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/buttons.htm @@ -0,0 +1,81 @@ +<%# + Module for providing data from a mobile satellite navigation system ( mobile modems, etc.) + -= Design and Development 2021-2022 =- + Licensed to the public under the Apache License 2.0. +-%> + +<%+gpoint/css/style%> +<%+gpoint/service%> + +<%- + local packageName = "gpoint" + local serviceRunning, serviceEnabled = false, false; + + serviceEnabled = luci.sys.init.enabled(packageName) + local ubusStatus = luci.util.ubus("service", "list", { name = packageName }) + if ubusStatus and ubusStatus[packageName] then + serviceRunning = true + end + + if serviceEnabled then + btn_start_status = true + btn_action_status = true + btn_stop_status = true + btn_enable_status = false + btn_disable_status = true + else + btn_start_status = false + btn_action_status = false + btn_stop_status = false + btn_enable_status = true + btn_disable_status = false + end + if serviceRunning then + btn_start_status = false + btn_action_status = true + btn_stop_status = true + else + btn_action_status = false + btn_stop_status = false + end +-%> + +
+
+ + + + + + +   +   +   +   + + + + +
+
+ +<%-if not btn_start_status then%> + +<%-end%> +<%-if not btn_action_status then%> + +<%-end%> +<%-if not btn_stop_status then%> + +<%-end%> +<%-if not btn_enable_status then%> + +<%-end%> +<%-if not btn_disable_status then%> + +<%-end%> diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/css/style.htm b/luci-app-gpoint-main/luasrc/view/gpoint/css/style.htm new file mode 100644 index 000000000..c6b8ea961 --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/css/style.htm @@ -0,0 +1,26 @@ + diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/js/js.htm b/luci-app-gpoint-main/luasrc/view/gpoint/js/js.htm new file mode 100644 index 000000000..4f917e63d --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/js/js.htm @@ -0,0 +1,183 @@ +<%# + Module for providing data from a mobile satellite navigation system ( mobile modems, etc.) + -= Design and Development 2021-2022 =- + Licensed to the public under the Apache License 2.0. +-%> + + diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/overview.htm b/luci-app-gpoint-main/luasrc/view/gpoint/overview.htm new file mode 100644 index 000000000..4b0e49f84 --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/overview.htm @@ -0,0 +1,140 @@ +<%# + Module for providing data from a mobile satellite navigation system ( mobile modems, etc.) + -= Design and Development 2021-2022 =- + Licensed to the public under the Apache License 2.0. +-%> + +<%+gpoint/css/style%> + +<%+header%> + + + +

+
GPoint: Router Location + +
+

+ +
+
+
+
+
+ +
+

<%:Details:%>

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
<%:Longitude:%>-<%:Latitude:%>-<%:Altitude:%>-
<%:Time (UTC):%>-<%:Date:%>-<%:Satellites:%>-
<%:Hdop:%>-<%:Course:%>-<%:Speed:%>-
+
+
+ + +<%+gpoint/js/js%> + +<%+footer%> diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/service.htm b/luci-app-gpoint-main/luasrc/view/gpoint/service.htm new file mode 100644 index 000000000..d1a9f606d --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/service.htm @@ -0,0 +1,65 @@ +<%# + Module for providing data from a mobile satellite navigation system ( mobile modems, etc.) + -= Design and Development 2021-2022 =- + Licensed to the public under the Apache License 2.0. +-%> + + diff --git a/luci-app-gpoint-main/luasrc/view/gpoint/service_status.htm b/luci-app-gpoint-main/luasrc/view/gpoint/service_status.htm new file mode 100644 index 000000000..f985697c9 --- /dev/null +++ b/luci-app-gpoint-main/luasrc/view/gpoint/service_status.htm @@ -0,0 +1,83 @@ +<%# + Module for providing data from a mobile satellite navigation system ( mobile modems, etc.) + -= Design and Development 2021-2022 =- + Licensed to the public under the Apache License 2.0. +-%> + +<%+cbi/valueheader%> + + + + + + + + + + + + + + + + + + + + + + + + +
-
Application: -
Remote Server: -
Yandex Locator: -
GpointFilter: -
KalmanFilter: -
+ + + +<%+cbi/valuefooter%> diff --git a/luci-app-gpoint-main/root/etc/config/gpoint b/luci-app-gpoint-main/root/etc/config/gpoint new file mode 100644 index 000000000..07f712714 --- /dev/null +++ b/luci-app-gpoint-main/root/etc/config/gpoint @@ -0,0 +1,5 @@ +config modem_settings 'modem_settings' + +config server_settings 'server_settings' + +config service_settings 'service_settings' diff --git a/luci-app-gpoint-main/root/etc/init.d/gpoint b/luci-app-gpoint-main/root/etc/init.d/gpoint new file mode 100644 index 000000000..15322ff85 --- /dev/null +++ b/luci-app-gpoint-main/root/etc/init.d/gpoint @@ -0,0 +1,32 @@ +#!/bin/sh /etc/rc.common +# Copyright 2021-2022 Vladislav Kadulin {spanky@yandex.ru} + +USE_PROCD=1 +START=95 +STOP=01 + +CONFIGURATION=gpoint + +start_service() { + config_load "${CONFIGURATION}" + procd_open_instance + procd_append_param command /usr/share/gpoint/gpoint + procd_set_param respawn + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_set_param pidfile /var/run/gpoint.pid + procd_close_instance + echo "gpoint service start" +} + +stop_service() { + if [ -f /var/run/gpoint.pid ]; then + sessionId=$(uci get gpoint.service_settings.sessionid) + uci set gpoint.service_settings.sessionid=stop + uci commit gpoint + ubus call session destroy "{\"ubus_rpc_session\":\"${sessionId}\"}" + echo "gpoint service stop" + else + echo "gpoint not running" + fi +} diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/gpoint b/luci-app-gpoint-main/root/usr/share/gpoint/gpoint new file mode 100644 index 000000000..9961fd7f5 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/gpoint @@ -0,0 +1,229 @@ +#!/usr/bin/env lua +------------------------------------------------------------- +-- luci-app-gpoint. Gnss information dashboard for 3G/LTE dongle. +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +common_path = "/usr/share/gpoint/modems/?.lua;/usr/share/gpoint/lib/?.lua;/usr/share/gpoint/proto/?.lua;/usr/share/gpoint/lib/kalman_filter/?.lua;" +package.path = common_path .. package.path + +local config = require("config") +local socket = require("socket") +local nixio = require("nixio.fs") +local ubus = require("ubus") + + +function portActive(port) + local fport = nixio.glob("/dev/tty[A-Z][A-Z]*") + for p in fport do + if string.find(p, port) then + return true, {warning = {app = {false, "OK"}}} + end + end + return false, {warning = { + app = {true, "Port is unavailable. Check the modem connections!"}, + locator = {}, + server = {} + } + } +end + +-------------------------------------------------------------------------------- +-- all warnings: [1]-true/false, [2]-what err +-- initializing modem +-------------------------------------------------------------------------------- +local modemVendor = "" +local modemStatus, modemConfig = config.getModemData() +if not modemStatus[1] then + if string.find(modemConfig.name, "Quectel") then + modemVendor = require("que") + elseif string.find(modemConfig.name, "Sierra") then + modemVendor = require("sierra") + elseif string.find(modemConfig.name, "U-Blox") then + modemVendor = require("ublox") + elseif string.find(modemConfig.name, "Simcom") then + modemVendor = require("simcom") + elseif string.find(modemConfig.name, "Dell") then + modemVendor = require("dell") + end +end +-------------------------------------------------------------------------------- +-- initializing Remote Server, Remote Server frequency update time +-------------------------------------------------------------------------------- +local gnssProtocol, frequencyDataSend = "", nil +local serverStatus, serverConfig = config.getServerData() +if not serverStatus[1] then + if serverConfig.protocol == "wialon" then + gnssProtocol = require("wialon_ips") + elseif serverConfig.protocol == "traccar" then + gnssProtocol = require("traccar") + end + frequencyDataSend = os.time() + serverConfig.frequency +end +-------------------------------------------------------------------------------- +-- initializing Yandex Locator +-------------------------------------------------------------------------------- +local locator = "" +local locatorGPSmiss = 0 +local locatorStatus, locatorConfig = config.getLoctorData() +if not locatorStatus[1] then + locator = require("locator") +end +-------------------------------------------------------------------------------- +-- Filter coorinate GeoHash Filter +-------------------------------------------------------------------------------- +local FilterGNSS = "" +local FilterGNSSdata = { + gp = { longitude = "", latitude = "" }, + gga = { longitude = "", latitude = "" }, + gns = { longitude = "", latitude = "" }, + coordHash = 0, + changesSize = 0, + empty = true +} + +local filterStatus, filterConfig = config.getFilterData() +if not filterStatus[1] then + FilterGNSS = require("geohash") +else + FilterGNSS, FilterGNSSdata = nil, nil +end +-------------------------------------------------------------------------------- +-- Filter coorinate KalmanFilter +-------------------------------------------------------------------------------- +local kalman = {} +local kalmanFilter = "" +local kalmanIsStop = true; +local secondsSinceLastUpdate = os.time() + +local kalmanStatus, kalmanConfig = config.getKalmanData() +if not kalmanStatus[1] then + kalmanFilter = require("gps_lib") +else + kalman,kalmanFilter,kalmanIsStop,secondsSinceLastUpdate = nil, nil, nil, nil +end +-------------------------------------------------------------------------------- +-- Config Ubus +-------------------------------------------------------------------------------- +local serviceIsStart = {warning={app={true,"Service start"},locator={true,"Loading..."},server={true,"Loading..."}}} +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubus") +end + +local createUbusSession = conn:call("session", "create", {timeout = 0}) +local ubusSessionId = createUbusSession.ubus_rpc_session +config.setUbusSessionId(ubusSessionId) +conn:call("session", "set", {ubus_rpc_session = ubusSessionId, values = serviceIsStart}) +serviceIsStart = nil +conn:close() +-------------------------------------------------------------------------------- + +local timeToUpdateGNSSdata = 3 + +while true do + -- modem GNSS data + local portStatus, GnssData = portActive(modemConfig.port) + if portStatus then + GnssData = modemVendor.getGNSSdata(modemConfig.port) + if GnssData.warning.app[1] then + modemVendor.start(modemConfig.port) + end + end + + -- yandex locator + GnssData.warning.locator = locatorStatus + if portStatus and not locatorStatus[1] then + if GnssData.gp.longitude == "-" and GnssData.gp.latitude == "-" then + if locatorGPSmiss >= 3 then + local err, latitude, longitude = locator.getLocation(locatorConfig.iface, locatorConfig.key) + GnssData.warning.locator = err[1] and err or {false, "Data received from locator..."} + if not GnssData.warning.locator[1] then + GnssData.gp.latitude = latitude + GnssData.gp.longitude = longitude + GnssData.gp.date = os.date("%d.%m.%Y") + GnssData.gp.utc = os.date("%H:%M", os.time(os.date("!*t"))) -- TODO time + UTF(+3) + GnssData.gga.latitude = locator.degreesToNmea(latitude) + GnssData.gga.longitude = locator.degreesToNmea(longitude) + end + else + GnssData.warning.locator = {true, "getting navigation data..."} + locatorGPSmiss = 1 + locatorGPSmiss + end + else + locatorGPSmiss = 0 + end + end + + -- drift GpointFilter + GnssData.warning.filter = filterStatus + if portStatus and not GnssData.warning.filter[1] then + if not GnssData.warning.gga[1] or not GnssData.warning.gns[1] and GnssData.warning.locator[2] == "OK" then + if GnssData.gp.spkm ~= '-' and tonumber(GnssData.gp.spkm) < filterConfig.speed then + + local tmpHash = FilterGNSS.encode(GnssData.gp.latitude, GnssData.gp.longitude, filterConfig.hash) + + if not FilterGNSSdata.empty and tmpHash == FilterGNSSdata.coordHash and FilterGNSSdata.changesSize < filterConfig.changes then + FilterGNSSdata.changesSize = 0 + GnssData.gp.latitude, GnssData.gp.longitude = FilterGNSSdata.gp.latitude, FilterGNSSdata.gp.longitude + GnssData.gga.latitude, GnssData.gga.longitude = FilterGNSSdata.gga.latitude, FilterGNSSdata.gga.longitude + GnssData.gns.latitude, GnssData.gns.longitude = FilterGNSSdata.gns.latitude, FilterGNSSdata.gns.longitude + + elseif FilterGNSSdata.changesSize >= filterConfig.changes or FilterGNSSdata.empty then + FilterGNSSdata.gp.latitude, FilterGNSSdata.gp.longitude = GnssData.gp.latitude, GnssData.gp.longitude + if not GnssData.warning.gga[1] then + FilterGNSSdata.gga.latitude, FilterGNSSdata.gga.longitude = GnssData.gga.latitude, GnssData.gga.longitude + end + if not GnssData.warning.gns[1] then + FilterGNSSdata.gns.latitude, FilterGNSSdata.gns.longitude = GnssData.gns.latitude, GnssData.gns.longitude + end + FilterGNSSdata.changesSize, FilterGNSSdata.empty, FilterGNSSdata.coordHash = 0, false, tmpHash + else + FilterGNSSdata.changesSize = FilterGNSSdata.changesSize + 1 + end + else + FilterGNSSdata.changesSize, FilterGNSSdata.empty = 0, true + end + end + end + + + -- KalmanFilter + -- TODO enabling and disabling in parking lot + GnssData.warning.kalman = kalmanStatus + if portStatus and not GnssData.warning.kalman[1] then + if not GnssData.warning.gga[1] or not GnssData.warning.gns[1] and GnssData.warning.locator[2] == "OK" then + if kalmanIsStop then + kalman = kalmanFilter.create_velocity2d(kalmanConfig.noise) + kalmanIsStop = false + end + kalman = kalmanFilter.update_velocity2d(kalman, GnssData.gp.latitude, GnssData.gp.longitude, os.time() - secondsSinceLastUpdate) + GnssData.gp.latitude, GnssData.gp.longitude = kalmanFilter.get_lat_lon(kalman) + GnssData.gga.latitude, GnssData.gga.longitude = locator.degreesToNmea(GnssData.gp.latitude), ('0' .. locator.degreesToNmea(GnssData.gp.longitude)) + GnssData.gns.latitude, GnssData.gns.longitude = GnssData.gga.latitude, GnssData.gga.longitude + else + kalmanIsStop = true + end + end + + -- remote server + -- Filter locator to access send data to server + GnssData.warning.server = serverStatus + if locatorGPSmiss == 0 or locatorGPSmiss >= 3 then + if portStatus and not serverStatus[1] and os.time() >= frequencyDataSend then + frequencyDataSend = os.time() + (serverConfig.frequency - timeToUpdateGNSSdata) + GnssData.warning.server = gnssProtocol.sendData(GnssData, serverConfig) + end + end + + -- ubus send json data in session + conn = ubus.connect() + if conn then + conn:call("session", "set", {ubus_rpc_session = ubusSessionId, values = GnssData}) + conn:close() + end + + -- update NMEA coordinate time + socket.sleep((locatorGPSmiss == 0 or locatorGPSmiss >= 3) and timeToUpdateGNSSdata or 1) +end diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/checksum.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/checksum.lua new file mode 100644 index 000000000..6efb81744 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/checksum.lua @@ -0,0 +1,95 @@ +------------------------------------------------------------------- +-- This module is designed to receive a checksum of GNSS messages, +-- Such as crc8 and crc16 checksum. +------------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +checksum = {} + +local function decimalToHex(num) + if num == 0 then + return '0' + end + local neg = false + if num < 0 then + neg = true + num = num * -1 + end + local hexstr = "0123456789ABCDEF" + local result = "" + while num > 0 do + local n = math.mod(num, 16) + result = string.sub(hexstr, n + 1, n + 1) .. result + num = math.floor(num / 16) + end + if neg then + result = '-' .. result + end + return result +end + +local function BitXOR(a, b) + local p, c = 1, 0 + while a > 0 and b > 0 do + local ra, rb = a % 2, b % 2 + if ra ~= rb then c = c + p end + a, b, p = (a - ra) / 2, (b - rb) / 2, p * 2 + end + + if a < b then a = b end + while a > 0 do + local ra = a % 2 + if ra > 0 then c = c + p end + a, p = (a - ra) / 2, p * 2 + end + return c +end + +local function BitAND(a, b) + local p, c = 1,0 + while a > 0 and b > 0 do + local ra, rb = a%2, b%2 + if ra + rb > 1 then c = c + p end + a, b, p = (a - ra) / 2, (b - rb) / 2, p*2 + end + return c +end + +local function rshift(x, by) + return math.floor(x / 2 ^ by) +end + + +-- Checksum for NMEA data (CRC8) +function checksum.crc8(data) + local crc8 = string.sub(data, #data - 1) + data = string.sub(data, 2, #data - 3) + + local b_sum = string.byte(data, 1) + for i = 2, #data do + b_sum = BitXOR(b_sum, string.byte(data, i)) + end + + return decimalToHex(b_sum) == crc8 and true or false +end + +-- Checksum for Wialone IPS (CRC16) +function checksum.crc16(s) + assert(type(s) == 'string') + local crc16 = 0x0000 + for i = 1, #s do + local c = s:byte(i) + crc16 = BitXOR(crc16, c) + for j = 1, 8 do + local k = BitAND(crc16, 1) + crc16 = rshift(crc16, 1) + if k ~= 0 then + crc16 = BitXOR(crc16, 0xA001) + end + end + end + return decimalToHex(crc16) +end + +return checksum diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/config.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/config.lua new file mode 100644 index 000000000..86c1fcc8e --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/config.lua @@ -0,0 +1,225 @@ +------------------------------------------------------------------- +-- Module is used for point configuration and interaction with the UI +------------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local uci = require("luci.model.uci") +local sys = require("luci.sys") + +config = {} + +local CFG = uci:get_all("gpoint") + +-- Status table +local STATUS = { + APP = { + MODEM_OK = {false, "OK"}, + MODEM_ERROR = {true, "Modem error. Select modem in the settings!"}, + PORT_ERROR = {true, "Modem Port error. Select port in the settings!"} + }, + SERVER = { + SERVICE_ON = {false, "OK"}, + SERVICE_OFF = {true, "OFF"}, + IP_ERROR = {true, "Server address error. Enter the server address!"}, + PORT_ERROR = {true, "Server port error. Set the server port!"}, + LOGIN_ERROR = {true, "Login (ID) error. Specify the device login!"} + }, + LOCATOR = { + SERVICE_ON = {false, "OK"}, + SERVICE_OFF = {true, "OFF"}, + API_KEY_ERROR = {true, "Yandex Locator: API key not found!"}, + WIFI_IFACE_ERROR = {true, "Yandex Locator: Wi-Fi interface not found!"} + }, + FILTER = { + SERVICE_ON = {false, "OK"}, + SERVICE_OFF = {true, "OFF"} + } +} + +----------------------------------------------------------------------------------- +-- APP (Modem Settings) + -- 1.Checking the configuration for the presence of the modem name + -- 2.Checking the presence of the port in the configuration and whether + -- it is in the list of devices, if the device is unavailable, we return a warning + -- 3. return err + modem data (name modem and NMEA port modem) +----------------------------------------------------------------------------------- +function config.getModemData() + + local err = {} + local modem = { + name = "-", + port = "-" + } + + if not CFG.modem_settings.modem and CFG.modem_settings.modem == "mnf" then + err = STATUS.APP.MODEM_ERROR + elseif CFG.modem_settings.port and CFG.modem_settings.port == "pnf" then + err = STATUS.APP.PORT_ERROR + else + err = STATUS.APP.MODEM_OK + end + + if not err[1] then + modem.name = CFG.modem_settings.modem + modem.port = CFG.modem_settings.port + end + return err, modem +end + +----------------------------------------------------------------------------------- +-- Remote Server + -- 1.We check whether the server service is enabled or not. + -- 2.The correctness of the completed forms is checked such as address, login, port, etc ... + -- 3.We return the absence of an error and the server configuration data otherwise an error, nil ... +----------------------------------------------------------------------------------- +function config.getServerData() + + local err = {} + local server = { + address = "", + port = "", + protocol = "", + login = "", + password = "", + frequency = "", + blackbox = { + enable = "", + cycle = "", + size = 0 + } + } + + if not CFG.server_settings.server_enable then + err = STATUS.SERVER.SERVICE_OFF + elseif not CFG.server_settings.server_ip then + err = STATUS.SERVER.IP_ERROR + elseif not CFG.server_settings.server_port then + err = STATUS.SERVER.PORT_ERROR + elseif not CFG.server_settings.server_login then + err = STATUS.SERVER.LOGIN_ERROR + else + err = STATUS.SERVER.SERVICE_ON + end + + if not err[1] then + server.address = CFG.server_settings.server_ip + server.port = CFG.server_settings.server_port + server.protocol = CFG.server_settings.proto + server.login = CFG.server_settings.server_login + + if server.protocol == "wialon" then + server.password = CFG.server_settings.server_password or "NA" + server.frequency = CFG.server_settings.server_frequency or 5 + server.blackbox.enable = CFG.server_settings.blackbox_enable and true or false + server.blackbox.cycle = CFG.server_settings.blackbox_cycle and true or false + server.blackbox.size = CFG.server_settings.blackbox_max_size or 1000 + elseif server.protocol == "traccar" then + server.frequency = CFG.server_settings.server_frequency or 5 + end + + return err, server + else + return err, nil + end +end + +----------------------------------------------------------------------------------- +-- Yandex Locator + -- 1.Check Yandex Locator service enable/disable + -- 2.Check Yandex API key status enable/disable + -- 3.Check Yandex Locator interface status enable/disable +----------------------------------------------------------------------------------- +function config.getLoctorData() + + local err = {} + local locator = { + enable = false, + iface = "", + key = "" + } + + if not CFG.service_settings.ya_enable then + err = STATUS.LOCATOR.SERVICE_OFF + elseif not CFG.service_settings.ya_key then + err = STATUS.LOCATOR.API_KEY_ERROR + elseif not CFG.service_settings.ya_wifi and CFG.service_settings.ya_wifi == "wnf" then + err = STATUS.LOCATOR.WIFI_IFACE_ERROR + else + err = STATUS.LOCATOR.SERVICE_ON + end + + if not err[1] then + locator.iface = CFG.service_settings.ya_wifi + locator.key = CFG.service_settings.ya_key + return err, locator + else + return err, nil + end +end + +----------------------------------------------------------------------------------- +-- GpointFilter + -- 1. Checking for the filter library + -- 2. Check GpointFilter service enable/disable + -- 3. Make the settings, if there are none, then we apply the default settings +----------------------------------------------------------------------------------- +function config.getFilterData() + + local err = {} + local filter = { + enable = false, + changes = 0, + hash ='0', + speed = 0 + } + + if not CFG.service_settings.filter_enable then + err = STATUS.FILTER.SERVICE_OFF + else + err = STATUS.FILTER.SERVICE_ON + filter.enable = true + filter.changes = tonumber(CFG.service_settings.filter_changes or 3) + filter.hash = tostring(CFG.service_settings.filter_hash or'7') + filter.speed = tonumber(CFG.service_settings.filter_speed or 2) + end + + return err, filter +end + +----------------------------------------------------------------------------------- +-- KalmanFilter + -- 1. Checking for the kalman filter library + -- 2. Check KalmanFilter service enable/disable + -- 3. Make the settings, if there are none, then we apply the default settings +----------------------------------------------------------------------------------- +function config.getKalmanData() + + local err = {} + local filter = { + enable = false, + noise = 0 + } + + if not CFG.service_settings.kalman_enable then + err = STATUS.FILTER.SERVICE_OFF + else + err = STATUS.FILTER.SERVICE_ON + filter.enable = true + filter.noise = tonumber(CFG.service_settings.kalman_noise or 1.0) + end + + return err, filter +end + +----------------------------------------------------------------------------------- +-- Session ID + -- 1.When initializing the ubus, we write the session id to the uci to work with the UI +----------------------------------------------------------------------------------- +function config.setUbusSessionId(id) + uci:set("gpoint", "service_settings", "sessionid", id) + uci:save("gpoint") + uci:commit("gpoint") +end + +return config diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/geohash.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/geohash.lua new file mode 100644 index 000000000..de0d09c2f --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/geohash.lua @@ -0,0 +1,147 @@ +-- Geohash +-- (c) 2015 Ivan Ribeiro Rocha (ivan.ribeiro@gmail.com) +-- (c) 2022 modified by Vladislav Kadulin (spanky@yandex.ru) + +local bit = require("bit32") + +geohash = {} + +local BITS = { 16, 8, 4, 2, 1 } +local BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz" + +local NEIGHBORS = { right = { even = "bc01fg45238967deuvhjyznpkmstqrwx" }, + left = { even = "238967debc01fg45kmstqrwxuvhjyznp" }, + top = { even = "p0r21436x8zb9dcf5h7kjnmqesgutwvy" }, + bottom = { even = "14365h7k9dcfesgujnmqp0r2twvyx8zb" } } + +local BORDERS = { right = { even = "bcfguvyz" }, + left = { even = "0145hjnp" }, + top = { even = "prxz" }, + bottom = { even = "028b" } } + +NEIGHBORS.bottom.odd = NEIGHBORS.left.even +NEIGHBORS.top.odd = NEIGHBORS.right.even +NEIGHBORS.left.odd = NEIGHBORS.bottom.even +NEIGHBORS.right.odd = NEIGHBORS.top.even + +BORDERS.bottom.odd = BORDERS.left.even +BORDERS.top.odd = BORDERS.right.even +BORDERS.left.odd = BORDERS.bottom.even +BORDERS.right.odd = BORDERS.top.even + +function geohash.decode(hash) + local flip = true; + local coords = { latitude = { -90.0, 90.0 }, + longitude = { -180.0, 180.0 } } + + for i = 1, #hash do + local c = hash:sub(i, i) + local cd = BASE32:find(c) - 1 + for j = 1, 5 do + mask = BITS[j] + local tab = (flip and coords.longitude) or coords.latitude + local idx = (bit.band(cd, mask) > 0) and 1 or 2 + tab[idx] = (tab[1] + tab[2]) / 2 + flip = not flip + end + end + + for k, _ in pairs(coords) do + coords[k][3] = (coords[k][1] + coords[k][2]) / 2 + end + + return { lat = coords.latitude, lon = coords.longitude } + +end + +function geohash.encode(latitude, longitude, precision) + local lat = { -90.0, 90.0 } + local lon = { -180.0, 180.0 } + local b, ch, flip = 0, 0, true + local res = ""; + + latitude = tonumber(latitude) + longitude = tonumber(longitude) + precision = tonumber(precision) + local precision = precision or 12 + + while #res < precision do + local tab = flip and lon or lat + local grd = flip and longitude or latitude + + mid = (tab[1] + tab[2]) / 2 + + if grd > mid then + ch = bit.bor(ch, BITS[b + 1]) + tab[1] = mid + else + tab[2] = mid + end + + flip = not flip; + + if b < 4 then + b = b + 1 + else + res = res..BASE32:sub(ch + 1, ch + 1); + b, ch = 0, 0 + end + end + return res +end + +function geohash.calculate_distance(lat1, lon1, lat2, lon2) + local R = 6371000 + local r1, r2 = math.rad(lat1), math.rad(lat2) + local dlat, dlon = math.rad((lat2-lat1)), math.rad((lon2-lon1)) + local a = math.sin(dlat/2) * math.sin(dlat/2) + + math.cos(r1) * math.cos(r2) * + math.sin(dlon/2) * math.sin(dlon/2) + local c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) + return R * c +end + +function geohash.distance(hash1, hash2) + local t1, t2 = decode(hash1), decode(hash2) + return calculate_distance(coord(t1).lat, coord(t1).lon, + coord(t2).lat, coord(t2).lon) +end + +function geohash.neighbor(hash, dir) + hash = hash:lower() + local len = #hash + local last = hash:sub(len, len); + local flip = ((math.mod(len,2) == 0) and 'even') or 'odd' + local base = hash:sub(1, len - 1) + if BORDERS[dir][flip]:find(last) then + base = neighbor(base, dir) + end + local n = NEIGHBORS[dir][flip]:find(last) + return base..BASE32:sub(n, n) +end + +function geohash.neighbors(hash) + local neighbors = { top = neighbor(hash, 'top'), + bottom = neighbor(hash, 'bottom'), + right = neighbor(hash, 'right'), + left = neighbor(hash, 'left') } + neighbors.topleft = neighbor(neighbors.left, 'top'); + neighbors.topright = neighbor(neighbors.right, 'top'); + neighbors.bottomleft = neighbor(neighbors.left, 'bottom'); + neighbors.bottomright = neighbor(neighbors.right, 'bottom'); + return neighbors +end + +function geohash.coord(t) + if type(t) == 'table' then + return { lat = t.lat[3], lon = t.lon[3] } + end + return coord(decode(t)) +end + +function geohash.coord_str(t) + local t = coord(t) + return string.format("lat: %s and lon: %s", tostring(t.lat), tostring(t.lon)) +end + +return geohash \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/gps_lib.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/gps_lib.lua new file mode 100644 index 000000000..3e81f5acd --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/gps_lib.lua @@ -0,0 +1,111 @@ +local kalman = require("kalman_lib") +local matrix = require("matrix_lib") + +gps_lib = {} + +local PI = 3.14159265 +local EARTH_RADIUS_IN_MILES = 3963.1676 + +function gps_lib.set_seconds_per_timestep(kalman_filter, seconds_per_timestep) + local unit_scaler = 0.001 + kalman_filter.state_transition[1][3] = unit_scaler * seconds_per_timestep + kalman_filter.state_transition[2][4] = unit_scaler * seconds_per_timestep + return kalman_filter +end + +function gps_lib.create_velocity2d(noise) + local kalman_filter = kalman.create(4, 2) + local v2p = 0.001 + + kalman_filter.state_transition = matrix.set_identity(kalman_filter.state_transition) + kalman_filter = gps_lib.set_seconds_per_timestep(kalman_filter, 1.0) + kalman_filter.observation_model = matrix.set(kalman_filter.observation_model, + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0) + + local pos = 0.000001 + kalman_filter.process_noise_covariance = matrix.set(kalman_filter.process_noise_covariance, + pos, 0.0, 0.0, 0.0, + 0.0, pos, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) + + kalman_filter.observation_noise_covariance = matrix.set(kalman_filter.observation_noise_covariance, + pos * noise, 0.0, + 0.0, pos * noise) + + kalman_filter.state_estimate = matrix.set(kalman_filter.state_estimate, 0.0, 0.0, 0.0, 0.0) + kalman_filter.estimate_covariance = matrix.set_identity(kalman_filter.estimate_covariance) + local trillion = 1000.0 * 1000.0 * 1000.0 * 1000.0 + kalman_filter.estimate_covariance = matrix.scale(kalman_filter.estimate_covariance, trillion) + return kalman_filter +end + +function gps_lib.update_velocity2d(kalman_filter, lat, lon, seconds_since_last_timestep) + kalman_filter = gps_lib.set_seconds_per_timestep(kalman_filter, seconds_since_last_timestep) + kalman_filter.observation = matrix.set(kalman_filter.observation, lat * 1000.0, lon * 1000.0) + kalman_filter = kalman.update(kalman_filter) + return kalman_filter +end + +function gps_lib.get_lat_lon(kalman_filter) + return string.format("%0.6f", kalman_filter.state_estimate[1][1] / 1000.0), + string.format("%0.6f", kalman_filter.state_estimate[2][1] / 1000.0) +end + +function gps_lib.get_velocity(kalman_filter) + return kalman_filter.state_estimate[3][1] / (1000.0 * 1000.0), + kalman_filter.state_estimate[4][1] / (1000.0 * 1000.0) +end + +function gps_lib.get_bearing(kalman_filter) + local lat, lon = gps_lib.get_lat_lon(kalman_filter) + local delta_lat, delta_lon = gps_lib.get_velocity(kalman_filter) + + local to_radians = PI / 180.0 + lat = lat * to_radians + lon = lon * to_radians + delta_lat = delta_lat * to_radians + delta_lon = delta_lon * to_radians + + local lat1 = lat - delta_lat + local y = math.sin(delta_lon) * math.cos(lat) + local x = math.cos(lat1) * math.sin(lat) - math.sin(lat1) * math.cos(lat) * math.cos(delta_lon) + local bearing = math.atan2(y, x) + + bearing = bearing / to_radians + while bearing >= 360 do + bearing = bearing - 360 + end + while bearing < 0 do + bearing = bearing + 360 + end + return bearing +end + +function gps_lib.calculate_mph(lat, lon, delta_lat, delta_lon) + local to_radians = PI / 180 + lat = lat * to_radians + lon = lon * to_radians + delta_lat = delta_lat * to_radians + delta_lon = delta_lon * to_radians + + local lat1 = lat - delta_lat + local sin_half_dlat = math.sin(delta_lat / 2) + local sin_half_dlon = math.sin(delta_lon / 2) + + local a = sin_half_dlat * sin_half_dlat + math.cos(lat1) * math.cos(lat) * sin_half_dlon * sin_half_dlon + local radians_per_second = 2 * math.atan2(1000 * math.sqrt(a), 1000 * math.sqrt(1.0 - a)) + + local miles_per_second = radians_per_second * EARTH_RADIUS_IN_MILES + local miles_per_hour = miles_per_second * 60 * 60 + return miles_per_hour +end + +function gps_lib.get_mph(kalman_filter) + local lat, lon = gps_lib.get_lat_lon(kalman_filter) + local delta_lat, delta_lon = gps_lib.get_velocity(kalman_filter) + return gps_lib.calculate_mph(lat, lon, delta_lat, delta_lon) +end + +return gps_lib \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/kalman_lib.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/kalman_lib.lua new file mode 100644 index 000000000..207b43007 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/kalman_lib.lua @@ -0,0 +1,82 @@ + local matrix = require("matrix_lib") + +kalman_lib = {} + +function kalman_lib.create(state, observation) + local kalman = { + timestep = 0, -- K + -- These parameters define the size of the matrices. + state_dimension = state, + observation_dimension = observation, + + -- This group of matrices must be specified by the user. + state_transition = matrix.create(state, state), -- F_k + observation_model = matrix.create(observation, state), -- H_k + process_noise_covariance = matrix.create(state, state), -- Q_k + observation_noise_covariance = matrix.create(observation, observation), -- R_k + + -- The observation is modified by the user before every time step. + observation = matrix.create(observation, 1), -- z_k + + -- This group of matrices are updated every time step by the filter. + predicted_state = matrix.create(state, 1), -- x-hat_k|k-1 + predicted_estimate_covariance = matrix.create(state, state), -- P_k|k-1 + innovation = matrix.create(observation, 1), -- y-tilde_k + innovation_covariance = matrix.create(observation, observation), -- S_k + inverse_innovation_covariance = matrix.create(observation, observation), -- S_k^-1 + optimal_gain = matrix.create(state, observation), -- K_k + state_estimate = matrix.create(state, 1), -- x-hat_k|k + estimate_covariance = matrix.create(state, state), -- P_k|k + + -- This group is used for meaningless intermediate calculations. + vertical_scratch = matrix.create(state, observation), + mall_square_scratch = matrix.create(observation, observation), + big_square_scratch = matrix.create(state, state) + } + return kalman +end + +function kalman_lib.predict(kalman) + kalman.timestep = kalman.timestep + 1 + -- Predict the state + kalman.predicted_state = matrix.multiply(kalman.state_transition, kalman.state_estimate, kalman.predicted_state) + -- Predict the state estimate covariance + kalman.big_square_scratch = matrix.multiply(kalman.state_transition, kalman.estimate_covariance, kalman.big_square_scratch) + kalman.predicted_estimate_covariance = matrix.multiply_by_transpose(kalman.big_square_scratch, kalman.state_transition, kalman.predicted_estimate_covariance) + kalman.predicted_estimate_covariance = matrix.add(kalman.predicted_estimate_covariance, kalman.process_noise_covariance, kalman.predicted_estimate_covariance) + return kalman +end + +function kalman_lib.estimate(kalman) + -- Calculate innovation + kalman.innovation = matrix.multiply(kalman.observation_model, kalman.predicted_state, kalman.innovation) + kalman.innovation = matrix.subtract(kalman.observation, kalman.innovation, kalman.innovation) + -- Calculate innovation covariance + kalman.vertical_scratch = matrix.multiply_by_transpose(kalman.predicted_estimate_covariance, kalman.observation_model, kalman.vertical_scratch) + kalman.innovation_covariance = matrix.multiply(kalman.observation_model, kalman.vertical_scratch, kalman.innovation_covariance) + kalman.innovation_covariance = matrix.add(kalman.innovation_covariance, kalman.observation_noise_covariance, kalman.innovation_covariance) + -- Invert the innovation covariance. + -- Note: this destroys the innovation covariance. + -- TODO: handle inversion failure intelligently. + matrix.destructive_invert(kalman.innovation_covariance, kalman.inverse_innovation_covariance) + -- Calculate the optimal Kalman gain. + -- Note we still have a useful partial product in vertical scratch + -- from the innovation covariance. + kalman.optimal_gain = matrix.multiply(kalman.vertical_scratch, kalman.inverse_innovation_covariance, kalman.optimal_gain) + -- Estimate the state + kalman.state_estimate = matrix.multiply(kalman.optimal_gain, kalman.innovation, kalman.state_estimate) + kalman.state_estimate = matrix.add(kalman.state_estimate, kalman.predicted_state, kalman.state_estimate) + -- Estimate the state covariance + kalman.big_square_scratch = matrix.multiply(kalman.optimal_gain, kalman.observation_model, kalman.big_square_scratch) + kalman.big_square_scratch = matrix.subtract_from_identity(kalman.big_square_scratch) + kalman.estimate_covariance = matrix.multiply(kalman.big_square_scratch, kalman.predicted_estimate_covariance, kalman.estimate_covariance) + return kalman +end + +function kalman_lib.update(kalman) + kalman = kalman_lib.predict(kalman) + kalman = kalman_lib.estimate(kalman) + return kalman +end + +return kalman_lib diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/matrix_lib.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/matrix_lib.lua new file mode 100644 index 000000000..23c3dbfc4 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/kalman_filter/matrix_lib.lua @@ -0,0 +1,191 @@ +matrix_lib = {} + +function matrix_lib.create(rows, cols) + local matrix = {} + for i = 1,rows do + matrix[i] = {} + for j = 1,cols do + matrix[i][j] = 0.0 + end + end + return matrix +end + +function matrix_lib.print(matrix) + for i = 1, #matrix do + for j = 1, #matrix[i] do + io.write(matrix[i][j] .. " ") + end + io.write('\n') + end +end + +function matrix_lib.set(matrix, ...) + local k = 1 + for i = 1, #matrix do + for j = 1, #matrix[i] do + if arg[k] ~= nil then + matrix[i][j] = arg[k] + end + k = k + 1 + end + end + return matrix +end + +function matrix_lib.set_identity(matrix) + for i = 1, #matrix do + for j = 1, #matrix[i] do + matrix[i][j] = i == j and 1.0 or 0.0 + end + end + return matrix +end + +function matrix_lib.copy(matrix) + local copy = {} + for i = 1, #matrix do + copy[i] = {} + for j = 1, #matrix[i] do + copy[i][j] = matrix[i][j] + end + end + return copy +end + +function matrix_lib.add(matrix_a, matrix_b, matrix_c) + for i = 1, #matrix_a do + for j = 1, #matrix_a[i] do + matrix_c[i][j] = matrix_a[i][j] + matrix_b[i][j] + end + end + return matrix_c +end + +function matrix_lib.subtract(matrix_a, matrix_b, matrix_c) + for i = 1, #matrix_a do + for j = 1, #matrix_a[i] do + matrix_c[i][j] = matrix_a[i][j] - matrix_b[i][j] + end + end + return matrix_c +end + +function matrix_lib.subtract_from_identity(matrix) + for i = 1, #matrix do + for j = 1, #matrix[i] do + matrix[i][j] = i == j and (1.0 - matrix[i][j]) or (0.0 - matrix[i][j]) + end + end + return matrix +end + +function matrix_lib.multiply(matrix_a, matrix_b, matrix_c) + for i = 1, #matrix_c do + for j = 1, #matrix_c[i] do + matrix_c[i][j] = 0.0 + for k = 1, #matrix_a[i] do + matrix_c[i][j] = matrix_c[i][j] + (matrix_a[i][k] * matrix_b[k][j]) + end + end + end + return matrix_c +end + +function matrix_lib.multiply_by_transpose(matrix_a, matrix_b, matrix_c) + for i = 1, #matrix_c do + for j = 1, #matrix_c[i] do + matrix_c[i][j] = 0.0 + for k = 1, #matrix_a[1] do + matrix_c[i][j] = matrix_c[i][j] + (matrix_a[i][k] * matrix_b[j][k]) + end + end + end + return matrix_c +end + +function matrix_lib.transpose(matrix_input, matrix_output) + for i = 1, #matrix_input do + for j = 1, #matrix_input[i] do + matrix_output[j][i] = matrix_input[i][j] + end + end + return matrix_output +end + +function matrix_lib.equal(matrix_a, matrix_b, tolerance) + for i = 1, #matrix_a do + for j = 1, #matrix_a[i] do + if math.abs(matrix_a[i][j] - matrix_b[i][j]) > tolerance then + return false + end + end + end + return true +end + +function matrix_lib.scale(matrix, scalar) + for i = 1, #matrix do + for j = 1, #matrix[i] do + matrix[i][j] = matrix[i][j] * scalar + end + end + return matrix +end + +function matrix_lib.swap_rows(matrix, r1, r2) + local tmp = matrix[r1] + matrix[r1] = matrix[r2] + matrix[r2] = tmp + return matrix +end + +function matrix_lib.scale_row(matrix, r, scalar) + for i = 1, #matrix do + matrix[r][i] = matrix[r][i] * scalar + end + return matrix +end + +function matrix_lib.shear_row(matrix, r1, r2, scalar) + for i = 1, #matrix do + matrix[r1][i] = matrix[r1][i] + (scalar * matrix[r2][i]) + end + return matrix +end + +function matrix_lib.destructive_invert(matrix_input, matrix_output) + matrix_output = matrix_lib.set_identity(matrix_output) + for i = 1, #matrix_input do + if matrix_input[i][i] == 0.0 then + local j + for j = i + 1, #matrix_input do + if matrix_input[r][i] ~= 0.0 then + return + end + end + + if j == #matrix_input then + return + end + + matrix_input = matrix_lib.swap_rows(matrix_input, i, j) + matrix_output = matrix_lib.swap_rows(matrix_output, i, j) + end + + local scalar = 1.0 / matrix_input[i][i] + matrix_input = matrix_lib.scale_row(matrix_input, i, scalar) + matrix_output = matrix_lib.scale_row(matrix_output, i, scalar) + + for r = 1, #matrix_input do + if i ~= r then + local shear_needed = -matrix_input[r][i] + matrix_input = matrix_lib.shear_row(matrix_input, r, i, shear_needed) + matrix_output = matrix_lib.shear_row(matrix_output, r, i, shear_needed) + end + end + end + return matrix_input, matrix_output +end + +return matrix_lib \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/locator.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/locator.lua new file mode 100644 index 000000000..cea13b093 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/locator.lua @@ -0,0 +1,78 @@ +------------------------------------------------------------------- +-- Module is designed to work with the Yandex Locator API +-- (WiFi is required!) +------------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local json = require("luci.jsonc") +local sys = require("luci.sys") +local iwinfo = require("iwinfo") + +locator = {} + +local function configJSON(jsonData, iface, key) + jsonData.common.api_key = key + local inter = iwinfo.type(iface) + local scanlist = iwinfo[inter].scanlist(iface) + for _, v in pairs(scanlist) do + v.bssid = string.gsub(v.bssid, ':', '') + table.insert(jsonData.wifi_networks, {["mac"] = v.bssid, ["signal_strength"] = v.signal}) + end +end + +local function request(curl, jsonData) + curl = curl .. json.stringify(jsonData) .. '\'' + local res = sys.exec(curl) + if res == "" then + res = "{\"error\": {\"message\":\"No internet connection\"}}" + end + return json.parse(res) +end + +-- Converter from degrees to NMEA data. +function locator.degreesToNmea(coord) + local degrees = math.floor(coord) + coord = math.abs(coord) - degrees + local sign = coord < 0 and "-" or "" + return sign .. string.format("%02i%02.5f", degrees, coord * 60.00) +end + +-- Getting data coordinates via Yandex API +function locator.getLocation(iface_name, api_key) + local curl = "curl -X POST 'http://api.lbs.yandex.net/geolocation' -d 'json=" + local jsonData = { + wifi_networks = {}, + common = { + version = "1.0", + api_key = "" + } + } + + configJSON(jsonData, iface_name, api_key) + local location = request(curl, jsonData) + local err = {false, "OK"} + local latitude = "" + local longitude = "" + local altitude = "" + + if location.error then + err = {true, location.error.message} + end + + if location.position then + if tonumber(location.position.precision) >= 100000 then + err = {true, "Bad precision"} + else + latitude = string.format("%0.8f", location.position.latitude) + longitude = string.format("%0.8f", location.position.longitude) + if latitude == "" or longitude == "" then + err = {true, "Bad data..."} + end + end + end + + return err, latitude, longitude +end + +return locator diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/nmea.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/nmea.lua new file mode 100644 index 000000000..698ab8000 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/nmea.lua @@ -0,0 +1,385 @@ +------------------------------------------------------------- +-- This module is designed to extract data from NMEA messages. +-- All data is combined into a table "GnssData". +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local uci = require("luci.model.uci") +local serial = require("serial") +local checksum = require("checksum") + + +local nmea = {} + +-- Table for navigation data +local function createGnssForm() + local GnssForm = { + warning = { + app = {true, ""}, + gga = {true, ""}, + rmc = {true, ""}, + vtg = {true, ""}, + gsa = {true, ""}, + gp = {true, ""}, + gns = {true, ""}, + server = {true, ""}, + locator = {true, ""} + }, + gp = { longitude = "-", latitude = "-"}, + gga = { longitude = "-", latitude = "-"} + } + return GnssForm +end + +--Converting coordinates from the NMEA protocol to degrees +local function nmeaCoordinatesToDouble(coord) + local deg = math.floor(coord / 100) + return deg + (coord - 100 * deg) / 60 +end + +--We are looking for the desired data line in the line received from the device +local function findInResp(data, begin) + local err = true + local b = string.find(data, begin) + local e = string.find(data, "\r\n", b) + + if b and e then + err = false + else + b, e = nil, nil + end + return err, b, e +end + +-- message parsing, checksum checking +local function getCropData(data, msg) + local err, b, e = findInResp(data, msg) + if not err then + data = string.gsub(string.sub(data, b, e), '%c', "") + if checksum.crc8(data) then + data = string.gsub(data, msg, '', 1) + data = string.gsub(data, "*%d+%w+", '', 1) + err = {false, "OK"} + else + err = {true, "Checksum error"} + data = nil + end + else + err = {true, "No data found"} + data = nil + end + return err, data +end + +-- Creating a table with data before adding data to a single space +function doTable(data, keys) + local parseData = {} + + while string.find(data, ',,') do + data = string.gsub(data, ',,', ",-,") + end + + if string.sub(data, 1, 1) == ',' then + data = '-' .. data + end + + local i = 1 + for val in string.gmatch(data, "[^,]+") do + parseData[keys[i]] = val + i = i + 1 + end + return parseData +end + +-- The function of searching the time zone by the received coordinates +local function findTimeZone(time, date, lon) + local datetime = { year,month,day,hour,min,sec } + local timeZone = uci:get("gpoint", "modem_settings", "timezone") + + -- calculate the time zone by coordinates + if timeZone == nil or timeZone == "auto" then + timeZone = math.floor((tonumber(lon) + (7.5 * (tonumber(lon) > 0 and 1.0 or -1.0))) / 15.0) + end + + datetime.hour, datetime.min, datetime.sec = string.match(time, "(%d%d)(%d%d)(%d%d)") + datetime.day, datetime.month, datetime.year = string.match(date,"(%d%d)(%d%d)(%d%d)") + datetime.year = "20" .. datetime.year -- Someone change this to 21 in the 2100 year + + --we request the unix time and then add the time zone + local unix = os.time(datetime) + unix = unix + ((math.floor(tonumber(timeZone) * 100)) % 100) * 36 + return unix + math.floor(tonumber(timeZone)) * 3600 +end + +-- Add 0 for the time and date values if < 10 +local function addZero(val) + return tonumber(val) > 9 and tostring(val) or '0' .. tostring(val) +end + +-- If there is no data, the default values of the table are dashed +local function addDash(data) + local dashData = {} + for i=1, #data do + dashData[data[i]] = '-' + end + return dashData +end + +--------------------------------------------------------------------------------------------------------------- +-- GGA - Global Positioning System Fix Data +local function getGGA(GnssData, resp) + GnssData.gga = { + "utc", -- UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds. + "latitude", -- Latitude, dd is degrees, mm.mm is minutes + "ne", -- N or S (North or South) + "longitude", -- Longitude, dd is degrees, mm.mm is minutes + "ew", -- E or W (East or West) + "qual", -- GPS Quality Indicator (non null) + "sat", -- Number of satellites in use, 00 - 12 + "hdp", -- Horizontal Dilution of precision (meters) + "alt", -- Antenna Altitude above/below mean-sea-level (geoid) (in meters) + "ualt", -- Units of antenna altitude, meters + "gsep", -- Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level + "ugsep", -- Units of geoidal separation, meters + "age", -- Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, null field when DGPS is not used + "drs" -- Differential reference station ID, 0000-1023 + } + + local err, gga = getCropData(resp, "$GPGGA,") + if not err[1] and string.gsub(gga, ',', '') ~= '0' + and string.sub(gga, string.find(gga, ',') + 1, string.find(gga, ',') + 1) ~= ',' then + GnssData.gga = doTable(gga, GnssData.gga) + GnssData.warning.gga = {false, "OK"} + else + GnssData.gga = addDash(GnssData.gga) + GnssData.warning.gga = err[1] and err or {true, "Bad GGA data"} + end +end + +-- RMC - Recommended Minimum Navigation Information +local function getRMC(GnssData, resp) + GnssData.rmc = { + "utc", -- UTC of position fix, hh is hours, mm is minutes, ss.ss is seconds. + "valid", -- Status, A = Valid, V = Warning + "latitude", -- Latitude, dd is degrees. mm.mm is minutes. + "ns", -- N or S + "longitude", -- Longitude, ddd is degrees. mm.mm is minutes. + "ew", -- E or W + "knots", -- Speed over ground, knots + "tmgdt", -- Track made good, degrees true + "date", -- Date, ddmmyy + "mv", -- Magnetic Variation, degrees + "ewm", -- E or W + "nstat", -- Nav Status (NMEA 4.1 and later) A=autonomous, D=differential, E=Estimated, -> + -- M=Manual input mode N=not valid, S=Simulator, V = Valid + "sc" --checksum + } + + local err, rmc = getCropData(resp, "$GPRMC,") + if not err[1] and string.find(rmc, ",A,") then + GnssData.rmc = doTable(rmc, GnssData.rmc) + GnssData.warning.rmc = {false, "OK"} + else + GnssData.rmc = addDash(GnssData.rmc) + GnssData.warning.rmc = err[1] and err or {true, "Bad RMC data"} + end +end + +-- VTG - Track made good and Ground speed +local function getVTG(GnssData, resp) + GnssData.vtg = { + "course_t", -- Course over ground, degrees True + 't', -- T = True + "course_m", -- Course over ground, degrees Magnetic + 'm', -- M = Magnetic + "knots", -- Speed over ground, knots + 'n', -- N = Knots + "speed", -- Speed over ground, km/hr + 'k', -- K = Kilometers Per Hour + "faa" -- FAA mode indicator (NMEA 2.3 and later) + } + + local err, vtg = getCropData(resp, "$GPVTG,") + if not err[1] and (string.find(vtg, 'A') or string.find(vtg, 'D')) then + GnssData.vtg = doTable(vtg, GnssData.vtg) + GnssData.warning.vtg = {false, "OK"} + else + GnssData.vtg = addDash(GnssData.vtg) + GnssData.warning.vtg = err[1] and err or {true, "Bad VTG data"} + end +end + +--GSA - GPS DOP and active satellites +local function getGSA(GnssData, resp) + GnssData.gsa = { + "smode", -- Selection mode: M=Manual, forced to operate in 2D or 3D, A=Automatic, 2D/3D + "mode", -- Mode (1 = no fix, 2 = 2D fix, 3 = 3D fix) + "id1", -- ID of 1st satellite used for fix + "id2", -- ID of 2nd satellite used for fix + "id3", -- ID of 3rd satellite used for fix + "id4", -- ID of 4th satellite used for fix + "id5", -- ID of 5th satellite used for fix + "id6", -- ID of 6th satellite used for fix + "id7", -- ID of 7th satellite used for fix + "id8", -- ID of 8th satellite used for fix + "id9", -- ID of 9th satellite used for fix + "id10", -- ID of 10th satellite used for fix + "id11", -- ID of 11th satellite used for fix + "id12", -- ID of 12th satellite used for fix + "pdop", -- PDOP + "hdop", -- HDOP + "vdop", -- VDOP + "sc" -- checksum + } + + local err, gsa = getCropData(resp, "$GPGSA,") + if not err[1] and string.find(gsa, '2') then + GnssData.gsa = doTable(gsa, GnssData.gsa) + GnssData.warning.gsa = {false, "OK"} + else + GnssData.gsa = addDash(GnssData.gsa) + GnssData.warning.gsa = err[1] and err or {true, "Bad GSA data"} + end +end + +--GNS - GLONAS Fix data +local function getGNS(GnssData, resp) + GnssData.gns = { + "utc", -- UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds. + "latitude", -- Latitude, dd is degrees, mm.mm is minutes + "ne", -- N or S (North or South) + "longitude", -- Longitude, dd is degrees, mm.mm is minutes + "ew", -- E or W (East or West) + "mi", -- Mode indicator (non-null) + "sat", -- Total number of satellites in use, 00-99 + "hdp", -- Horizontal Dilution of Precision, HDOP + "alt", -- Antenna Altitude above/below mean-sea-level (geoid) (in meters) + "gsep", -- Goeidal separation meters + "age", -- Age of differential data + "drs", -- Differential reference station ID + "nstat" --Navigational status (optional) S = Safe C = Caution U = Unsafe V = Not valid for navigation + } + + local err, gns = getCropData(resp, "$GNGNS,") + if not err[1] and string.gsub(gns, ',', '') ~= '0' and not string.find(gns, "NNN") then + GnssData.gns = doTable(gns, GnssData.gns) + GnssData.warning.gns = {false, "OK"} + else + GnssData.gns = addDash(GnssData.gns) + GnssData.warning.gns = err[1] and err or {true, "Bad GNS data"} + end +end + +-- Prepares data for the web application (Some custom data) +local function getGPoint(GnssData, resp) + GnssData.gp = { + longitude = '-', + latitude = '-', + altitude = '-', + utc = '-', + date = '-', + nsat = '-', + hdop = '-', + cog = '-', + spkm = '-', + unix = '-' + } + + local err = {true, ""} + local GpsOrGlonas = false + + if not GnssData.warning.gga[1] then + GpsOrGlonas = GnssData.gga + elseif not GnssData.warning.gns[1] then + GpsOrGlonas = GnssData.gns + else + err[2] = "GGA: " .. GnssData.warning.gga[2] .. ' ' .. "GNS: " .. GnssData.warning.gns[2] .. ' ' + end + + if GpsOrGlonas then + GnssData.gp.latitude = string.format("%0.6f", nmeaCoordinatesToDouble(GpsOrGlonas.latitude)) + GnssData.gp.longitude = string.format("%0.6f", nmeaCoordinatesToDouble(GpsOrGlonas.longitude)) + GnssData.gp.altitude = GpsOrGlonas.alt + GnssData.gp.nsat = GpsOrGlonas.sat + GnssData.gp.hdop = GpsOrGlonas.hdp + end + + if not GnssData.warning.vtg[1] then + GnssData.gp.cog = GnssData.vtg.course_t + GnssData.gp.spkm = GnssData.vtg.speed + else + err[2] = err[2] .. "VTG: " .. GnssData.warning.vtg[2] .. ' ' + end + + if not GnssData.warning.rmc[1] then + local unixTime = findTimeZone(GnssData.rmc.utc, GnssData.rmc.date, nmeaCoordinatesToDouble(GnssData.rmc.longitude)) + local dateTime = os.date("*t", unixTime) + + GnssData.gp.utc = string.format("%s:%s", addZero(dateTime.hour), addZero(dateTime.min)) + GnssData.gp.date = string.format("%s.%s.%d", addZero(dateTime.day), addZero(dateTime.month), dateTime.year) + GnssData.gp.unix = unixTime + else + err[2] = err[2] .. "RMC: " .. GnssData.warning.rmc[2] + end + + if GnssData.warning.gga[1] and GnssData.warning.gns[1] and GnssData.warning.vtg[1] and GnssData.warning.rmc[1] then + err = {false, "Updating data..."} + end + + if err[2] == "" then + err = {false, "OK"} + end + + GnssData.warning.gp = err +end + +------------------------------------------------------ +-- Get a certain kind of NMEA data (data parsing) +------------------------------------------------------ + +function nmea.getData(line, port) + GnssData = createGnssForm() + GnssData.warning.app, resp = serial.read(port) + + if line == "GP" then + getGGA(GnssData, resp) + getGNS(GnssData, resp) + getRMC(GnssData, resp) + getVTG(GnssData, resp) + getGPoint(GnssData, resp) + elseif line == "GGA" then + getGGA(GnssData, resp) + elseif line == "GNS" then + getGNS(GnssData, resp) + elseif line == "RMC" then + getRMC(GnssData, resp) + elseif line == "VTG" then + getVTG(GnssData, resp) + elseif line == "GSA" then + getGSA(GnssData, resp) + else + GnssData.warning.app = {true, "Bad argument..."} + end + return GnssData +end + +------------------------------------------------------ +-- parsing all NMEA data +------------------------------------------------------ + +function nmea.getAllData(port) + GnssData = createGnssForm() + GnssData.warning.app, resp = serial.read(port) + + getGGA(GnssData, resp) + getGNS(GnssData, resp) + getRMC(GnssData, resp) + getVTG(GnssData, resp) + getGSA(GnssData, resp) -- rarely used + getGPoint(GnssData, resp) + + return GnssData +end + +return nmea diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/lib/serial.lua b/luci-app-gpoint-main/root/usr/share/gpoint/lib/serial.lua new file mode 100644 index 000000000..ca6dc1381 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/lib/serial.lua @@ -0,0 +1,73 @@ +------------------------------------------------------------------- +-- Wrapper for working with a modem via serial port +------------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local rs232 = require("luars232") + +local serial = {} + +local function configSerial(port) + assert(port:set_baud_rate(rs232.RS232_BAUD_115200) == rs232.RS232_ERR_NOERROR) + assert(port:set_parity(rs232.RS232_PARITY_NONE) == rs232.RS232_ERR_NOERROR) + assert(port:set_data_bits(rs232.RS232_DATA_8) == rs232.RS232_ERR_NOERROR) + assert(port:set_stop_bits(rs232.RS232_STOP_1) == rs232.RS232_ERR_NOERROR) + assert(port:set_flow_control(rs232.RS232_FLOW_OFF) == rs232.RS232_ERR_NOERROR) +end + +-- write data from modem (AT PORT) +function serial.write(serial_port, command) + local err, port = rs232.open(serial_port) + if err ~= rs232.RS232_ERR_NOERROR then + err = {true, "Error opening AT port"} + assert(port:close() == rs232.RS232_ERR_NOERROR) + return err + end + configSerial(port) + + local err, len_written = port:write(command .. "\r\n") + if err ~= rs232.RS232_ERR_NOERROR then + err = {true, "Error writing AT port"} + assert(port:close() == rs232.RS232_ERR_NOERROR) + return err + end + + err = {false, "OK"} + assert(port:close() == rs232.RS232_ERR_NOERROR) + return err +end + +-- read data from modem (GNSS PORT) +function serial.read(serial_port) + local err, port = rs232.open(serial_port) + if err ~= rs232.RS232_ERR_NOERROR then + err = {true, "Error opening GNSS port"} + assert(port:close() == rs232.RS232_ERR_NOERROR) + return err, '' + end + configSerial(port) + + local READ_LEN = 1024 -- Read byte form GNSS port + local TIMEOUT = 1000 -- Timeout reading in miliseconds + + local serialData, err, read_data = {}, "", "" + while READ_LEN > 0 do + err, read_data = port:read(1, TIMEOUT) + if err ~= rs232.RS232_ERR_NOERROR then + err = {true, "Error reading GNSS port. Updating data or searching for satellites..."} + assert(port:close() == rs232.RS232_ERR_NOERROR) + return err, "" + end + if read_data ~= nil then + table.insert(serialData, read_data) + READ_LEN = READ_LEN - 1 + end + end + assert(port:close() == rs232.RS232_ERR_NOERROR) + + err = {false, "OK"} + return err, table.concat(serialData) +end + +return serial diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/modems/dell.lua b/luci-app-gpoint-main/root/usr/share/gpoint/modems/dell.lua new file mode 100644 index 000000000..a28b63dfe --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/modems/dell.lua @@ -0,0 +1,44 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + + +local nmea = require("nmea") +local serial = require("serial") +local nixio = require("nixio.fs") + +local dell = {} + +local DELL_BEGIN_GPS = "AT+GPS=1" +local DELL_END_GPS = "AT+GPS=0" + +-- automatic activation of the NMEA port for data transmission +function dell.start(port) + local p = tonumber(string.sub(port, #port)) + 1 + p = string.gsub(port, '%d', tostring(p)) + local error, resp = true, { + warning = { + app = {true, "Port is unavailable. Check the modem connections!"}, + locator = {}, + server = {} + } + } + -- DELL DW5821 series default NMEA /dev/ttyUSB2 + local fport = nixio.glob("/dev/tty[A-Z][A-Z]*") + for name in fport do + if string.find(name, p) then + error, resp = serial.write(p, DELL_BEGIN_GPS) + end + end + return error, resp +end +-- stop send data to NMEA port +function dell.stop(port) + error, resp = serial.write(port, DELL_END_GPS) + return error, resp +end +-- get GNSS data for application +function dell.getGNSSdata(port) + return nmea.getAllData(port) +end + +return dell diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/modems/que.lua b/luci-app-gpoint-main/root/usr/share/gpoint/modems/que.lua new file mode 100644 index 000000000..f837ca90e --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/modems/que.lua @@ -0,0 +1,41 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + +local nmea = require("nmea") +local serial = require("serial") +local nixio = require("nixio.fs") + +local que = {} + +local QUECTEL_BEGIN_GPS = "AT+QGPS=1" +local QUECTEL_END_GPS = "AT+QGPSEND" + +-- automatic activation of the NMEA port for data transmission +function que.start(port) + local p = tonumber(string.sub(port, #port)) + 1 + p = string.gsub(port, '%d', tostring(p)) + local error, resp = true, {warning = { + app = {true, "Port is unavailable. Check the modem connections!"}, + locator = {}, + server = {} + } + } + local fport = nixio.glob("/dev/tty[A-Z][A-Z]*") + for name in fport do + if string.find(name, p) then + error, resp = serial.write(p, QUECTEL_BEGIN_GPS) + end + end + return error, resp +end +-- stop send data to NMEA port +function que.stop(port) + error, resp = serial.write(port, QUECTEL_END_GPS) + return error, resp +end +-- get GNSS data for application +function que.getGNSSdata(port) + return nmea.getAllData(port) +end + +return que \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/modems/sierra.lua b/luci-app-gpoint-main/root/usr/share/gpoint/modems/sierra.lua new file mode 100644 index 000000000..5d0ba6cae --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/modems/sierra.lua @@ -0,0 +1,40 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + + +local nmea = require("nmea") +local serial = require("serial") +local nixio = require("nixio.fs") + +local sierra = {} + +local SIERRA_BEGIN_GPS = "$GPS_START" +local SIERRA_END_GPS = "$GPS_STOP" + +-- automatic activation of the NMEA port for data transmission +function sierra.start(port) + local error, resp = true, {warning = { + app = {true, "Port is unavailable. Check the modem connections!"}, + locator = {}, + server = {} + } + } + local fport = nixio.glob("/dev/tty[A-Z][A-Z]*") + for name in fport do + if string.find(name, port) then + error, resp = serial.write(port, SIERRA_BEGIN_GPS) + end + end + return error, resp +end +-- stop send data to NMEA port +function sierra.stop(port) + error, resp = serial.write(port, SIERRA_END_GPS) + return error, resp +end +-- get GNSS data for application +function sierra.getGNSSdata(port) + return nmea.getAllData(port) +end + +return sierra \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/modems/simcom.lua b/luci-app-gpoint-main/root/usr/share/gpoint/modems/simcom.lua new file mode 100644 index 000000000..02319b4e6 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/modems/simcom.lua @@ -0,0 +1,44 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + + +local nmea = require("nmea") +local serial = require("serial") +local nixio = require("nixio.fs") + +local simcom = {} + +local SIMCOM_BEGIN_GPS = "AT+CGPS=1,1" +local SIMCOM_END_GPS = "AT+CGPS=0,1" + +-- automatic activation of the NMEA port for data transmission +function simcom.start(port) + local p = tonumber(string.sub(port, #port)) + 1 + p = string.gsub(port, '%d', tostring(p)) + local error, resp = true, { + warning = { + app = {true, "Port is unavailable. Check the modem connections!"}, + locator = {}, + server = {} + } + } + -- SIM7600 series default NMEA /dev/ttyUSB1 + local fport = nixio.glob("/dev/tty[A-Z][A-Z]*") + for name in fport do + if string.find(name, p) then + error, resp = serial.write(p, SIMCOM_BEGIN_GPS) + end + end + return error, resp +end +-- stop send data to NMEA port +function simcom.stop(port) + error, resp = serial.write(port, SIMCOM_END_GPS) + return error, resp +end +-- get GNSS data for application +function simcom.getGNSSdata(port) + return nmea.getAllData(port) +end + +return simcom diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/modems/ublox.lua b/luci-app-gpoint-main/root/usr/share/gpoint/modems/ublox.lua new file mode 100644 index 000000000..9bf3898fa --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/modems/ublox.lua @@ -0,0 +1,22 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + + +local nmea = require("nmea") + +local ublox = {} + +-- Wrapper over the interface, the module does not need an implementation +function ublox.start(port) + return false, {warning = {app = {false, "GOOD!"}, locator = {}, server = {}}} +end + +function ublox.stop(port) + return {false, "OK"} +end +-- get GNSS data for application +function ublox.getGNSSdata(port) + return nmea.getAllData(port) +end + +return ublox \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/proto/traccar.lua b/luci-app-gpoint-main/root/usr/share/gpoint/proto/traccar.lua new file mode 100644 index 000000000..148da57f7 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/proto/traccar.lua @@ -0,0 +1,31 @@ +------------------------------------------------------------- +-- Traccar Client use this protocol to report GPS data to the server side. +-- OsmAnd Live Tracking web address format: +-- http://demo.traccar.org:5055/?id=123456&lat={0}&lon={1}×tamp={2}&hdop={3}&altitude={4}&speed={5} +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local http = require("socket.http") + +local trackcar = {} + +local function OsmAnd(GnssData, serverConfig) + local unix = GnssData.warning.rmc[1] and os.time() or GnssData.gp.unix + return string.format("http://%s:%s/?id=%s&lat=%s&lon=%s×tamp=%s&hdop=%s&altitude=%s&speed=%s&satellites=%s", + serverConfig.address, serverConfig.port, serverConfig.login, + GnssData.gp.latitude or '-', GnssData.gp.longitude or '-', + unix or '-', GnssData.gp.hdop or '-', + GnssData.gp.altitude or '-', GnssData.gp.spkm or '-', + GnssData.gp.nsat or '-') +end + +-- Send data to server side +function trackcar.sendData(GnssData, serverConfig) + local data = OsmAnd(GnssData, serverConfig) + http.TIMEOUT = 0.5 + http.request{ method = "POST", url = data} + return {false, "OK"} +end + +return trackcar \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips.lua b/luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips.lua new file mode 100644 index 000000000..f6ffedd76 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips.lua @@ -0,0 +1,160 @@ +------------------------------------------------------------- +-- A module for working with the "WIALON IPS" navigation protocol. +-- This module prepares and sends data to a remote server. +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local json = require("luci.jsonc") +local checksum = require("checksum") +local blackbox = require("wialon_ips_boof") +local socket = require("socket") +local tcp = assert(socket.tcp()) + +local wialon_ips = {} + +-- Abbreviated data package +local function shortData(GnssData) + local SD = {"NA","NA","NA","NA","NA","NA","NA","NA","NA","NA"} + if not GnssData.warning.rmc[1] then + SD[1], SD[2] = GnssData.rmc.date, GnssData.rmc.utc + end + if not GnssData.warning.gga[1] then + -- Lat2[4], Lon2[6] - NA + SD[3], SD[5] = GnssData.gga.latitude, GnssData.gga.longitude + SD[9], SD[10] = GnssData.gga.alt, GnssData.gga.sat + elseif not GnssData.warning.gns[1] then + SD[3], SD[5] = GnssData.gns.latitude, GnssData.gns.longitude + SD[9], SD[10] = GnssData.gns.alt, GnssData.gns.sat + elseif not GnssData.warning.locator[1] then + SD[1], SD[2] = os.date("%d%m%y"), os.date("%H%M%S", os.time(os.date("!*t"))) .. ".00" + SD[3], SD[5] = GnssData.gga.latitude, '0' .. GnssData.gga.longitude + end + if not GnssData.warning.vtg[1] then + SD[7], SD[8] = GnssData.vtg.speed, GnssData.vtg.course_t + end + SD[11] = checksum.crc16(table.concat(SD, ";") .. ';') + return "#SD#" .. table.concat(SD, ";") .. "\r\n" +end + +-- Extended Data package with CRC16 +local function bigData(GnssData, params) + local D = {"NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","","NA","NA"} + if not GnssData.warning.rmc[1] then + D[1], D[2] = GnssData.rmc.date, GnssData.rmc.utc + end + if not GnssData.warning.gga[1] then + -- Lat2[4], Lon2[6] - NA + D[3], D[5] = GnssData.gga.latitude, GnssData.gga.longitude + D[9], D[10] = GnssData.gga.alt, GnssData.gga.sat + D[11] = GnssData.gga.hdp + elseif not GnssData.warning.gns[1] then + D[3], D[5] = GnssData.gns.latitude, GnssData.gns.longitude + D[9], D[10] = GnssData.gns.alt, GnssData.gns.sat + D[11] = GnssData.gns.hdp + elseif not GnssData.warning.locator[1] then + D[1], D[2] = os.date("%d%m%y"), os.date("%H%M%S", os.time(os.date("!*t"))) .. ".00" + D[3], D[5] = GnssData.gga.latitude, '0' .. GnssData.gga.longitude + D[16] = string.format("%s:%s:%s", "yandex", '3', "Data from wi-fi scan") + end + if not GnssData.warning.vtg[1] then + D[7], D[8] = GnssData.vtg.speed, GnssData.vtg.course_t + end + if params then + -- TODO PARAMS WITH YA LOCATOR + D[16] = string.format("%s:%s:%s", params[1], params[2], params[3]) + end + D[17] = checksum.crc16(table.concat(D, ";") .. ';') + return "#D#" .. table.concat(D, ";") .. "\r\n" +end + +-- Response from the server to the message +local function handlErr(resp) + local ERROR_CODE = { + ["#AL#1"] = "OK", + ["#ASD#1"] = "OK", + ["#AD#1"] = "OK", + ["#AL#0"] = "Rejected connection", + ["#AL#01"] = "Password verification error", + ["#AL#10"] = "Checksum verification error", + ["#ASD#-1"] = "Package structure error", + ["#ASD#0"] = "Incorrect time", + ["#ASD#10"] = "Error getting coordinates", + ["#ASD#11"] = "Error getting speed, course, or altitude", + ["#ASD#12"] = "Error getting the number of satellites", + ["#ASD#13"] = "Checksum verification error", + ["#AD#-1"] = "Package structure error", + ["#AD#0"] = "Incorrect time", + ["#AD#10"] = "Error getting coordinates", + ["#AD#11"] = "Error getting speed, course, or altitude", + ["#AD#12"] = "Error in getting the number of satellites or HDOP", + ["#AD#13"] = "Error getting Inputs or Outputs", + ["#AD#14"] = "Error receiving ADC", + ["#AD#15"] = "Error getting additional parameters", + ["#AD#16"] = "Checksum verification error" + } + + for k, v in pairs(ERROR_CODE) do + if k == resp then + return v + end + end + return "Unknown error" +end + +-- Login Package +local function login(imei, pass) + local L = {} + L[1], L[2], L[3] = "2.0", imei, pass + L[4] = checksum.crc16(table.concat(L, ";") .. ';') + return "#L#" .. table.concat(L, ";") .. "\r\n" +end + +-- Send data to server side +function wialon_ips.sendData(GnssData, serverConfig) + local r, s, e + local DATA_OK = "OK" + local err = {false, DATA_OK} + local wialonData = bigData(GnssData) + + -- Data is missing, there is nothing to send + if string.find(wialonData, "DB2D") then + return {true, "No data to send"} + end + + s, e = tcp:connect(serverConfig.address, serverConfig.port) + if not s then + blackbox.set(GnssData, serverConfig) + tcp:close() + return {true, e} + end + tcp:settimeout(2) + tcp:send(login(serverConfig.login, serverConfig.password) .. '\n') + r, e = tcp:receive() + + if handlErr(r) == DATA_OK then + tcp:send(wialonData) + r, e = tcp:receive() + if handlErr(r) == DATA_OK then + local booferSize, booferData = blackbox.get(serverConfig) + if booferSize > 0 then + tcp:send(booferData .. '\n') + r, e = tcp:receive() + local sentSize = string.gsub(r, "%D", "") + if tonumber(sentSize) and tonumber(sentSize) >= booferSize then + blackbox.clean(serverConfig) + end + end + elseif handlErr(r) ~= DATA_OK then + blackbox.set(GnssData, serverConfig) + end + else + err = {true, handlErr(r)} + blackbox.set(GnssData, serverConfig) + end + + tcp:close() + return err +end + +return wialon_ips \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips_boof.lua b/luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips_boof.lua new file mode 100644 index 000000000..fe556f246 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/proto/wialon_ips_boof.lua @@ -0,0 +1,119 @@ +------------------------------------------------------------- +-- A module for working with the "WIALON IPS" navigation protocol. +-- This module saves navigation data in case of signal loss +-- (if it is not possible to transfer data to the server) +------------------------------------------------------------- +-- Copyright 2021-2022 Vladislav Kadulin +-- Licensed to the GNU General Public License v3.0 + +local json = require("luci.jsonc") +local checksum = require("checksum") + +local wialon_ips_boof = {} + +-- Extended Data package +local function transformBooferData(GnssData, params) + local D = {"NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","","NA","NA"} + if not GnssData.warning.rmc[1] then + D[1], D[2] = GnssData.rmc.date, GnssData.rmc.utc + else + D[1], D[2] = os.date("%d%m%y"), os.date("%H%M%S",os.time(os.date("!*t"))) .. ".00" + end + if not GnssData.warning.gga[1] then + -- Lat2[4], Lon2[6] - NA + D[3], D[5] = GnssData.gga.latitude, GnssData.gga.longitude + D[9], D[10] = GnssData.gga.alt, GnssData.gga.sat + D[11] = GnssData.gga.hdp + elseif not GnssData.warning.gns[1] then + D[3], D[5] = GnssData.gns.latitude, GnssData.gns.longitude + D[9], D[10] = GnssData.gns.alt, GnssData.gns.sat + D[11] = GnssData.gns.hdp + elseif not GnssData.warning.locator[1] then + D[3], D[5] = GnssData.gga.latitude, '0' .. GnssData.gga.longitude + end + if not GnssData.warning.vtg[1] then + D[7], D[8] = GnssData.vtg.speed, GnssData.vtg.course_t + end + if params then + D[16] = string.format("%s:%s:%s", params[1], params[2], params[3]) + else + D[16] = string.format("%s:%s:%s", "boof", '3', "Data from boofer") + end + + return table.concat(D, ";") +end + +-- read GNSS data from file +local function readBoof() + local file = io.open("/usr/share/gpoint/tmp/blackbox.json", 'r') + if not file then return nil end + local bb_data = json.parse(file:read("*a")) + file:close() + return bb_data +end + +-- write GNSS data to file +local function writeBoof(boof) + local file = io.open("/usr/share/gpoint/tmp/blackbox.json", 'w') + file:write(json.stringify(boof)) + file:close() +end + +-- create boofer with data if +local function createBoof(size) + local BLACKBOX = { ["size"]=0,["max"]=tonumber(size),["data"]={} } + writeBoof(BLACKBOX) + return BLACKBOX +end + +-- Package from the black box +local function parseBlackBoxData(data) + local B = "" + for _, gnss_msg in pairs(data) do + B = B .. gnss_msg .. '|' + end + return "#B#" .. B .. checksum.crc16(B) .. "\r\n" +end + +-- get data from the black box +function wialon_ips_boof.get(serverConfig) + if not serverConfig.blackbox.enable then + return -1, nil + end + + local blackbox = readBoof() + if blackbox == nil or tonumber(blackbox.max) ~= tonumber(serverConfig.blackbox.size) then + blackbox = createBoof(serverConfig.blackbox.size) + end + + return blackbox.size, parseBlackBoxData(blackbox.data) +end + +-- send data to the black box +function wialon_ips_boof.set(GnssData, serverConfig) + + if not serverConfig.blackbox.enable then + return -1 + end + + local blackbox = readBoof() + if blackbox == nil or tonumber(blackbox.max) ~= tonumber(serverConfig.blackbox.size) then + blackbox = createBoof(serverConfig.blackbox.size) + end + + if blackbox.size < blackbox.max then + blackbox.size = blackbox.size + 1 + elseif serverConfig.blackbox.cycle then + blackbox.size = 1 + end + + blackbox.data[blackbox.size] = transformBooferData(GnssData) + writeBoof(blackbox) +end + +-- clear the black box +function wialon_ips_boof.clean(serverConfig) + createBoof(serverConfig.blackbox.size) +end + +return wialon_ips_boof \ No newline at end of file diff --git a/luci-app-gpoint-main/root/usr/share/gpoint/tmp/blackbox.json b/luci-app-gpoint-main/root/usr/share/gpoint/tmp/blackbox.json new file mode 100644 index 000000000..bcf57a157 --- /dev/null +++ b/luci-app-gpoint-main/root/usr/share/gpoint/tmp/blackbox.json @@ -0,0 +1 @@ +{"max":1000,"data":[],"size":0} \ No newline at end of file diff --git a/luci-app-gpoint-main/test/chechsum_test.lua b/luci-app-gpoint-main/test/chechsum_test.lua new file mode 100644 index 000000000..9356457e1 --- /dev/null +++ b/luci-app-gpoint-main/test/chechsum_test.lua @@ -0,0 +1,143 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + +local checksum = require("checksum") +local lu = require('luaunit') + +local goodGNSSSdata = { + "$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*4E", + "$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*52", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*3F", + "$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*66", + "$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*73", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*36", + "$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*69", + "$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*7D", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*3C", + "$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*6E", + "$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*77", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*32", + "$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*65", + "$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*7A", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*34", + "$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*49", + "$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*7E", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*36", + "$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*53", + "$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*76", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*30", + "$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*4E", + "$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*71", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*37", + "$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*4F", + "$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*73", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*37", + "$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*4B", + "$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*71", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*34", + "$GPRMC,113732.568,V,4154.949,N,08002.497,W,47.2,0.20,220721,,E*4A", + "$GPGGA,113733.568,4154.949,N,08002.497,W,0,00,0.2,,M,,M,,*72", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.3,0.8,0.2*39", + "$GPRMC,113735.568,V,4154.951,N,08002.496,W,31.1,0.13,220721,,E*47", + "$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*75", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*35", + "$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*47", + "$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*76", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*39", + "$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*41", + "$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*73", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*30", + "$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*44", + "$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*76", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*3B", + "$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*4D", + "$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*7C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*35", + "$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*40", + "$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*7C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*36", + "$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*41", + "$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*7B", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*37", + "$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*4E", + "$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*71", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*37" +} + +local badGNSSSdata = { + "$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*48", + "$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*54", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*33", + "$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*5F", + "$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*82", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*35", + "$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*5A", + "$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*12", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*35", + "$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*9E", + "$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*94", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*32", + "$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*9C", + "$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*79", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*39", + "$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*90", + "$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*42", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*43", + "$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*44", + "$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*4A", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*4D", + "$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*44", + "$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*44", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*4D", + "$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*4D", + "$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*4D", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*24", + "$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*59", + "$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*4D", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*39", + "$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*79", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*39", + "$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*67", + "$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*79", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*35", + "$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*31", + "$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*33", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*34", + "$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*34", + "$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*75", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*4B", + "$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*5D", + "$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*5C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*55", + "$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*50", + "$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*5C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*35", + "$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*51", + "$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*5B", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*35", + "$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*5E", + "$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*51", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*57" +} + +function testCRC8() + for i = 1, #goodGNSSSdata do + lu.assertEquals(checksum.crc8(goodGNSSSdata[i]), true) + end + for i = 1, #badGNSSSdata do + lu.assertEquals(checksum.crc8(badGNSSSdata[i]), false) + end +end +local a = checksum.crc16("$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*57") + +function testCRC16() + lu.assertEquals(checksum.crc16(goodGNSSSdata[1]), "E50C") + lu.assertEquals(checksum.crc16(goodGNSSSdata[2]), "CCD5") + lu.assertEquals(checksum.crc16(goodGNSSSdata[3]), "852C") + lu.assertEquals(checksum.crc16(goodGNSSSdata[4]), "EDB5") + lu.assertEquals(checksum.crc16(goodGNSSSdata[5]), "5DF0") + lu.assertEquals(checksum.crc16(goodGNSSSdata[6]), "7B29") + lu.assertEquals(checksum.crc16(goodGNSSSdata[7]), "7CC") +end + +os.exit(lu.LuaUnit.run()) diff --git a/luci-app-gpoint-main/test/gps_test.lua b/luci-app-gpoint-main/test/gps_test.lua new file mode 100644 index 000000000..ac6d2c237 --- /dev/null +++ b/luci-app-gpoint-main/test/gps_test.lua @@ -0,0 +1,96 @@ +local gps = require("gps_lib") +--local kalman = require("kalman_lib") + +function read_lat_lon() + local file = io.open("/www/kalman_lua/matrix_c_test/testdata/gps_example_1", 'r') + + while file:read() do + local lat, lon = file:read("*n", "*n") + if lat and lon then + return lat, lon + end + end + + file:close() + print("read_lat_lon - OK") +end + +function test_read_lat_long() + local lat, lon = read_lat_lon() + assert(math.abs(lat - 39.315828) < 0.000001) + assert(math.abs(lon - -120.167838) < 0.000001) + for i = 1, 131 do + local lat, lon = read_lat_lon() + end + print("test_read_lat_long - OK") +end + +function test_bearing_north() + local kalman = gps.create_velocity2d(1.0) + for i = 1, 100 do + kalman = gps.update_velocity2d(kalman, i * 0.0001, 0.0, 1.0) + end + + local bearing = gps.get_bearing(kalman) + assert(math.abs(bearing - 0.0) < 0.01) + + local dlat, dlon = gps.get_velocity(kalman) + assert(math.abs(dlat - 0.0001) < 0.00001) + assert(math.abs(dlon) < 0.00001) + print("test_bearing_north - OK") +end + +function test_bearing_east() + local kalman = gps.create_velocity2d(1.0) + for i = 1, 100 do + kalman = gps.update_velocity2d(kalman, 0.0, i * 0.0001, 1.0) + end + + local bearing = gps.get_bearing(kalman) + assert(math.abs(bearing - 90.0) < 0.01) + --At this rate, it takes 10,000 timesteps to travel one longitude + --unit, and thus 3,600,000 timesteps to travel the circumference of + --the earth. Let's say one timestep is a second, so it takes + --3,600,000 seconds, which is 60,000 minutes, which is 1,000 + --hours. Since the earth is about 25000 miles around, this means we + --are traveling at about 25 miles per hour. + local mph = gps.get_mph(kalman) + assert(math.abs(mph - 25.0) < 2.0) + print("test_bearing_east - OK") +end + +function test_bearing_south() + local kalman = gps.create_velocity2d(1.0) + for i = 1, 100 do + kalman = gps.update_velocity2d(kalman, i * -0.0001, 0.0, 1.0) + end + + local bearing = gps.get_bearing(kalman) + assert(math.abs(bearing - 180.0) < 0.01) + print("test_bearing_south - OK") +end + +function test_bearing_west() + local kalman = gps.create_velocity2d(1.0) + for i = 1, 100 do + kalman = gps.update_velocity2d(kalman, 0.0, i * -0.0001, 1.0) + end + + local bearing = gps.get_bearing(kalman) + assert(math.abs(bearing - 270.0) < 0.01) + print("test_bearing_west - OK") +end + +function test_calculate_mph() + local mph = gps.calculate_mph(39.315842, -120.167107, -0.000031, 0.000003); + assert(math.abs(mph - 7.74) < 0.01); + print("test_calculate_mph - OK") +end + +-- test start +test_read_lat_long() +test_bearing_north() +test_bearing_east() +test_bearing_south() +test_bearing_west() +test_calculate_mph() \ No newline at end of file diff --git a/luci-app-gpoint-main/test/kalman_test.lua b/luci-app-gpoint-main/test/kalman_test.lua new file mode 100644 index 000000000..a2b5ab674 --- /dev/null +++ b/luci-app-gpoint-main/test/kalman_test.lua @@ -0,0 +1,34 @@ +local kalman = require("kalman_lib") +local matrix = require("matrix_lib") + +-- Test the example of a train moving along a 1-d track +function test_train() + local k = kalman.create(2, 1) + -- The train state is a 2d vector containing position and velocity. + -- Velocity is measured in position units per timestep units. + k.state_transition = matrix.set(k.state_transition, 1.0, 1.0, + 0.0, 1.0) + -- We only observe position + k.observation_model = matrix.set(k.observation_model, 1.0, 0.0) + -- The covariance matrices are blind guesses + k.process_noise_covariance = matrix.set_identity(k.process_noise_covariance) + k.observation_noise_covariance = matrix.set_identity(k.observation_noise_covariance) + -- Our knowledge of the start position is incorrect and unconfident + local deviation = 1000.0 + k.state_estimate = matrix.set(k.state_estimate, 10 * deviation) + k.estimate_covariance = matrix.set_identity(k.estimate_covariance) + k.estimate_covariance = matrix.scale(k.estimate_covariance, deviation * deviation) + + for i = 1, 10 do + k.observation = matrix.set(k.observation, i) + k = kalman.update(k) + end + + + print("estimated position: " .. tostring(k.state_estimate[1][1])) + print("estimated position: " .. tostring(k.state_estimate[2][1])) + +end + +test_train() +print("OK") \ No newline at end of file diff --git a/luci-app-gpoint-main/test/luaunit.lua b/luci-app-gpoint-main/test/luaunit.lua new file mode 100644 index 000000000..7ac4b0e89 --- /dev/null +++ b/luci-app-gpoint-main/test/luaunit.lua @@ -0,0 +1,3452 @@ +--[[ + luaunit.lua + +Description: A unit testing framework +Homepage: https://github.com/bluebird75/luaunit +Development by Philippe Fremy +Based on initial work of Ryu, Gwang (http://www.gpgstudy.com/gpgiki/LuaUnit) +License: BSD License, see LICENSE.txt +]]-- + +require("math") +local M={} + +-- private exported functions (for testing) +M.private = {} + +M.VERSION='3.4' +M._VERSION=M.VERSION -- For LuaUnit v2 compatibility + +-- a version which distinguish between regular Lua and LuaJit +M._LUAVERSION = (jit and jit.version) or _VERSION + +--[[ Some people like assertEquals( actual, expected ) and some people prefer +assertEquals( expected, actual ). +]]-- +M.ORDER_ACTUAL_EXPECTED = true +M.PRINT_TABLE_REF_IN_ERROR_MSG = false +M.LINE_LENGTH = 80 +M.TABLE_DIFF_ANALYSIS_THRESHOLD = 10 -- display deep analysis for more than 10 items +M.LIST_DIFF_ANALYSIS_THRESHOLD = 10 -- display deep analysis for more than 10 items + +-- this setting allow to remove entries from the stack-trace, for +-- example to hide a call to a framework which would be calling luaunit +M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE = 0 + +--[[ EPS is meant to help with Lua's floating point math in simple corner +cases like almostEquals(1.1-0.1, 1), which may not work as-is (e.g. on numbers +with rational binary representation) if the user doesn't provide some explicit +error margin. + +The default margin used by almostEquals() in such cases is EPS; and since +Lua may be compiled with different numeric precisions (single vs. double), we +try to select a useful default for it dynamically. Note: If the initial value +is not acceptable, it can be changed by the user to better suit specific needs. + +See also: https://en.wikipedia.org/wiki/Machine_epsilon +]] +M.EPS = 2^-52 -- = machine epsilon for "double", ~2.22E-16 +if math.abs(1.1 - 1 - 0.1) > M.EPS then + -- rounding error is above EPS, assume single precision + M.EPS = 2^-23 -- = machine epsilon for "float", ~1.19E-07 +end + +-- set this to false to debug luaunit +local STRIP_LUAUNIT_FROM_STACKTRACE = true + +M.VERBOSITY_DEFAULT = 10 +M.VERBOSITY_LOW = 1 +M.VERBOSITY_QUIET = 0 +M.VERBOSITY_VERBOSE = 20 +M.DEFAULT_DEEP_ANALYSIS = nil +M.FORCE_DEEP_ANALYSIS = true +M.DISABLE_DEEP_ANALYSIS = false + +-- set EXPORT_ASSERT_TO_GLOBALS to have all asserts visible as global values +-- EXPORT_ASSERT_TO_GLOBALS = true + +-- we need to keep a copy of the script args before it is overriden +local cmdline_argv = rawget(_G, "arg") + +M.FAILURE_PREFIX = 'LuaUnit test FAILURE: ' -- prefix string for failed tests +M.SUCCESS_PREFIX = 'LuaUnit test SUCCESS: ' -- prefix string for successful tests finished early +M.SKIP_PREFIX = 'LuaUnit test SKIP: ' -- prefix string for skipped tests + + + +M.USAGE=[[Usage: lua [options] [testname1 [testname2] ... ] +Options: + -h, --help: Print this help + --version: Print version information + -v, --verbose: Increase verbosity + -q, --quiet: Set verbosity to minimum + -e, --error: Stop on first error + -f, --failure: Stop on first failure or error + -s, --shuffle: Shuffle tests before running them + -o, --output OUTPUT: Set output type to OUTPUT + Possible values: text, tap, junit, nil + -n, --name NAME: For junit only, mandatory name of xml file + -r, --repeat NUM: Execute all tests NUM times, e.g. to trig the JIT + -p, --pattern PATTERN: Execute all test names matching the Lua PATTERN + May be repeated to include several patterns + Make sure you escape magic chars like +? with % + -x, --exclude PATTERN: Exclude all test names matching the Lua PATTERN + May be repeated to exclude several patterns + Make sure you escape magic chars like +? with % + testname1, testname2, ... : tests to run in the form of testFunction, + TestClass or TestClass.testMethod + +You may also control LuaUnit options with the following environment variables: +* LUAUNIT_OUTPUT: same as --output +* LUAUNIT_JUNIT_FNAME: same as --name ]] + +---------------------------------------------------------------- +-- +-- general utility functions +-- +---------------------------------------------------------------- + +--[[ Note on catching exit + +I have seen the case where running a big suite of test cases and one of them would +perform a os.exit(0), making the outside world think that the full test suite was executed +successfully. + +This is an attempt to mitigate this problem: we override os.exit() to now let a test +exit the framework while we are running. When we are not running, it behaves normally. +]] + +M.oldOsExit = os.exit +os.exit = function(...) + if M.LuaUnit and #M.LuaUnit.instances ~= 0 then + local msg = [[You are trying to exit but there is still a running instance of LuaUnit. +LuaUnit expects to run until the end before exiting with a complete status of successful/failed tests. + +To force exit LuaUnit while running, please call before os.exit (assuming lu is the luaunit module loaded): + + lu.unregisterCurrentSuite() + +]] + M.private.error_fmt(2, msg) + end + M.oldOsExit(...) +end + +local function pcall_or_abort(func, ...) + -- unpack is a global function for Lua 5.1, otherwise use table.unpack + local unpack = rawget(_G, "unpack") or table.unpack + local result = {pcall(func, ...)} + if not result[1] then + -- an error occurred + print(result[2]) -- error message + print() + print(M.USAGE) + os.exit(-1) + end + return unpack(result, 2) +end + +local crossTypeOrdering = { + number = 1, boolean = 2, string = 3, table = 4, other = 5 +} +local crossTypeComparison = { + number = function(a, b) return a < b end, + string = function(a, b) return a < b end, + other = function(a, b) return tostring(a) < tostring(b) end, +} + +local function crossTypeSort(a, b) + local type_a, type_b = type(a), type(b) + if type_a == type_b then + local func = crossTypeComparison[type_a] or crossTypeComparison.other + return func(a, b) + end + type_a = crossTypeOrdering[type_a] or crossTypeOrdering.other + type_b = crossTypeOrdering[type_b] or crossTypeOrdering.other + return type_a < type_b +end + +local function __genSortedIndex( t ) + -- Returns a sequence consisting of t's keys, sorted. + local sortedIndex = {} + + for key,_ in pairs(t) do + table.insert(sortedIndex, key) + end + + table.sort(sortedIndex, crossTypeSort) + return sortedIndex +end +M.private.__genSortedIndex = __genSortedIndex + +local function sortedNext(state, control) + -- Equivalent of the next() function of table iteration, but returns the + -- keys in sorted order (see __genSortedIndex and crossTypeSort). + -- The state is a temporary variable during iteration and contains the + -- sorted key table (state.sortedIdx). It also stores the last index (into + -- the keys) used by the iteration, to find the next one quickly. + local key + + --print("sortedNext: control = "..tostring(control) ) + if control == nil then + -- start of iteration + state.count = #state.sortedIdx + state.lastIdx = 1 + key = state.sortedIdx[1] + return key, state.t[key] + end + + -- normally, we expect the control variable to match the last key used + if control ~= state.sortedIdx[state.lastIdx] then + -- strange, we have to find the next value by ourselves + -- the key table is sorted in crossTypeSort() order! -> use bisection + local lower, upper = 1, state.count + repeat + state.lastIdx = math.modf((lower + upper) / 2) + key = state.sortedIdx[state.lastIdx] + if key == control then + break -- key found (and thus prev index) + end + if crossTypeSort(key, control) then + -- key < control, continue search "right" (towards upper bound) + lower = state.lastIdx + 1 + else + -- key > control, continue search "left" (towards lower bound) + upper = state.lastIdx - 1 + end + until lower > upper + if lower > upper then -- only true if the key wasn't found, ... + state.lastIdx = state.count -- ... so ensure no match in code below + end + end + + -- proceed by retrieving the next value (or nil) from the sorted keys + state.lastIdx = state.lastIdx + 1 + key = state.sortedIdx[state.lastIdx] + if key then + return key, state.t[key] + end + + -- getting here means returning `nil`, which will end the iteration +end + +local function sortedPairs(tbl) + -- Equivalent of the pairs() function on tables. Allows to iterate in + -- sorted order. As required by "generic for" loops, this will return the + -- iterator (function), an "invariant state", and the initial control value. + -- (see http://www.lua.org/pil/7.2.html) + return sortedNext, {t = tbl, sortedIdx = __genSortedIndex(tbl)}, nil +end +M.private.sortedPairs = sortedPairs + +-- seed the random with a strongly varying seed +math.randomseed(math.floor((os.clock()*1E11) / 100)) -- add /100 OpenWrt some bug + +local function randomizeTable( t ) + -- randomize the item orders of the table t + for i = #t, 2, -1 do + local j = math.random(i) + if i ~= j then + t[i], t[j] = t[j], t[i] + end + end +end +M.private.randomizeTable = randomizeTable + +local function strsplit(delimiter, text) +-- Split text into a list consisting of the strings in text, separated +-- by strings matching delimiter (which may _NOT_ be a pattern). +-- Example: strsplit(", ", "Anna, Bob, Charlie, Dolores") + if delimiter == "" or delimiter == nil then -- this would result in endless loops + error("delimiter is nil or empty string!") + end + if text == nil then + return nil + end + + local list, pos, first, last = {}, 1 + while true do + first, last = text:find(delimiter, pos, true) + if first then -- found? + table.insert(list, text:sub(pos, first - 1)) + pos = last + 1 + else + table.insert(list, text:sub(pos)) + break + end + end + return list +end +M.private.strsplit = strsplit + +local function hasNewLine( s ) + -- return true if s has a newline + return (string.find(s, '\n', 1, true) ~= nil) +end +M.private.hasNewLine = hasNewLine + +local function prefixString( prefix, s ) + -- Prefix all the lines of s with prefix + return prefix .. string.gsub(s, '\n', '\n' .. prefix) +end +M.private.prefixString = prefixString + +local function strMatch(s, pattern, start, final ) + -- return true if s matches completely the pattern from index start to index end + -- return false in every other cases + -- if start is nil, matches from the beginning of the string + -- if final is nil, matches to the end of the string + start = start or 1 + final = final or string.len(s) + + local foundStart, foundEnd = string.find(s, pattern, start, false) + return foundStart == start and foundEnd == final +end +M.private.strMatch = strMatch + +local function patternFilter(patterns, expr) + -- Run `expr` through the inclusion and exclusion rules defined in patterns + -- and return true if expr shall be included, false for excluded. + -- Inclusion pattern are defined as normal patterns, exclusions + -- patterns start with `!` and are followed by a normal pattern + + -- result: nil = UNKNOWN (not matched yet), true = ACCEPT, false = REJECT + -- default: true if no explicit "include" is found, set to false otherwise + local default, result = true, nil + + if patterns ~= nil then + for _, pattern in ipairs(patterns) do + local exclude = pattern:sub(1,1) == '!' + if exclude then + pattern = pattern:sub(2) + else + -- at least one include pattern specified, a match is required + default = false + end + -- print('pattern: ',pattern) + -- print('exclude: ',exclude) + -- print('default: ',default) + + if string.find(expr, pattern) then + -- set result to false when excluding, true otherwise + result = not exclude + end + end + end + + if result ~= nil then + return result + end + return default +end +M.private.patternFilter = patternFilter + +local function xmlEscape( s ) + -- Return s escaped for XML attributes + -- escapes table: + -- " " + -- ' ' + -- < < + -- > > + -- & & + + return string.gsub( s, '.', { + ['&'] = "&", + ['"'] = """, + ["'"] = "'", + ['<'] = "<", + ['>'] = ">", + } ) +end +M.private.xmlEscape = xmlEscape + +local function xmlCDataEscape( s ) + -- Return s escaped for CData section, escapes: "]]>" + return string.gsub( s, ']]>', ']]>' ) +end +M.private.xmlCDataEscape = xmlCDataEscape + + +local function lstrip( s ) + --[[Return s with all leading white spaces and tabs removed]] + local idx = 0 + while idx < s:len() do + idx = idx + 1 + local c = s:sub(idx,idx) + if c ~= ' ' and c ~= '\t' then + break + end + end + return s:sub(idx) +end +M.private.lstrip = lstrip + +local function extractFileLineInfo( s ) + --[[ From a string in the form "(leading spaces) dir1/dir2\dir3\file.lua:linenb: msg" + + Return the "file.lua:linenb" information + ]] + local s2 = lstrip(s) + local firstColon = s2:find(':', 1, true) + if firstColon == nil then + -- string is not in the format file:line: + return s + end + local secondColon = s2:find(':', firstColon+1, true) + if secondColon == nil then + -- string is not in the format file:line: + return s + end + + return s2:sub(1, secondColon-1) +end +M.private.extractFileLineInfo = extractFileLineInfo + + +local function stripLuaunitTrace2( stackTrace, errMsg ) + --[[ + -- Example of a traceback: + < + [C]: in function 'xpcall' + ./luaunit.lua:1449: in function 'protectedCall' + ./luaunit.lua:1508: in function 'execOneFunction' + ./luaunit.lua:1596: in function 'runSuiteByInstances' + ./luaunit.lua:1660: in function 'runSuiteByNames' + ./luaunit.lua:1736: in function 'runSuite' + example_with_luaunit.lua:140: in main chunk + [C]: in ?>> + error message: <> + + Other example: + < + [C]: in function 'xpcall' + ./luaunit.lua:1517: in function 'protectedCall' + ./luaunit.lua:1578: in function 'execOneFunction' + ./luaunit.lua:1677: in function 'runSuiteByInstances' + ./luaunit.lua:1730: in function 'runSuiteByNames' + ./luaunit.lua:1806: in function 'runSuite' + example_with_luaunit.lua:140: in main chunk + [C]: in ?>> + error message: <> + + < + [C]: in function 'xpcall' + luaunit2/luaunit.lua:1532: in function 'protectedCall' + luaunit2/luaunit.lua:1591: in function 'execOneFunction' + luaunit2/luaunit.lua:1679: in function 'runSuiteByInstances' + luaunit2/luaunit.lua:1743: in function 'runSuiteByNames' + luaunit2/luaunit.lua:1819: in function 'runSuite' + luaunit2/example_with_luaunit.lua:140: in main chunk + [C]: in ?>> + error message: <> + + + -- first line is "stack traceback": KEEP + -- next line may be luaunit line: REMOVE + -- next lines are call in the program under testOk: REMOVE + -- next lines are calls from luaunit to call the program under test: KEEP + + -- Strategy: + -- keep first line + -- remove lines that are part of luaunit + -- kepp lines until we hit a luaunit line + + The strategy for stripping is: + * keep first line "stack traceback:" + * part1: + * analyse all lines of the stack from bottom to top of the stack (first line to last line) + * extract the "file:line:" part of the line + * compare it with the "file:line" part of the error message + * if it does not match strip the line + * if it matches, keep the line and move to part 2 + * part2: + * anything NOT starting with luaunit.lua is the interesting part of the stack trace + * anything starting again with luaunit.lua is part of the test launcher and should be stripped out + ]] + + local function isLuaunitInternalLine( s ) + -- return true if line of stack trace comes from inside luaunit + return s:find('[/\\]luaunit%.lua:%d+: ') ~= nil + end + + -- print( '<<'..stackTrace..'>>' ) + + local t = strsplit( '\n', stackTrace ) + -- print( prettystr(t) ) + + local idx = 2 + + local errMsgFileLine = extractFileLineInfo(errMsg) + -- print('emfi="'..errMsgFileLine..'"') + + -- remove lines that are still part of luaunit + while t[idx] and extractFileLineInfo(t[idx]) ~= errMsgFileLine do + -- print('Removing : '..t[idx] ) + table.remove(t, idx) + end + + -- keep lines until we hit luaunit again + while t[idx] and (not isLuaunitInternalLine(t[idx])) do + -- print('Keeping : '..t[idx] ) + idx = idx + 1 + end + + -- remove remaining luaunit lines + while t[idx] do + -- print('Removing2 : '..t[idx] ) + table.remove(t, idx) + end + + -- print( prettystr(t) ) + return table.concat( t, '\n') + +end +M.private.stripLuaunitTrace2 = stripLuaunitTrace2 + + +local function prettystr_sub(v, indentLevel, printTableRefs, cycleDetectTable ) + local type_v = type(v) + if "string" == type_v then + -- use clever delimiters according to content: + -- enclose with single quotes if string contains ", but no ' + if v:find('"', 1, true) and not v:find("'", 1, true) then + return "'" .. v .. "'" + end + -- use double quotes otherwise, escape embedded " + return '"' .. v:gsub('"', '\\"') .. '"' + + elseif "table" == type_v then + --if v.__class__ then + -- return string.gsub( tostring(v), 'table', v.__class__ ) + --end + return M.private._table_tostring(v, indentLevel, printTableRefs, cycleDetectTable) + + elseif "number" == type_v then + -- eliminate differences in formatting between various Lua versions + if v ~= v then + return "#NaN" -- "not a number" + end + if v == math.huge then + return "#Inf" -- "infinite" + end + if v == -math.huge then + return "-#Inf" + end + if _VERSION == "Lua 5.3" then + local i = math.tointeger(v) + if i then + return tostring(i) + end + end + end + + return tostring(v) +end + +local function prettystr( v ) + --[[ Pretty string conversion, to display the full content of a variable of any type. + + * string are enclosed with " by default, or with ' if string contains a " + * tables are expanded to show their full content, with indentation in case of nested tables + ]]-- + local cycleDetectTable = {} + local s = prettystr_sub(v, 1, M.PRINT_TABLE_REF_IN_ERROR_MSG, cycleDetectTable) + if cycleDetectTable.detected and not M.PRINT_TABLE_REF_IN_ERROR_MSG then + -- some table contain recursive references, + -- so we must recompute the value by including all table references + -- else the result looks like crap + cycleDetectTable = {} + s = prettystr_sub(v, 1, true, cycleDetectTable) + end + return s +end +M.prettystr = prettystr + +function M.adjust_err_msg_with_iter( err_msg, iter_msg ) + --[[ Adjust the error message err_msg: trim the FAILURE_PREFIX or SUCCESS_PREFIX information if needed, + add the iteration message if any and return the result. + + err_msg: string, error message captured with pcall + iter_msg: a string describing the current iteration ("iteration N") or nil + if there is no iteration in this test. + + Returns: (new_err_msg, test_status) + new_err_msg: string, adjusted error message, or nil in case of success + test_status: M.NodeStatus.FAIL, SUCCESS or ERROR according to the information + contained in the error message. + ]] + if iter_msg then + iter_msg = iter_msg..', ' + else + iter_msg = '' + end + + local RE_FILE_LINE = '.*:%d+: ' + + -- error message is not necessarily a string, + -- so convert the value to string with prettystr() + if type( err_msg ) ~= 'string' then + err_msg = prettystr( err_msg ) + end + + if (err_msg:find( M.SUCCESS_PREFIX ) == 1) or err_msg:match( '('..RE_FILE_LINE..')' .. M.SUCCESS_PREFIX .. ".*" ) then + -- test finished early with success() + return nil, M.NodeStatus.SUCCESS + end + + if (err_msg:find( M.SKIP_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.SKIP_PREFIX .. ".*" ) ~= nil) then + -- substitute prefix by iteration message + err_msg = err_msg:gsub('.*'..M.SKIP_PREFIX, iter_msg, 1) + -- print("failure detected") + return err_msg, M.NodeStatus.SKIP + end + + if (err_msg:find( M.FAILURE_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.FAILURE_PREFIX .. ".*" ) ~= nil) then + -- substitute prefix by iteration message + err_msg = err_msg:gsub(M.FAILURE_PREFIX, iter_msg, 1) + -- print("failure detected") + return err_msg, M.NodeStatus.FAIL + end + + + + -- print("error detected") + -- regular error, not a failure + if iter_msg then + local match + -- "./test\\test_luaunit.lua:2241: some error msg + match = err_msg:match( '(.*:%d+: ).*' ) + if match then + err_msg = err_msg:gsub( match, match .. iter_msg ) + else + -- no file:line: infromation, just add the iteration info at the beginning of the line + err_msg = iter_msg .. err_msg + end + end + return err_msg, M.NodeStatus.ERROR +end + +local function tryMismatchFormatting( table_a, table_b, doDeepAnalysis, margin ) + --[[ + Prepares a nice error message when comparing tables, performing a deeper + analysis. + + Arguments: + * table_a, table_b: tables to be compared + * doDeepAnalysis: + M.DEFAULT_DEEP_ANALYSIS: (the default if not specified) perform deep analysis only for big lists and big dictionnaries + M.FORCE_DEEP_ANALYSIS : always perform deep analysis + M.DISABLE_DEEP_ANALYSIS: never perform deep analysis + * margin: supplied only for almost equality + + Returns: {success, result} + * success: false if deep analysis could not be performed + in this case, just use standard assertion message + * result: if success is true, a multi-line string with deep analysis of the two lists + ]] + + -- check if table_a & table_b are suitable for deep analysis + if type(table_a) ~= 'table' or type(table_b) ~= 'table' then + return false + end + + if doDeepAnalysis == M.DISABLE_DEEP_ANALYSIS then + return false + end + + local len_a, len_b, isPureList = #table_a, #table_b, true + + for k1, v1 in pairs(table_a) do + if type(k1) ~= 'number' or k1 > len_a then + -- this table a mapping + isPureList = false + break + end + end + + if isPureList then + for k2, v2 in pairs(table_b) do + if type(k2) ~= 'number' or k2 > len_b then + -- this table a mapping + isPureList = false + break + end + end + end + + if isPureList and math.min(len_a, len_b) < M.LIST_DIFF_ANALYSIS_THRESHOLD then + if not (doDeepAnalysis == M.FORCE_DEEP_ANALYSIS) then + return false + end + end + + if isPureList then + return M.private.mismatchFormattingPureList( table_a, table_b, margin ) + else + -- only work on mapping for the moment + -- return M.private.mismatchFormattingMapping( table_a, table_b, doDeepAnalysis ) + return false + end +end +M.private.tryMismatchFormatting = tryMismatchFormatting + +local function getTaTbDescr() + if not M.ORDER_ACTUAL_EXPECTED then + return 'expected', 'actual' + end + return 'actual', 'expected' +end + +local function extendWithStrFmt( res, ... ) + table.insert( res, string.format( ... ) ) +end + +local function mismatchFormattingMapping( table_a, table_b, doDeepAnalysis ) + --[[ + Prepares a nice error message when comparing tables which are not pure lists, performing a deeper + analysis. + + Returns: {success, result} + * success: false if deep analysis could not be performed + in this case, just use standard assertion message + * result: if success is true, a multi-line string with deep analysis of the two lists + ]] + + -- disable for the moment + --[[ + local result = {} + local descrTa, descrTb = getTaTbDescr() + + local keysCommon = {} + local keysOnlyTa = {} + local keysOnlyTb = {} + local keysDiffTaTb = {} + + local k, v + + for k,v in pairs( table_a ) do + if is_equal( v, table_b[k] ) then + table.insert( keysCommon, k ) + else + if table_b[k] == nil then + table.insert( keysOnlyTa, k ) + else + table.insert( keysDiffTaTb, k ) + end + end + end + + for k,v in pairs( table_b ) do + if not is_equal( v, table_a[k] ) and table_a[k] == nil then + table.insert( keysOnlyTb, k ) + end + end + + local len_a = #keysCommon + #keysDiffTaTb + #keysOnlyTa + local len_b = #keysCommon + #keysDiffTaTb + #keysOnlyTb + local limited_display = (len_a < 5 or len_b < 5) + + if math.min(len_a, len_b) < M.TABLE_DIFF_ANALYSIS_THRESHOLD then + return false + end + + if not limited_display then + if len_a == len_b then + extendWithStrFmt( result, 'Table A (%s) and B (%s) both have %d items', descrTa, descrTb, len_a ) + else + extendWithStrFmt( result, 'Table A (%s) has %d items and table B (%s) has %d items', descrTa, len_a, descrTb, len_b ) + end + + if #keysCommon == 0 and #keysDiffTaTb == 0 then + table.insert( result, 'Table A and B have no keys in common, they are totally different') + else + local s_other = 'other ' + if #keysCommon then + extendWithStrFmt( result, 'Table A and B have %d identical items', #keysCommon ) + else + table.insert( result, 'Table A and B have no identical items' ) + s_other = '' + end + + if #keysDiffTaTb ~= 0 then + result[#result] = string.format( '%s and %d items differing present in both tables', result[#result], #keysDiffTaTb) + else + result[#result] = string.format( '%s and no %sitems differing present in both tables', result[#result], s_other, #keysDiffTaTb) + end + end + + extendWithStrFmt( result, 'Table A has %d keys not present in table B and table B has %d keys not present in table A', #keysOnlyTa, #keysOnlyTb ) + end + + local function keytostring(k) + if "string" == type(k) and k:match("^[_%a][_%w]*$") then + return k + end + return prettystr(k) + end + + if #keysDiffTaTb ~= 0 then + table.insert( result, 'Items differing in A and B:') + for k,v in sortedPairs( keysDiffTaTb ) do + extendWithStrFmt( result, ' - A[%s]: %s', keytostring(v), prettystr(table_a[v]) ) + extendWithStrFmt( result, ' + B[%s]: %s', keytostring(v), prettystr(table_b[v]) ) + end + end + + if #keysOnlyTa ~= 0 then + table.insert( result, 'Items only in table A:' ) + for k,v in sortedPairs( keysOnlyTa ) do + extendWithStrFmt( result, ' - A[%s]: %s', keytostring(v), prettystr(table_a[v]) ) + end + end + + if #keysOnlyTb ~= 0 then + table.insert( result, 'Items only in table B:' ) + for k,v in sortedPairs( keysOnlyTb ) do + extendWithStrFmt( result, ' + B[%s]: %s', keytostring(v), prettystr(table_b[v]) ) + end + end + + if #keysCommon ~= 0 then + table.insert( result, 'Items common to A and B:') + for k,v in sortedPairs( keysCommon ) do + extendWithStrFmt( result, ' = A and B [%s]: %s', keytostring(v), prettystr(table_a[v]) ) + end + end + + return true, table.concat( result, '\n') + ]] +end +M.private.mismatchFormattingMapping = mismatchFormattingMapping + +local function mismatchFormattingPureList( table_a, table_b, margin ) + --[[ + Prepares a nice error message when comparing tables which are lists, performing a deeper + analysis. + + margin is supplied only for almost equality + + Returns: {success, result} + * success: false if deep analysis could not be performed + in this case, just use standard assertion message + * result: if success is true, a multi-line string with deep analysis of the two lists + ]] + local result, descrTa, descrTb = {}, getTaTbDescr() + + local len_a, len_b, refa, refb = #table_a, #table_b, '', '' + if M.PRINT_TABLE_REF_IN_ERROR_MSG then + refa, refb = string.format( '<%s> ', M.private.table_ref(table_a)), string.format('<%s> ', M.private.table_ref(table_b) ) + end + local longest, shortest = math.max(len_a, len_b), math.min(len_a, len_b) + local deltalv = longest - shortest + + local commonUntil = shortest + for i = 1, shortest do + if not M.private.is_table_equals(table_a[i], table_b[i], margin) then + commonUntil = i - 1 + break + end + end + + local commonBackTo = shortest - 1 + for i = 0, shortest - 1 do + if not M.private.is_table_equals(table_a[len_a-i], table_b[len_b-i], margin) then + commonBackTo = i - 1 + break + end + end + + + table.insert( result, 'List difference analysis:' ) + if len_a == len_b then + -- TODO: handle expected/actual naming + extendWithStrFmt( result, '* lists %sA (%s) and %sB (%s) have the same size', refa, descrTa, refb, descrTb ) + else + extendWithStrFmt( result, '* list sizes differ: list %sA (%s) has %d items, list %sB (%s) has %d items', refa, descrTa, len_a, refb, descrTb, len_b ) + end + + extendWithStrFmt( result, '* lists A and B start differing at index %d', commonUntil+1 ) + if commonBackTo >= 0 then + if deltalv > 0 then + extendWithStrFmt( result, '* lists A and B are equal again from index %d for A, %d for B', len_a-commonBackTo, len_b-commonBackTo ) + else + extendWithStrFmt( result, '* lists A and B are equal again from index %d', len_a-commonBackTo ) + end + end + + local function insertABValue(ai, bi) + bi = bi or ai + if M.private.is_table_equals( table_a[ai], table_b[bi], margin) then + return extendWithStrFmt( result, ' = A[%d], B[%d]: %s', ai, bi, prettystr(table_a[ai]) ) + else + extendWithStrFmt( result, ' - A[%d]: %s', ai, prettystr(table_a[ai])) + extendWithStrFmt( result, ' + B[%d]: %s', bi, prettystr(table_b[bi])) + end + end + + -- common parts to list A & B, at the beginning + if commonUntil > 0 then + table.insert( result, '* Common parts:' ) + for i = 1, commonUntil do + insertABValue( i ) + end + end + + -- diffing parts to list A & B + if commonUntil < shortest - commonBackTo - 1 then + table.insert( result, '* Differing parts:' ) + for i = commonUntil + 1, shortest - commonBackTo - 1 do + insertABValue( i ) + end + end + + -- display indexes of one list, with no match on other list + if shortest - commonBackTo <= longest - commonBackTo - 1 then + table.insert( result, '* Present only in one list:' ) + for i = shortest - commonBackTo, longest - commonBackTo - 1 do + if len_a > len_b then + extendWithStrFmt( result, ' - A[%d]: %s', i, prettystr(table_a[i]) ) + -- table.insert( result, '+ (no matching B index)') + else + -- table.insert( result, '- no matching A index') + extendWithStrFmt( result, ' + B[%d]: %s', i, prettystr(table_b[i]) ) + end + end + end + + -- common parts to list A & B, at the end + if commonBackTo >= 0 then + table.insert( result, '* Common parts at the end of the lists' ) + for i = longest - commonBackTo, longest do + if len_a > len_b then + insertABValue( i, i-deltalv ) + else + insertABValue( i-deltalv, i ) + end + end + end + + return true, table.concat( result, '\n') +end +M.private.mismatchFormattingPureList = mismatchFormattingPureList + +local function prettystrPairs(value1, value2, suffix_a, suffix_b) + --[[ + This function helps with the recurring task of constructing the "expected + vs. actual" error messages. It takes two arbitrary values and formats + corresponding strings with prettystr(). + + To keep the (possibly complex) output more readable in case the resulting + strings contain line breaks, they get automatically prefixed with additional + newlines. Both suffixes are optional (default to empty strings), and get + appended to the "value1" string. "suffix_a" is used if line breaks were + encountered, "suffix_b" otherwise. + + Returns the two formatted strings (including padding/newlines). + ]] + local str1, str2 = prettystr(value1), prettystr(value2) + if hasNewLine(str1) or hasNewLine(str2) then + -- line break(s) detected, add padding + return "\n" .. str1 .. (suffix_a or ""), "\n" .. str2 + end + return str1 .. (suffix_b or ""), str2 +end +M.private.prettystrPairs = prettystrPairs + +local UNKNOWN_REF = 'table 00-unknown ref' +local ref_generator = { value=1, [UNKNOWN_REF]=0 } + +local function table_ref( t ) + -- return the default tostring() for tables, with the table ID, even if the table has a metatable + -- with the __tostring converter + local ref = '' + local mt = getmetatable( t ) + if mt == nil then + ref = tostring(t) + else + local success, result + success, result = pcall(setmetatable, t, nil) + if not success then + -- protected table, if __tostring is defined, we can + -- not get the reference. And we can not know in advance. + ref = tostring(t) + if not ref:match( 'table: 0?x?[%x]+' ) then + return UNKNOWN_REF + end + else + ref = tostring(t) + setmetatable( t, mt ) + end + end + -- strip the "table: " part + ref = ref:sub(8) + if ref ~= UNKNOWN_REF and ref_generator[ref] == nil then + -- Create a new reference number + ref_generator[ref] = ref_generator.value + ref_generator.value = ref_generator.value+1 + end + if M.PRINT_TABLE_REF_IN_ERROR_MSG then + return string.format('table %02d-%s', ref_generator[ref], ref) + else + return string.format('table %02d', ref_generator[ref]) + end +end +M.private.table_ref = table_ref + +local TABLE_TOSTRING_SEP = ", " +local TABLE_TOSTRING_SEP_LEN = string.len(TABLE_TOSTRING_SEP) + +local function _table_tostring( tbl, indentLevel, printTableRefs, cycleDetectTable ) + printTableRefs = printTableRefs or M.PRINT_TABLE_REF_IN_ERROR_MSG + cycleDetectTable = cycleDetectTable or {} + cycleDetectTable[tbl] = true + + local result, dispOnMultLines = {}, false + + -- like prettystr but do not enclose with "" if the string is just alphanumerical + -- this is better for displaying table keys who are often simple strings + local function keytostring(k) + if "string" == type(k) and k:match("^[_%a][_%w]*$") then + return k + end + return prettystr_sub(k, indentLevel+1, printTableRefs, cycleDetectTable) + end + + local mt = getmetatable( tbl ) + + if mt and mt.__tostring then + -- if table has a __tostring() function in its metatable, use it to display the table + -- else, compute a regular table + result = tostring(tbl) + if type(result) ~= 'string' then + return string.format( '', prettystr(result) ) + end + result = strsplit( '\n', result ) + return M.private._table_tostring_format_multiline_string( result, indentLevel ) + + else + -- no metatable, compute the table representation + + local entry, count, seq_index = nil, 0, 1 + for k, v in sortedPairs( tbl ) do + + -- key part + if k == seq_index then + -- for the sequential part of tables, we'll skip the "=" output + entry = '' + seq_index = seq_index + 1 + elseif cycleDetectTable[k] then + -- recursion in the key detected + cycleDetectTable.detected = true + entry = "<"..table_ref(k)..">=" + else + entry = keytostring(k) .. "=" + end + + -- value part + if cycleDetectTable[v] then + -- recursion in the value detected! + cycleDetectTable.detected = true + entry = entry .. "<"..table_ref(v)..">" + else + entry = entry .. + prettystr_sub( v, indentLevel+1, printTableRefs, cycleDetectTable ) + end + count = count + 1 + result[count] = entry + end + return M.private._table_tostring_format_result( tbl, result, indentLevel, printTableRefs ) + end + +end +M.private._table_tostring = _table_tostring -- prettystr_sub() needs it + +local function _table_tostring_format_multiline_string( tbl_str, indentLevel ) + local indentString = '\n'..string.rep(" ", indentLevel - 1) + return table.concat( tbl_str, indentString ) + +end +M.private._table_tostring_format_multiline_string = _table_tostring_format_multiline_string + + +local function _table_tostring_format_result( tbl, result, indentLevel, printTableRefs ) + -- final function called in _table_to_string() to format the resulting list of + -- string describing the table. + + local dispOnMultLines = false + + -- set dispOnMultLines to true if the maximum LINE_LENGTH would be exceeded with the values + local totalLength = 0 + for k, v in ipairs( result ) do + totalLength = totalLength + string.len( v ) + if totalLength >= M.LINE_LENGTH then + dispOnMultLines = true + break + end + end + + -- set dispOnMultLines to true if the max LINE_LENGTH would be exceeded + -- with the values and the separators. + if not dispOnMultLines then + -- adjust with length of separator(s): + -- two items need 1 sep, three items two seps, ... plus len of '{}' + if #result > 0 then + totalLength = totalLength + TABLE_TOSTRING_SEP_LEN * (#result - 1) + end + dispOnMultLines = (totalLength + 2 >= M.LINE_LENGTH) + end + + -- now reformat the result table (currently holding element strings) + if dispOnMultLines then + local indentString = string.rep(" ", indentLevel - 1) + result = { + "{\n ", + indentString, + table.concat(result, ",\n " .. indentString), + "\n", + indentString, + "}" + } + else + result = {"{", table.concat(result, TABLE_TOSTRING_SEP), "}"} + end + if printTableRefs then + table.insert(result, 1, "<"..table_ref(tbl).."> ") -- prepend table ref + end + return table.concat(result) +end +M.private._table_tostring_format_result = _table_tostring_format_result -- prettystr_sub() needs it + +local function table_findkeyof(t, element) + -- Return the key k of the given element in table t, so that t[k] == element + -- (or `nil` if element is not present within t). Note that we use our + -- 'general' is_equal comparison for matching, so this function should + -- handle table-type elements gracefully and consistently. + if type(t) == "table" then + for k, v in pairs(t) do + if M.private.is_table_equals(v, element) then + return k + end + end + end + return nil +end + +local function _is_table_items_equals(actual, expected ) + local type_a, type_e = type(actual), type(expected) + + if type_a ~= type_e then + return false + + elseif (type_a == 'table') --[[and (type_e == 'table')]] then + for k, v in pairs(actual) do + if table_findkeyof(expected, v) == nil then + return false -- v not contained in expected + end + end + for k, v in pairs(expected) do + if table_findkeyof(actual, v) == nil then + return false -- v not contained in actual + end + end + return true + + elseif actual ~= expected then + return false + end + + return true +end + +--[[ +This is a specialized metatable to help with the bookkeeping of recursions +in _is_table_equals(). It provides an __index table that implements utility +functions for easier management of the table. The "cached" method queries +the state of a specific (actual,expected) pair; and the "store" method sets +this state to the given value. The state of pairs not "seen" / visited is +assumed to be `nil`. +]] +local _recursion_cache_MT = { + __index = { + -- Return the cached value for an (actual,expected) pair (or `nil`) + cached = function(t, actual, expected) + local subtable = t[actual] or {} + return subtable[expected] + end, + + -- Store cached value for a specific (actual,expected) pair. + -- Returns the value, so it's easy to use for a "tailcall" (return ...). + store = function(t, actual, expected, value, asymmetric) + local subtable = t[actual] + if not subtable then + subtable = {} + t[actual] = subtable + end + subtable[expected] = value + + -- Unless explicitly marked "asymmetric": Consider the recursion + -- on (expected,actual) to be equivalent to (actual,expected) by + -- default, and thus cache the value for both. + if not asymmetric then + t:store(expected, actual, value, true) + end + + return value + end + } +} + +local function _is_table_equals(actual, expected, cycleDetectTable, marginForAlmostEqual) + --[[Returns true if both table are equal. + + If argument marginForAlmostEqual is suppied, number comparison is done using alomstEqual instead + of strict equality. + + cycleDetectTable is an internal argument used during recursion on tables. + ]] + --print('_is_table_equals( \n '..prettystr(actual)..'\n , '..prettystr(expected).. + -- '\n , '..prettystr(cycleDetectTable)..'\n , '..prettystr(marginForAlmostEqual)..' )') + + local type_a, type_e = type(actual), type(expected) + + if type_a ~= type_e then + return false -- different types won't match + end + + if type_a == 'number' then + if marginForAlmostEqual ~= nil then + return M.almostEquals(actual, expected, marginForAlmostEqual) + else + return actual == expected + end + elseif type_a ~= 'table' then + -- other types compare directly + return actual == expected + end + + cycleDetectTable = cycleDetectTable or { actual={}, expected={} } + if cycleDetectTable.actual[ actual ] then + -- oh, we hit a cycle in actual + if cycleDetectTable.expected[ expected ] then + -- uh, we hit a cycle at the same time in expected + -- so the two tables have similar structure + return true + end + + -- cycle was hit only in actual, the structure differs from expected + return false + end + + if cycleDetectTable.expected[ expected ] then + -- no cycle in actual, but cycle in expected + -- the structure differ + return false + end + + -- at this point, no table cycle detected, we are + -- seeing this table for the first time + + -- mark the cycle detection + cycleDetectTable.actual[ actual ] = true + cycleDetectTable.expected[ expected ] = true + + + local actualKeysMatched = {} + for k, v in pairs(actual) do + actualKeysMatched[k] = true -- Keep track of matched keys + if not _is_table_equals(v, expected[k], cycleDetectTable, marginForAlmostEqual) then + -- table differs on this key + -- clear the cycle detection before returning + cycleDetectTable.actual[ actual ] = nil + cycleDetectTable.expected[ expected ] = nil + return false + end + end + + for k, v in pairs(expected) do + if not actualKeysMatched[k] then + -- Found a key that we did not see in "actual" -> mismatch + -- clear the cycle detection before returning + cycleDetectTable.actual[ actual ] = nil + cycleDetectTable.expected[ expected ] = nil + return false + end + -- Otherwise actual[k] was already matched against v = expected[k]. + end + + -- all key match, we have a match ! + cycleDetectTable.actual[ actual ] = nil + cycleDetectTable.expected[ expected ] = nil + return true +end +M.private._is_table_equals = _is_table_equals + +local function failure(main_msg, extra_msg_or_nil, level) + -- raise an error indicating a test failure + -- for error() compatibility we adjust "level" here (by +1), to report the + -- calling context + local msg + if type(extra_msg_or_nil) == 'string' and extra_msg_or_nil:len() > 0 then + msg = extra_msg_or_nil .. '\n' .. main_msg + else + msg = main_msg + end + error(M.FAILURE_PREFIX .. msg, (level or 1) + 1 + M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE) +end + +local function is_table_equals(actual, expected, marginForAlmostEqual) + return _is_table_equals(actual, expected, nil, marginForAlmostEqual) +end +M.private.is_table_equals = is_table_equals + +local function fail_fmt(level, extra_msg_or_nil, ...) + -- failure with printf-style formatted message and given error level + failure(string.format(...), extra_msg_or_nil, (level or 1) + 1) +end +M.private.fail_fmt = fail_fmt + +local function error_fmt(level, ...) + -- printf-style error() + error(string.format(...), (level or 1) + 1 + M.STRIP_EXTRA_ENTRIES_IN_STACK_TRACE) +end +M.private.error_fmt = error_fmt + +---------------------------------------------------------------- +-- +-- assertions +-- +---------------------------------------------------------------- + +local function errorMsgEquality(actual, expected, doDeepAnalysis, margin) + -- margin is supplied only for almost equal verification + + if not M.ORDER_ACTUAL_EXPECTED then + expected, actual = actual, expected + end + if type(expected) == 'string' or type(expected) == 'table' then + local strExpected, strActual = prettystrPairs(expected, actual) + local result = string.format("expected: %s\nactual: %s", strExpected, strActual) + if margin then + result = result .. '\nwere not equal by the margin of: '..prettystr(margin) + end + + -- extend with mismatch analysis if possible: + local success, mismatchResult + success, mismatchResult = tryMismatchFormatting( actual, expected, doDeepAnalysis, margin ) + if success then + result = table.concat( { result, mismatchResult }, '\n' ) + end + return result + end + return string.format("expected: %s, actual: %s", + prettystr(expected), prettystr(actual)) +end + +function M.assertError(f, ...) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + if pcall( f, ... ) then + failure( "Expected an error when calling function but no error generated", nil, 2 ) + end +end + +function M.fail( msg ) + -- stops a test due to a failure + failure( msg, nil, 2 ) +end + +function M.failIf( cond, msg ) + -- Fails a test with "msg" if condition is true + if cond then + failure( msg, nil, 2 ) + end +end + +function M.skip(msg) + -- skip a running test + error_fmt(2, M.SKIP_PREFIX .. msg) +end + +function M.skipIf( cond, msg ) + -- skip a running test if condition is met + if cond then + error_fmt(2, M.SKIP_PREFIX .. msg) + end +end + +function M.runOnlyIf( cond, msg ) + -- continue a running test if condition is met, else skip it + if not cond then + error_fmt(2, M.SKIP_PREFIX .. prettystr(msg)) + end +end + +function M.success() + -- stops a test with a success + error_fmt(2, M.SUCCESS_PREFIX) +end + +function M.successIf( cond ) + -- stops a test with a success if condition is met + if cond then + error_fmt(2, M.SUCCESS_PREFIX) + end +end + + +------------------------------------------------------------------ +-- Equality assertions +------------------------------------------------------------------ + +function M.assertEquals(actual, expected, extra_msg_or_nil, doDeepAnalysis) + if type(actual) == 'table' and type(expected) == 'table' then + if not is_table_equals(actual, expected) then + failure( errorMsgEquality(actual, expected, doDeepAnalysis), extra_msg_or_nil, 2 ) + end + elseif type(actual) ~= type(expected) then + failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 ) + elseif actual ~= expected then + failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 ) + end +end + +function M.almostEquals( actual, expected, margin ) + if type(actual) ~= 'number' or type(expected) ~= 'number' or type(margin) ~= 'number' then + error_fmt(3, 'almostEquals: must supply only number arguments.\nArguments supplied: %s, %s, %s', + prettystr(actual), prettystr(expected), prettystr(margin)) + end + if margin < 0 then + error_fmt(3, 'almostEquals: margin must not be negative, current value is ' .. margin) + end + return math.abs(expected - actual) <= margin +end + +function M.assertAlmostEquals( actual, expected, margin, extra_msg_or_nil ) + -- check that two floats are close by margin + margin = margin or M.EPS + if type(margin) ~= 'number' then + error_fmt(2, 'almostEquals: margin must be a number, not %s', prettystr(margin)) + end + + if type(actual) == 'table' and type(expected) == 'table' then + -- handle almost equals for table + if not is_table_equals(actual, expected, margin) then + failure( errorMsgEquality(actual, expected, nil, margin), extra_msg_or_nil, 2 ) + end + elseif type(actual) == 'number' and type(expected) == 'number' and type(margin) == 'number' then + if not M.almostEquals(actual, expected, margin) then + if not M.ORDER_ACTUAL_EXPECTED then + expected, actual = actual, expected + end + local delta = math.abs(actual - expected) + fail_fmt(2, extra_msg_or_nil, 'Values are not almost equal\n' .. + 'Actual: %s, expected: %s, delta %s above margin of %s', + actual, expected, delta, margin) + end + else + error_fmt(3, 'almostEquals: must supply only number or table arguments.\nArguments supplied: %s, %s, %s', + prettystr(actual), prettystr(expected), prettystr(margin)) + end +end + +function M.assertNotEquals(actual, expected, extra_msg_or_nil) + if type(actual) ~= type(expected) then + return + end + + if type(actual) == 'table' and type(expected) == 'table' then + if not is_table_equals(actual, expected) then + return + end + elseif actual ~= expected then + return + end + fail_fmt(2, extra_msg_or_nil, 'Received the not expected value: %s', prettystr(actual)) +end + +function M.assertNotAlmostEquals( actual, expected, margin, extra_msg_or_nil ) + -- check that two floats are not close by margin + margin = margin or M.EPS + if M.almostEquals(actual, expected, margin) then + if not M.ORDER_ACTUAL_EXPECTED then + expected, actual = actual, expected + end + local delta = math.abs(actual - expected) + fail_fmt(2, extra_msg_or_nil, 'Values are almost equal\nActual: %s, expected: %s' .. + ', delta %s below margin of %s', + actual, expected, delta, margin) + end +end + +function M.assertItemsEquals(actual, expected, extra_msg_or_nil) + -- checks that the items of table expected + -- are contained in table actual. Warning, this function + -- is at least O(n^2) + if not _is_table_items_equals(actual, expected ) then + expected, actual = prettystrPairs(expected, actual) + fail_fmt(2, extra_msg_or_nil, 'Content of the tables are not identical:\nExpected: %s\nActual: %s', + expected, actual) + end +end + +------------------------------------------------------------------ +-- String assertion +------------------------------------------------------------------ + +function M.assertStrContains( str, sub, isPattern, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + -- assert( type(str) == 'string', 'Argument 1 of assertStrContains() should be a string.' ) ) + -- assert( type(sub) == 'string', 'Argument 2 of assertStrContains() should be a string.' ) ) + if not string.find(str, sub, 1, not isPattern) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Could not find %s %s in string %s', + isPattern and 'pattern' or 'substring', sub, str) + end +end + +function M.assertStrIContains( str, sub, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + if not string.find(str:lower(), sub:lower(), 1, true) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Could not find (case insensitively) substring %s in string %s', + sub, str) + end +end + +function M.assertNotStrContains( str, sub, isPattern, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + if string.find(str, sub, 1, not isPattern) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Found the not expected %s %s in string %s', + isPattern and 'pattern' or 'substring', sub, str) + end +end + +function M.assertNotStrIContains( str, sub, extra_msg_or_nil ) + -- this relies on lua string.find function + -- a string always contains the empty string + if string.find(str:lower(), sub:lower(), 1, true) then + sub, str = prettystrPairs(sub, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Found (case insensitively) the not expected substring %s in string %s', + sub, str) + end +end + +function M.assertStrMatches( str, pattern, start, final, extra_msg_or_nil ) + -- Verify a full match for the string + if not strMatch( str, pattern, start, final ) then + pattern, str = prettystrPairs(pattern, str, '\n') + fail_fmt(2, extra_msg_or_nil, 'Could not match pattern %s with string %s', + pattern, str) + end +end + +local function _assertErrorMsgEquals( stripFileAndLine, expectedMsg, func, ... ) + local no_error, error_msg = pcall( func, ... ) + if no_error then + failure( 'No error generated when calling function but expected error: '..M.prettystr(expectedMsg), nil, 3 ) + end + if type(expectedMsg) == "string" and type(error_msg) ~= "string" then + -- table are converted to string automatically + error_msg = tostring(error_msg) + end + local differ = false + if stripFileAndLine then + if error_msg:gsub("^.+:%d+: ", "") ~= expectedMsg then + differ = true + end + else + if error_msg ~= expectedMsg then + local tr = type(error_msg) + local te = type(expectedMsg) + if te == 'table' then + if tr ~= 'table' then + differ = true + else + local ok = pcall(M.assertItemsEquals, error_msg, expectedMsg) + if not ok then + differ = true + end + end + else + differ = true + end + end + end + + if differ then + error_msg, expectedMsg = prettystrPairs(error_msg, expectedMsg) + fail_fmt(3, nil, 'Error message expected: %s\nError message received: %s\n', + expectedMsg, error_msg) + end +end + +function M.assertErrorMsgEquals( expectedMsg, func, ... ) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + _assertErrorMsgEquals(false, expectedMsg, func, ...) +end + +function M.assertErrorMsgContentEquals(expectedMsg, func, ...) + _assertErrorMsgEquals(true, expectedMsg, func, ...) +end + +function M.assertErrorMsgContains( partialMsg, func, ... ) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + local no_error, error_msg = pcall( func, ... ) + if no_error then + failure( 'No error generated when calling function but expected error containing: '..prettystr(partialMsg), nil, 2 ) + end + if type(error_msg) ~= "string" then + error_msg = tostring(error_msg) + end + if not string.find( error_msg, partialMsg, nil, true ) then + error_msg, partialMsg = prettystrPairs(error_msg, partialMsg) + fail_fmt(2, nil, 'Error message does not contain: %s\nError message received: %s\n', + partialMsg, error_msg) + end +end + +function M.assertErrorMsgMatches( expectedMsg, func, ... ) + -- assert that calling f with the arguments will raise an error + -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error + local no_error, error_msg = pcall( func, ... ) + if no_error then + failure( 'No error generated when calling function but expected error matching: "'..expectedMsg..'"', nil, 2 ) + end + if type(error_msg) ~= "string" then + error_msg = tostring(error_msg) + end + if not strMatch( error_msg, expectedMsg ) then + expectedMsg, error_msg = prettystrPairs(expectedMsg, error_msg) + fail_fmt(2, nil, 'Error message does not match pattern: %s\nError message received: %s\n', + expectedMsg, error_msg) + end +end + +------------------------------------------------------------------ +-- Type assertions +------------------------------------------------------------------ + +function M.assertEvalToTrue(value, extra_msg_or_nil) + if not value then + failure("expected: a value evaluating to true, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertEvalToFalse(value, extra_msg_or_nil) + if value then + failure("expected: false or nil, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsTrue(value, extra_msg_or_nil) + if value ~= true then + failure("expected: true, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsTrue(value, extra_msg_or_nil) + if value == true then + failure("expected: not true, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsFalse(value, extra_msg_or_nil) + if value ~= false then + failure("expected: false, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsFalse(value, extra_msg_or_nil) + if value == false then + failure("expected: not false, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsNil(value, extra_msg_or_nil) + if value ~= nil then + failure("expected: nil, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsNil(value, extra_msg_or_nil) + if value == nil then + failure("expected: not nil, actual: nil", extra_msg_or_nil, 2) + end +end + +--[[ +Add type assertion functions to the module table M. Each of these functions +takes a single parameter "value", and checks that its Lua type matches the +expected string (derived from the function name): + +M.assertIsXxx(value) -> ensure that type(value) conforms to "xxx" +]] +for _, funcName in ipairs( + {'assertIsNumber', 'assertIsString', 'assertIsTable', 'assertIsBoolean', + 'assertIsFunction', 'assertIsUserdata', 'assertIsThread'} +) do + local typeExpected = funcName:match("^assertIs([A-Z]%a*)$") + -- Lua type() always returns lowercase, also make sure the match() succeeded + typeExpected = typeExpected and typeExpected:lower() + or error("bad function name '"..funcName.."' for type assertion") + + M[funcName] = function(value, extra_msg_or_nil) + if type(value) ~= typeExpected then + if type(value) == 'nil' then + fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: nil', + typeExpected, type(value), prettystrPairs(value)) + else + fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: type %s, value %s', + typeExpected, type(value), prettystrPairs(value)) + end + end + end +end + +--[[ +Add shortcuts for verifying type of a variable, without failure (luaunit v2 compatibility) +M.isXxx(value) -> returns true if type(value) conforms to "xxx" +]] +for _, typeExpected in ipairs( + {'Number', 'String', 'Table', 'Boolean', + 'Function', 'Userdata', 'Thread', 'Nil' } +) do + local typeExpectedLower = typeExpected:lower() + local isType = function(value) + return (type(value) == typeExpectedLower) + end + M['is'..typeExpected] = isType + M['is_'..typeExpectedLower] = isType +end + +--[[ +Add non-type assertion functions to the module table M. Each of these functions +takes a single parameter "value", and checks that its Lua type differs from the +expected string (derived from the function name): + +M.assertNotIsXxx(value) -> ensure that type(value) is not "xxx" +]] +for _, funcName in ipairs( + {'assertNotIsNumber', 'assertNotIsString', 'assertNotIsTable', 'assertNotIsBoolean', + 'assertNotIsFunction', 'assertNotIsUserdata', 'assertNotIsThread'} +) do + local typeUnexpected = funcName:match("^assertNotIs([A-Z]%a*)$") + -- Lua type() always returns lowercase, also make sure the match() succeeded + typeUnexpected = typeUnexpected and typeUnexpected:lower() + or error("bad function name '"..funcName.."' for type assertion") + + M[funcName] = function(value, extra_msg_or_nil) + if type(value) == typeUnexpected then + fail_fmt(2, extra_msg_or_nil, 'expected: not a %s type, actual: value %s', + typeUnexpected, prettystrPairs(value)) + end + end +end + +function M.assertIs(actual, expected, extra_msg_or_nil) + if actual ~= expected then + if not M.ORDER_ACTUAL_EXPECTED then + actual, expected = expected, actual + end + local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG + M.PRINT_TABLE_REF_IN_ERROR_MSG = true + expected, actual = prettystrPairs(expected, actual, '\n', '') + M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg + fail_fmt(2, extra_msg_or_nil, 'expected and actual object should not be different\nExpected: %s\nReceived: %s', + expected, actual) + end +end + +function M.assertNotIs(actual, expected, extra_msg_or_nil) + if actual == expected then + local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG + M.PRINT_TABLE_REF_IN_ERROR_MSG = true + local s_expected + if not M.ORDER_ACTUAL_EXPECTED then + s_expected = prettystrPairs(actual) + else + s_expected = prettystrPairs(expected) + end + M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg + fail_fmt(2, extra_msg_or_nil, 'expected and actual object should be different: %s', s_expected ) + end +end + + +------------------------------------------------------------------ +-- Scientific assertions +------------------------------------------------------------------ + + +function M.assertIsNaN(value, extra_msg_or_nil) + if type(value) ~= "number" or value == value then + failure("expected: NaN, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsNaN(value, extra_msg_or_nil) + if type(value) == "number" and value ~= value then + failure("expected: not NaN, actual: NaN", extra_msg_or_nil, 2) + end +end + +function M.assertIsInf(value, extra_msg_or_nil) + if type(value) ~= "number" or math.abs(value) ~= math.huge then + failure("expected: #Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsPlusInf(value, extra_msg_or_nil) + if type(value) ~= "number" or value ~= math.huge then + failure("expected: #Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsMinusInf(value, extra_msg_or_nil) + if type(value) ~= "number" or value ~= -math.huge then + failure("expected: -#Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertNotIsPlusInf(value, extra_msg_or_nil) + if type(value) == "number" and value == math.huge then + failure("expected: not #Inf, actual: #Inf", extra_msg_or_nil, 2) + end +end + +function M.assertNotIsMinusInf(value, extra_msg_or_nil) + if type(value) == "number" and value == -math.huge then + failure("expected: not -#Inf, actual: -#Inf", extra_msg_or_nil, 2) + end +end + +function M.assertNotIsInf(value, extra_msg_or_nil) + if type(value) == "number" and math.abs(value) == math.huge then + failure("expected: not infinity, actual: " .. prettystr(value), extra_msg_or_nil, 2) + end +end + +function M.assertIsPlusZero(value, extra_msg_or_nil) + if type(value) ~= 'number' or value ~= 0 then + failure("expected: +0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + else if (1/value == -math.huge) then + -- more precise error diagnosis + failure("expected: +0.0, actual: -0.0", extra_msg_or_nil, 2) + else if (1/value ~= math.huge) then + -- strange, case should have already been covered + failure("expected: +0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end + end + end +end + +function M.assertIsMinusZero(value, extra_msg_or_nil) + if type(value) ~= 'number' or value ~= 0 then + failure("expected: -0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + else if (1/value == math.huge) then + -- more precise error diagnosis + failure("expected: -0.0, actual: +0.0", extra_msg_or_nil, 2) + else if (1/value ~= -math.huge) then + -- strange, case should have already been covered + failure("expected: -0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2) + end + end + end +end + +function M.assertNotIsPlusZero(value, extra_msg_or_nil) + if type(value) == 'number' and (1/value == math.huge) then + failure("expected: not +0.0, actual: +0.0", extra_msg_or_nil, 2) + end +end + +function M.assertNotIsMinusZero(value, extra_msg_or_nil) + if type(value) == 'number' and (1/value == -math.huge) then + failure("expected: not -0.0, actual: -0.0", extra_msg_or_nil, 2) + end +end + +function M.assertTableContains(t, expected, extra_msg_or_nil) + -- checks that table t contains the expected element + if table_findkeyof(t, expected) == nil then + t, expected = prettystrPairs(t, expected) + fail_fmt(2, extra_msg_or_nil, 'Table %s does NOT contain the expected element %s', + t, expected) + end +end + +function M.assertNotTableContains(t, expected, extra_msg_or_nil) + -- checks that table t doesn't contain the expected element + local k = table_findkeyof(t, expected) + if k ~= nil then + t, expected = prettystrPairs(t, expected) + fail_fmt(2, extra_msg_or_nil, 'Table %s DOES contain the unwanted element %s (at key %s)', + t, expected, prettystr(k)) + end +end + +---------------------------------------------------------------- +-- Compatibility layer +---------------------------------------------------------------- + +-- for compatibility with LuaUnit v2.x +function M.wrapFunctions() + -- In LuaUnit version <= 2.1 , this function was necessary to include + -- a test function inside the global test suite. Nowadays, the functions + -- are simply run directly as part of the test discovery process. + -- so just do nothing ! + io.stderr:write[[Use of WrapFunctions() is no longer needed. +Just prefix your test function names with "test" or "Test" and they +will be picked up and run by LuaUnit. +]] +end + +local list_of_funcs = { + -- { official function name , alias } + + -- general assertions + { 'assertEquals' , 'assert_equals' }, + { 'assertItemsEquals' , 'assert_items_equals' }, + { 'assertNotEquals' , 'assert_not_equals' }, + { 'assertAlmostEquals' , 'assert_almost_equals' }, + { 'assertNotAlmostEquals' , 'assert_not_almost_equals' }, + { 'assertEvalToTrue' , 'assert_eval_to_true' }, + { 'assertEvalToFalse' , 'assert_eval_to_false' }, + { 'assertStrContains' , 'assert_str_contains' }, + { 'assertStrIContains' , 'assert_str_icontains' }, + { 'assertNotStrContains' , 'assert_not_str_contains' }, + { 'assertNotStrIContains' , 'assert_not_str_icontains' }, + { 'assertStrMatches' , 'assert_str_matches' }, + { 'assertError' , 'assert_error' }, + { 'assertErrorMsgEquals' , 'assert_error_msg_equals' }, + { 'assertErrorMsgContains' , 'assert_error_msg_contains' }, + { 'assertErrorMsgMatches' , 'assert_error_msg_matches' }, + { 'assertErrorMsgContentEquals', 'assert_error_msg_content_equals' }, + { 'assertIs' , 'assert_is' }, + { 'assertNotIs' , 'assert_not_is' }, + { 'assertTableContains' , 'assert_table_contains' }, + { 'assertNotTableContains' , 'assert_not_table_contains' }, + { 'wrapFunctions' , 'WrapFunctions' }, + { 'wrapFunctions' , 'wrap_functions' }, + + -- type assertions: assertIsXXX -> assert_is_xxx + { 'assertIsNumber' , 'assert_is_number' }, + { 'assertIsString' , 'assert_is_string' }, + { 'assertIsTable' , 'assert_is_table' }, + { 'assertIsBoolean' , 'assert_is_boolean' }, + { 'assertIsNil' , 'assert_is_nil' }, + { 'assertIsTrue' , 'assert_is_true' }, + { 'assertIsFalse' , 'assert_is_false' }, + { 'assertIsNaN' , 'assert_is_nan' }, + { 'assertIsInf' , 'assert_is_inf' }, + { 'assertIsPlusInf' , 'assert_is_plus_inf' }, + { 'assertIsMinusInf' , 'assert_is_minus_inf' }, + { 'assertIsPlusZero' , 'assert_is_plus_zero' }, + { 'assertIsMinusZero' , 'assert_is_minus_zero' }, + { 'assertIsFunction' , 'assert_is_function' }, + { 'assertIsThread' , 'assert_is_thread' }, + { 'assertIsUserdata' , 'assert_is_userdata' }, + + -- type assertions: assertIsXXX -> assertXxx + { 'assertIsNumber' , 'assertNumber' }, + { 'assertIsString' , 'assertString' }, + { 'assertIsTable' , 'assertTable' }, + { 'assertIsBoolean' , 'assertBoolean' }, + { 'assertIsNil' , 'assertNil' }, + { 'assertIsTrue' , 'assertTrue' }, + { 'assertIsFalse' , 'assertFalse' }, + { 'assertIsNaN' , 'assertNaN' }, + { 'assertIsInf' , 'assertInf' }, + { 'assertIsPlusInf' , 'assertPlusInf' }, + { 'assertIsMinusInf' , 'assertMinusInf' }, + { 'assertIsPlusZero' , 'assertPlusZero' }, + { 'assertIsMinusZero' , 'assertMinusZero'}, + { 'assertIsFunction' , 'assertFunction' }, + { 'assertIsThread' , 'assertThread' }, + { 'assertIsUserdata' , 'assertUserdata' }, + + -- type assertions: assertIsXXX -> assert_xxx (luaunit v2 compat) + { 'assertIsNumber' , 'assert_number' }, + { 'assertIsString' , 'assert_string' }, + { 'assertIsTable' , 'assert_table' }, + { 'assertIsBoolean' , 'assert_boolean' }, + { 'assertIsNil' , 'assert_nil' }, + { 'assertIsTrue' , 'assert_true' }, + { 'assertIsFalse' , 'assert_false' }, + { 'assertIsNaN' , 'assert_nan' }, + { 'assertIsInf' , 'assert_inf' }, + { 'assertIsPlusInf' , 'assert_plus_inf' }, + { 'assertIsMinusInf' , 'assert_minus_inf' }, + { 'assertIsPlusZero' , 'assert_plus_zero' }, + { 'assertIsMinusZero' , 'assert_minus_zero' }, + { 'assertIsFunction' , 'assert_function' }, + { 'assertIsThread' , 'assert_thread' }, + { 'assertIsUserdata' , 'assert_userdata' }, + + -- type assertions: assertNotIsXXX -> assert_not_is_xxx + { 'assertNotIsNumber' , 'assert_not_is_number' }, + { 'assertNotIsString' , 'assert_not_is_string' }, + { 'assertNotIsTable' , 'assert_not_is_table' }, + { 'assertNotIsBoolean' , 'assert_not_is_boolean' }, + { 'assertNotIsNil' , 'assert_not_is_nil' }, + { 'assertNotIsTrue' , 'assert_not_is_true' }, + { 'assertNotIsFalse' , 'assert_not_is_false' }, + { 'assertNotIsNaN' , 'assert_not_is_nan' }, + { 'assertNotIsInf' , 'assert_not_is_inf' }, + { 'assertNotIsPlusInf' , 'assert_not_plus_inf' }, + { 'assertNotIsMinusInf' , 'assert_not_minus_inf' }, + { 'assertNotIsPlusZero' , 'assert_not_plus_zero' }, + { 'assertNotIsMinusZero' , 'assert_not_minus_zero' }, + { 'assertNotIsFunction' , 'assert_not_is_function' }, + { 'assertNotIsThread' , 'assert_not_is_thread' }, + { 'assertNotIsUserdata' , 'assert_not_is_userdata' }, + + -- type assertions: assertNotIsXXX -> assertNotXxx (luaunit v2 compat) + { 'assertNotIsNumber' , 'assertNotNumber' }, + { 'assertNotIsString' , 'assertNotString' }, + { 'assertNotIsTable' , 'assertNotTable' }, + { 'assertNotIsBoolean' , 'assertNotBoolean' }, + { 'assertNotIsNil' , 'assertNotNil' }, + { 'assertNotIsTrue' , 'assertNotTrue' }, + { 'assertNotIsFalse' , 'assertNotFalse' }, + { 'assertNotIsNaN' , 'assertNotNaN' }, + { 'assertNotIsInf' , 'assertNotInf' }, + { 'assertNotIsPlusInf' , 'assertNotPlusInf' }, + { 'assertNotIsMinusInf' , 'assertNotMinusInf' }, + { 'assertNotIsPlusZero' , 'assertNotPlusZero' }, + { 'assertNotIsMinusZero' , 'assertNotMinusZero' }, + { 'assertNotIsFunction' , 'assertNotFunction' }, + { 'assertNotIsThread' , 'assertNotThread' }, + { 'assertNotIsUserdata' , 'assertNotUserdata' }, + + -- type assertions: assertNotIsXXX -> assert_not_xxx + { 'assertNotIsNumber' , 'assert_not_number' }, + { 'assertNotIsString' , 'assert_not_string' }, + { 'assertNotIsTable' , 'assert_not_table' }, + { 'assertNotIsBoolean' , 'assert_not_boolean' }, + { 'assertNotIsNil' , 'assert_not_nil' }, + { 'assertNotIsTrue' , 'assert_not_true' }, + { 'assertNotIsFalse' , 'assert_not_false' }, + { 'assertNotIsNaN' , 'assert_not_nan' }, + { 'assertNotIsInf' , 'assert_not_inf' }, + { 'assertNotIsPlusInf' , 'assert_not_plus_inf' }, + { 'assertNotIsMinusInf' , 'assert_not_minus_inf' }, + { 'assertNotIsPlusZero' , 'assert_not_plus_zero' }, + { 'assertNotIsMinusZero' , 'assert_not_minus_zero' }, + { 'assertNotIsFunction' , 'assert_not_function' }, + { 'assertNotIsThread' , 'assert_not_thread' }, + { 'assertNotIsUserdata' , 'assert_not_userdata' }, + + -- all assertions with Coroutine duplicate Thread assertions + { 'assertIsThread' , 'assertIsCoroutine' }, + { 'assertIsThread' , 'assertCoroutine' }, + { 'assertIsThread' , 'assert_is_coroutine' }, + { 'assertIsThread' , 'assert_coroutine' }, + { 'assertNotIsThread' , 'assertNotIsCoroutine' }, + { 'assertNotIsThread' , 'assertNotCoroutine' }, + { 'assertNotIsThread' , 'assert_not_is_coroutine' }, + { 'assertNotIsThread' , 'assert_not_coroutine' }, +} + +-- Create all aliases in M +for _,v in ipairs( list_of_funcs ) do + local funcname, alias = v[1], v[2] + M[alias] = M[funcname] + + if EXPORT_ASSERT_TO_GLOBALS then + _G[funcname] = M[funcname] + _G[alias] = M[funcname] + end +end + +---------------------------------------------------------------- +-- +-- Outputters +-- +---------------------------------------------------------------- + +-- A common "base" class for outputters +-- For concepts involved (class inheritance) see http://www.lua.org/pil/16.2.html + +local genericOutput = { __class__ = 'genericOutput' } -- class +local genericOutput_MT = { __index = genericOutput } -- metatable +M.genericOutput = genericOutput -- publish, so that custom classes may derive from it + +function genericOutput.new(runner, default_verbosity) + -- runner is the "parent" object controlling the output, usually a LuaUnit instance + local t = { runner = runner } + if runner then + t.result = runner.result + t.verbosity = runner.verbosity or default_verbosity + t.fname = runner.fname + else + t.verbosity = default_verbosity + end + return setmetatable( t, genericOutput_MT) +end + +-- abstract ("empty") methods +function genericOutput:startSuite() + -- Called once, when the suite is started +end + +function genericOutput:startClass(className) + -- Called each time a new test class is started +end + +function genericOutput:startTest(testName) + -- called each time a new test is started, right before the setUp() + -- the current test status node is already created and available in: self.result.currentNode +end + +function genericOutput:updateStatus(node) + -- called with status failed or error as soon as the error/failure is encountered + -- this method is NOT called for a successful test because a test is marked as successful by default + -- and does not need to be updated +end + +function genericOutput:endTest(node) + -- called when the test is finished, after the tearDown() method +end + +function genericOutput:endClass() + -- called when executing the class is finished, before moving on to the next class of at the end of the test execution +end + +function genericOutput:endSuite() + -- called at the end of the test suite execution +end + + +---------------------------------------------------------------- +-- class TapOutput +---------------------------------------------------------------- + +local TapOutput = genericOutput.new() -- derived class +local TapOutput_MT = { __index = TapOutput } -- metatable +TapOutput.__class__ = 'TapOutput' + + -- For a good reference for TAP format, check: http://testanything.org/tap-specification.html + + function TapOutput.new(runner) + local t = genericOutput.new(runner, M.VERBOSITY_LOW) + return setmetatable( t, TapOutput_MT) + end + function TapOutput:startSuite() + print("1.."..self.result.selectedCount) + print('# Started on '..self.result.startDate) + end + function TapOutput:startClass(className) + if className ~= '[TestFunctions]' then + print('# Starting class: '..className) + end + end + + function TapOutput:updateStatus( node ) + if node:isSkipped() then + io.stdout:write("ok ", self.result.currentTestNumber, "\t# SKIP ", node.msg, "\n" ) + return + end + + io.stdout:write("not ok ", self.result.currentTestNumber, "\t", node.testName, "\n") + if self.verbosity > M.VERBOSITY_LOW then + print( prefixString( '# ', node.msg ) ) + end + if (node:isFailure() or node:isError()) and self.verbosity > M.VERBOSITY_DEFAULT then + print( prefixString( '# ', node.stackTrace ) ) + end + end + + function TapOutput:endTest( node ) + if node:isSuccess() then + io.stdout:write("ok ", self.result.currentTestNumber, "\t", node.testName, "\n") + end + end + + function TapOutput:endSuite() + print( '# '..M.LuaUnit.statusLine( self.result ) ) + return self.result.notSuccessCount + end + + +-- class TapOutput end + +---------------------------------------------------------------- +-- class JUnitOutput +---------------------------------------------------------------- + +-- See directory junitxml for more information about the junit format +local JUnitOutput = genericOutput.new() -- derived class +local JUnitOutput_MT = { __index = JUnitOutput } -- metatable +JUnitOutput.__class__ = 'JUnitOutput' + + function JUnitOutput.new(runner) + local t = genericOutput.new(runner, M.VERBOSITY_LOW) + t.testList = {} + return setmetatable( t, JUnitOutput_MT ) + end + + function JUnitOutput:startSuite() + -- open xml file early to deal with errors + if self.fname == nil then + error('With Junit, an output filename must be supplied with --name!') + end + if string.sub(self.fname,-4) ~= '.xml' then + self.fname = self.fname..'.xml' + end + self.fd = io.open(self.fname, "w") + if self.fd == nil then + error("Could not open file for writing: "..self.fname) + end + + print('# XML output to '..self.fname) + print('# Started on '..self.result.startDate) + end + function JUnitOutput:startClass(className) + if className ~= '[TestFunctions]' then + print('# Starting class: '..className) + end + end + function JUnitOutput:startTest(testName) + print('# Starting test: '..testName) + end + + function JUnitOutput:updateStatus( node ) + if node:isFailure() then + print( '# Failure: ' .. prefixString( '# ', node.msg ):sub(4, nil) ) + -- print('# ' .. node.stackTrace) + elseif node:isError() then + print( '# Error: ' .. prefixString( '# ' , node.msg ):sub(4, nil) ) + -- print('# ' .. node.stackTrace) + end + end + + function JUnitOutput:endSuite() + print( '# '..M.LuaUnit.statusLine(self.result)) + + -- XML file writing + self.fd:write('\n') + self.fd:write('\n') + self.fd:write(string.format( + ' \n', + self.result.runCount, self.result.startIsodate, self.result.duration, self.result.errorCount, self.result.failureCount, self.result.skippedCount )) + self.fd:write(" \n") + self.fd:write(string.format(' \n', _VERSION ) ) + self.fd:write(string.format(' \n', M.VERSION) ) + -- XXX please include system name and version if possible + self.fd:write(" \n") + + for i,node in ipairs(self.result.allTests) do + self.fd:write(string.format(' \n', + node.className, node.testName, node.duration ) ) + if node:isNotSuccess() then + self.fd:write(node:statusXML()) + end + self.fd:write(' \n') + end + + -- Next two lines are needed to validate junit ANT xsd, but really not useful in general: + self.fd:write(' \n') + self.fd:write(' \n') + + self.fd:write(' \n') + self.fd:write('\n') + self.fd:close() + return self.result.notSuccessCount + end + + +-- class TapOutput end + +---------------------------------------------------------------- +-- class TextOutput +---------------------------------------------------------------- + +--[[ Example of other unit-tests suite text output + +-- Python Non verbose: + +For each test: . or F or E + +If some failed tests: + ============== + ERROR / FAILURE: TestName (testfile.testclass) + --------- + Stack trace + + +then -------------- +then "Ran x tests in 0.000s" +then OK or FAILED (failures=1, error=1) + +-- Python Verbose: +testname (filename.classname) ... ok +testname (filename.classname) ... FAIL +testname (filename.classname) ... ERROR + +then -------------- +then "Ran x tests in 0.000s" +then OK or FAILED (failures=1, error=1) + +-- Ruby: +Started + . + Finished in 0.002695 seconds. + + 1 tests, 2 assertions, 0 failures, 0 errors + +-- Ruby: +>> ruby tc_simple_number2.rb +Loaded suite tc_simple_number2 +Started +F.. +Finished in 0.038617 seconds. + + 1) Failure: +test_failure(TestSimpleNumber) [tc_simple_number2.rb:16]: +Adding doesn't work. +<3> expected but was +<4>. + +3 tests, 4 assertions, 1 failures, 0 errors + +-- Java Junit +.......F. +Time: 0,003 +There was 1 failure: +1) testCapacity(junit.samples.VectorTest)junit.framework.AssertionFailedError + at junit.samples.VectorTest.testCapacity(VectorTest.java:87) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + +FAILURES!!! +Tests run: 8, Failures: 1, Errors: 0 + + +-- Maven + +# mvn test +------------------------------------------------------- + T E S T S +------------------------------------------------------- +Running math.AdditionTest +Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: +0.03 sec <<< FAILURE! + +Results : + +Failed tests: + testLireSymbole(math.AdditionTest) + +Tests run: 2, Failures: 1, Errors: 0, Skipped: 0 + + +-- LuaUnit +---- non verbose +* display . or F or E when running tests +---- verbose +* display test name + ok/fail +---- +* blank line +* number) ERROR or FAILURE: TestName + Stack trace +* blank line +* number) ERROR or FAILURE: TestName + Stack trace + +then -------------- +then "Ran x tests in 0.000s (%d not selected, %d skipped)" +then OK or FAILED (failures=1, error=1) + + +]] + +local TextOutput = genericOutput.new() -- derived class +local TextOutput_MT = { __index = TextOutput } -- metatable +TextOutput.__class__ = 'TextOutput' + + function TextOutput.new(runner) + local t = genericOutput.new(runner, M.VERBOSITY_DEFAULT) + t.errorList = {} + return setmetatable( t, TextOutput_MT ) + end + + function TextOutput:startSuite() + if self.verbosity > M.VERBOSITY_DEFAULT then + print( 'Started on '.. self.result.startDate ) + end + end + + function TextOutput:startTest(testName) + if self.verbosity > M.VERBOSITY_DEFAULT then + io.stdout:write( " ", self.result.currentNode.testName, " ... " ) + end + end + + function TextOutput:endTest( node ) + if node:isSuccess() then + if self.verbosity > M.VERBOSITY_DEFAULT then + io.stdout:write("Ok\n") + else + io.stdout:write(".") + io.stdout:flush() + end + else + if self.verbosity > M.VERBOSITY_DEFAULT then + print( node.status ) + print( node.msg ) + --[[ + -- find out when to do this: + if self.verbosity > M.VERBOSITY_DEFAULT then + print( node.stackTrace ) + end + ]] + else + -- write only the first character of status E, F or S + io.stdout:write(string.sub(node.status, 1, 1)) + io.stdout:flush() + end + end + end + + function TextOutput:displayOneFailedTest( index, fail ) + print(index..") "..fail.testName ) + print( fail.msg ) + print( fail.stackTrace ) + print() + end + + function TextOutput:displayErroredTests() + if #self.result.errorTests ~= 0 then + print("Tests with errors:") + print("------------------") + for i, v in ipairs(self.result.errorTests) do + self:displayOneFailedTest(i, v) + end + end + end + + function TextOutput:displayFailedTests() + if #self.result.failedTests ~= 0 then + print("Failed tests:") + print("-------------") + for i, v in ipairs(self.result.failedTests) do + self:displayOneFailedTest(i, v) + end + end + end + + function TextOutput:endSuite() + if self.verbosity > M.VERBOSITY_DEFAULT then + print("=========================================================") + else + print() + end + self:displayErroredTests() + self:displayFailedTests() + print( M.LuaUnit.statusLine( self.result ) ) + if self.result.notSuccessCount == 0 then + print('OK') + end + end + +-- class TextOutput end + + +---------------------------------------------------------------- +-- class NilOutput +---------------------------------------------------------------- + +local function nopCallable() + --print(42) + return nopCallable +end + +local NilOutput = { __class__ = 'NilOuptut' } -- class +local NilOutput_MT = { __index = nopCallable } -- metatable + +function NilOutput.new(runner) + return setmetatable( { __class__ = 'NilOutput' }, NilOutput_MT ) +end + +---------------------------------------------------------------- +-- +-- class LuaUnit +-- +---------------------------------------------------------------- + +M.LuaUnit = { + outputType = TextOutput, + verbosity = M.VERBOSITY_DEFAULT, + __class__ = 'LuaUnit', + instances = {} +} +local LuaUnit_MT = { __index = M.LuaUnit } + +if EXPORT_ASSERT_TO_GLOBALS then + LuaUnit = M.LuaUnit +end + + function M.LuaUnit.new() + local newInstance = setmetatable( {}, LuaUnit_MT ) + return newInstance + end + + -----------------[[ Utility methods ]]--------------------- + + function M.LuaUnit.asFunction(aObject) + -- return "aObject" if it is a function, and nil otherwise + if 'function' == type(aObject) then + return aObject + end + end + + function M.LuaUnit.splitClassMethod(someName) + --[[ + Return a pair of className, methodName strings for a name in the form + "class.method". If no class part (or separator) is found, will return + nil, someName instead (the latter being unchanged). + + This convention thus also replaces the older isClassMethod() test: + You just have to check for a non-nil className (return) value. + ]] + local separator = string.find(someName, '.', 1, true) + if separator then + return someName:sub(1, separator - 1), someName:sub(separator + 1) + end + return nil, someName + end + + function M.LuaUnit.isMethodTestName( s ) + -- return true is the name matches the name of a test method + -- default rule is that is starts with 'Test' or with 'test' + return string.sub(s, 1, 4):lower() == 'test' + end + + function M.LuaUnit.isTestName( s ) + -- return true is the name matches the name of a test + -- default rule is that is starts with 'Test' or with 'test' + return string.sub(s, 1, 4):lower() == 'test' + end + + function M.LuaUnit.collectTests() + -- return a list of all test names in the global namespace + -- that match LuaUnit.isTestName + + local testNames = {} + for k, _ in pairs(_G) do + if type(k) == "string" and M.LuaUnit.isTestName( k ) then + table.insert( testNames , k ) + end + end + table.sort( testNames ) + return testNames + end + + function M.LuaUnit.parseCmdLine( cmdLine ) + -- parse the command line + -- Supported command line parameters: + -- --verbose, -v: increase verbosity + -- --quiet, -q: silence output + -- --error, -e: treat errors as fatal (quit program) + -- --output, -o, + name: select output type + -- --pattern, -p, + pattern: run test matching pattern, may be repeated + -- --exclude, -x, + pattern: run test not matching pattern, may be repeated + -- --shuffle, -s, : shuffle tests before reunning them + -- --name, -n, + fname: name of output file for junit, default to stdout + -- --repeat, -r, + num: number of times to execute each test + -- [testnames, ...]: run selected test names + -- + -- Returns a table with the following fields: + -- verbosity: nil, M.VERBOSITY_DEFAULT, M.VERBOSITY_QUIET, M.VERBOSITY_VERBOSE + -- output: nil, 'tap', 'junit', 'text', 'nil' + -- testNames: nil or a list of test names to run + -- exeRepeat: num or 1 + -- pattern: nil or a list of patterns + -- exclude: nil or a list of patterns + + local result, state = {}, nil + local SET_OUTPUT = 1 + local SET_PATTERN = 2 + local SET_EXCLUDE = 3 + local SET_FNAME = 4 + local SET_REPEAT = 5 + + if cmdLine == nil then + return result + end + + local function parseOption( option ) + if option == '--help' or option == '-h' then + result['help'] = true + return + elseif option == '--version' then + result['version'] = true + return + elseif option == '--verbose' or option == '-v' then + result['verbosity'] = M.VERBOSITY_VERBOSE + return + elseif option == '--quiet' or option == '-q' then + result['verbosity'] = M.VERBOSITY_QUIET + return + elseif option == '--error' or option == '-e' then + result['quitOnError'] = true + return + elseif option == '--failure' or option == '-f' then + result['quitOnFailure'] = true + return + elseif option == '--shuffle' or option == '-s' then + result['shuffle'] = true + return + elseif option == '--output' or option == '-o' then + state = SET_OUTPUT + return state + elseif option == '--name' or option == '-n' then + state = SET_FNAME + return state + elseif option == '--repeat' or option == '-r' then + state = SET_REPEAT + return state + elseif option == '--pattern' or option == '-p' then + state = SET_PATTERN + return state + elseif option == '--exclude' or option == '-x' then + state = SET_EXCLUDE + return state + end + error('Unknown option: '..option,3) + end + + local function setArg( cmdArg, state ) + if state == SET_OUTPUT then + result['output'] = cmdArg + return + elseif state == SET_FNAME then + result['fname'] = cmdArg + return + elseif state == SET_REPEAT then + result['exeRepeat'] = tonumber(cmdArg) + or error('Malformed -r argument: '..cmdArg) + return + elseif state == SET_PATTERN then + if result['pattern'] then + table.insert( result['pattern'], cmdArg ) + else + result['pattern'] = { cmdArg } + end + return + elseif state == SET_EXCLUDE then + local notArg = '!'..cmdArg + if result['pattern'] then + table.insert( result['pattern'], notArg ) + else + result['pattern'] = { notArg } + end + return + end + error('Unknown parse state: '.. state) + end + + + for i, cmdArg in ipairs(cmdLine) do + if state ~= nil then + setArg( cmdArg, state, result ) + state = nil + else + if cmdArg:sub(1,1) == '-' then + state = parseOption( cmdArg ) + else + if result['testNames'] then + table.insert( result['testNames'], cmdArg ) + else + result['testNames'] = { cmdArg } + end + end + end + end + + if result['help'] then + M.LuaUnit.help() + end + + if result['version'] then + M.LuaUnit.version() + end + + if state ~= nil then + error('Missing argument after '..cmdLine[ #cmdLine ],2 ) + end + + return result + end + + function M.LuaUnit.help() + print(M.USAGE) + os.exit(0) + end + + function M.LuaUnit.version() + print('LuaUnit v'..M.VERSION..' by Philippe Fremy ') + os.exit(0) + end + +---------------------------------------------------------------- +-- class NodeStatus +---------------------------------------------------------------- + + local NodeStatus = { __class__ = 'NodeStatus' } -- class + local NodeStatus_MT = { __index = NodeStatus } -- metatable + M.NodeStatus = NodeStatus + + -- values of status + NodeStatus.SUCCESS = 'SUCCESS' + NodeStatus.SKIP = 'SKIP' + NodeStatus.FAIL = 'FAIL' + NodeStatus.ERROR = 'ERROR' + + function NodeStatus.new( number, testName, className ) + -- default constructor, test are PASS by default + local t = { number = number, testName = testName, className = className } + setmetatable( t, NodeStatus_MT ) + t:success() + return t + end + + function NodeStatus:success() + self.status = self.SUCCESS + -- useless because lua does this for us, but it helps me remembering the relevant field names + self.msg = nil + self.stackTrace = nil + end + + function NodeStatus:skip(msg) + self.status = self.SKIP + self.msg = msg + self.stackTrace = nil + end + + function NodeStatus:fail(msg, stackTrace) + self.status = self.FAIL + self.msg = msg + self.stackTrace = stackTrace + end + + function NodeStatus:error(msg, stackTrace) + self.status = self.ERROR + self.msg = msg + self.stackTrace = stackTrace + end + + function NodeStatus:isSuccess() + return self.status == NodeStatus.SUCCESS + end + + function NodeStatus:isNotSuccess() + -- Return true if node is either failure or error or skip + return (self.status == NodeStatus.FAIL or self.status == NodeStatus.ERROR or self.status == NodeStatus.SKIP) + end + + function NodeStatus:isSkipped() + return self.status == NodeStatus.SKIP + end + + function NodeStatus:isFailure() + return self.status == NodeStatus.FAIL + end + + function NodeStatus:isError() + return self.status == NodeStatus.ERROR + end + + function NodeStatus:statusXML() + if self:isError() then + return table.concat( + {' \n', + ' \n'}) + elseif self:isFailure() then + return table.concat( + {' \n', + ' \n'}) + elseif self:isSkipped() then + return table.concat({' ', xmlEscape(self.msg),'\n' } ) + end + return ' \n' -- (not XSD-compliant! normally shouldn't get here) + end + + --------------[[ Output methods ]]------------------------- + + local function conditional_plural(number, singular) + -- returns a grammatically well-formed string "%d " + local suffix = '' + if number ~= 1 then -- use plural + suffix = (singular:sub(-2) == 'ss') and 'es' or 's' + end + return string.format('%d %s%s', number, singular, suffix) + end + + function M.LuaUnit.statusLine(result) + -- return status line string according to results + local s = { + string.format('Ran %d tests in %0.3f seconds', + result.runCount, result.duration), + conditional_plural(result.successCount, 'success'), + } + if result.notSuccessCount > 0 then + if result.failureCount > 0 then + table.insert(s, conditional_plural(result.failureCount, 'failure')) + end + if result.errorCount > 0 then + table.insert(s, conditional_plural(result.errorCount, 'error')) + end + else + table.insert(s, '0 failures') + end + if result.skippedCount > 0 then + table.insert(s, string.format("%d skipped", result.skippedCount)) + end + if result.nonSelectedCount > 0 then + table.insert(s, string.format("%d non-selected", result.nonSelectedCount)) + end + return table.concat(s, ', ') + end + + function M.LuaUnit:startSuite(selectedCount, nonSelectedCount) + self.result = { + selectedCount = selectedCount, + nonSelectedCount = nonSelectedCount, + successCount = 0, + runCount = 0, + currentTestNumber = 0, + currentClassName = "", + currentNode = nil, + suiteStarted = true, + startTime = os.clock(), + startDate = os.date(os.getenv('LUAUNIT_DATEFMT')), + startIsodate = os.date('%Y-%m-%dT%H:%M:%S'), + patternIncludeFilter = self.patternIncludeFilter, + + -- list of test node status + allTests = {}, + failedTests = {}, + errorTests = {}, + skippedTests = {}, + + failureCount = 0, + errorCount = 0, + notSuccessCount = 0, + skippedCount = 0, + } + + self.outputType = self.outputType or TextOutput + self.output = self.outputType.new(self) + self.output:startSuite() + end + + function M.LuaUnit:startClass( className, classInstance ) + self.result.currentClassName = className + self.output:startClass( className ) + self:setupClass( className, classInstance ) + end + + function M.LuaUnit:startTest( testName ) + self.result.currentTestNumber = self.result.currentTestNumber + 1 + self.result.runCount = self.result.runCount + 1 + self.result.currentNode = NodeStatus.new( + self.result.currentTestNumber, + testName, + self.result.currentClassName + ) + self.result.currentNode.startTime = os.clock() + table.insert( self.result.allTests, self.result.currentNode ) + self.output:startTest( testName ) + end + + function M.LuaUnit:updateStatus( err ) + -- "err" is expected to be a table / result from protectedCall() + if err.status == NodeStatus.SUCCESS then + return + end + + local node = self.result.currentNode + + --[[ As a first approach, we will report only one error or one failure for one test. + + However, we can have the case where the test is in failure, and the teardown is in error. + In such case, it's a good idea to report both a failure and an error in the test suite. This is + what Python unittest does for example. However, it mixes up counts so need to be handled carefully: for + example, there could be more (failures + errors) count that tests. What happens to the current node ? + + We will do this more intelligent version later. + ]] + + -- if the node is already in failure/error, just don't report the new error (see above) + if node.status ~= NodeStatus.SUCCESS then + return + end + + if err.status == NodeStatus.FAIL then + node:fail( err.msg, err.trace ) + table.insert( self.result.failedTests, node ) + elseif err.status == NodeStatus.ERROR then + node:error( err.msg, err.trace ) + table.insert( self.result.errorTests, node ) + elseif err.status == NodeStatus.SKIP then + node:skip( err.msg ) + table.insert( self.result.skippedTests, node ) + else + error('No such status: ' .. prettystr(err.status)) + end + + self.output:updateStatus( node ) + end + + function M.LuaUnit:endTest() + local node = self.result.currentNode + -- print( 'endTest() '..prettystr(node)) + -- print( 'endTest() '..prettystr(node:isNotSuccess())) + node.duration = os.clock() - node.startTime + node.startTime = nil + self.output:endTest( node ) + + if node:isSuccess() then + self.result.successCount = self.result.successCount + 1 + elseif node:isError() then + if self.quitOnError or self.quitOnFailure then + -- Runtime error - abort test execution as requested by + -- "--error" option. This is done by setting a special + -- flag that gets handled in internalRunSuiteByInstances(). + print("\nERROR during LuaUnit test execution:\n" .. node.msg) + self.result.aborted = true + end + elseif node:isFailure() then + if self.quitOnFailure then + -- Failure - abort test execution as requested by + -- "--failure" option. This is done by setting a special + -- flag that gets handled in internalRunSuiteByInstances(). + print("\nFailure during LuaUnit test execution:\n" .. node.msg) + self.result.aborted = true + end + elseif node:isSkipped() then + self.result.runCount = self.result.runCount - 1 + else + error('No such node status: ' .. prettystr(node.status)) + end + self.result.currentNode = nil + end + + function M.LuaUnit:endClass() + self:teardownClass( self.lastClassName, self.lastClassInstance ) + self.output:endClass() + end + + function M.LuaUnit:endSuite() + if self.result.suiteStarted == false then + error('LuaUnit:endSuite() -- suite was already ended' ) + end + self.result.duration = os.clock()-self.result.startTime + self.result.suiteStarted = false + + -- Expose test counts for outputter's endSuite(). This could be managed + -- internally instead by using the length of the lists of failed tests + -- but unit tests rely on these fields being present. + self.result.failureCount = #self.result.failedTests + self.result.errorCount = #self.result.errorTests + self.result.notSuccessCount = self.result.failureCount + self.result.errorCount + self.result.skippedCount = #self.result.skippedTests + + self.output:endSuite() + end + + function M.LuaUnit:setOutputType(outputType, fname) + -- Configures LuaUnit runner output + -- outputType is one of: NIL, TAP, JUNIT, TEXT + -- when outputType is junit, the additional argument fname is used to set the name of junit output file + -- for other formats, fname is ignored + if outputType:upper() == "NIL" then + self.outputType = NilOutput + return + end + if outputType:upper() == "TAP" then + self.outputType = TapOutput + return + end + if outputType:upper() == "JUNIT" then + self.outputType = JUnitOutput + if fname then + self.fname = fname + end + return + end + if outputType:upper() == "TEXT" then + self.outputType = TextOutput + return + end + error( 'No such format: '..outputType,2) + end + + --------------[[ Runner ]]----------------- + + function M.LuaUnit:protectedCall(classInstance, methodInstance, prettyFuncName) + -- if classInstance is nil, this is just a function call + -- else, it's method of a class being called. + + local function err_handler(e) + -- transform error into a table, adding the traceback information + return { + status = NodeStatus.ERROR, + msg = e, + trace = string.sub(debug.traceback("", 1), 2) + } + end + + local ok, err + if classInstance then + -- stupid Lua < 5.2 does not allow xpcall with arguments so let's use a workaround + ok, err = xpcall( function () methodInstance(classInstance) end, err_handler ) + else + ok, err = xpcall( function () methodInstance() end, err_handler ) + end + if ok then + return {status = NodeStatus.SUCCESS} + end + -- print('ok="'..prettystr(ok)..'" err="'..prettystr(err)..'"') + + local iter_msg + iter_msg = self.exeRepeat and 'iteration '..self.currentCount + + err.msg, err.status = M.adjust_err_msg_with_iter( err.msg, iter_msg ) + + if err.status == NodeStatus.SUCCESS or err.status == NodeStatus.SKIP then + err.trace = nil + return err + end + + -- reformat / improve the stack trace + if prettyFuncName then -- we do have the real method name + err.trace = err.trace:gsub("in (%a+) 'methodInstance'", "in %1 '"..prettyFuncName.."'") + end + if STRIP_LUAUNIT_FROM_STACKTRACE then + err.trace = stripLuaunitTrace2(err.trace, err.msg) + end + + return err -- return the error "object" (table) + end + + + function M.LuaUnit:execOneFunction(className, methodName, classInstance, methodInstance) + -- When executing a test function, className and classInstance must be nil + -- When executing a class method, all parameters must be set + + if type(methodInstance) ~= 'function' then + self:unregisterSuite() + error( tostring(methodName)..' must be a function, not '..type(methodInstance)) + end + + local prettyFuncName + if className == nil then + className = '[TestFunctions]' + prettyFuncName = methodName + else + prettyFuncName = className..'.'..methodName + end + + if self.lastClassName ~= className then + if self.lastClassName ~= nil then + self:endClass() + end + self:startClass( className, classInstance ) + self.lastClassName = className + self.lastClassInstance = classInstance + end + + self:startTest(prettyFuncName) + + local node = self.result.currentNode + for iter_n = 1, self.exeRepeat or 1 do + if node:isNotSuccess() then + break + end + self.currentCount = iter_n + + -- run setUp first (if any) + if classInstance then + local func = self.asFunction( classInstance.setUp ) or + self.asFunction( classInstance.Setup ) or + self.asFunction( classInstance.setup ) or + self.asFunction( classInstance.SetUp ) + if func then + self:updateStatus(self:protectedCall(classInstance, func, className..'.setUp')) + end + end + + -- run testMethod() + if node:isSuccess() then + self:updateStatus(self:protectedCall(classInstance, methodInstance, prettyFuncName)) + end + + -- lastly, run tearDown (if any) + if classInstance then + local func = self.asFunction( classInstance.tearDown ) or + self.asFunction( classInstance.TearDown ) or + self.asFunction( classInstance.teardown ) or + self.asFunction( classInstance.Teardown ) + if func then + self:updateStatus(self:protectedCall(classInstance, func, className..'.tearDown')) + end + end + end + + self:endTest() + end + + function M.LuaUnit.expandOneClass( result, className, classInstance ) + --[[ + Input: a list of { name, instance }, a class name, a class instance + Ouptut: modify result to add all test method instance in the form: + { className.methodName, classInstance } + ]] + for methodName, methodInstance in sortedPairs(classInstance) do + if M.LuaUnit.asFunction(methodInstance) and M.LuaUnit.isMethodTestName( methodName ) then + table.insert( result, { className..'.'..methodName, classInstance } ) + end + end + end + + function M.LuaUnit.expandClasses( listOfNameAndInst ) + --[[ + -- expand all classes (provided as {className, classInstance}) to a list of {className.methodName, classInstance} + -- functions and methods remain untouched + + Input: a list of { name, instance } + + Output: + * { function name, function instance } : do nothing + * { class.method name, class instance }: do nothing + * { class name, class instance } : add all method names in the form of (className.methodName, classInstance) + ]] + local result = {} + + for i,v in ipairs( listOfNameAndInst ) do + local name, instance = v[1], v[2] + if M.LuaUnit.asFunction(instance) then + table.insert( result, { name, instance } ) + else + if type(instance) ~= 'table' then + error( 'Instance must be a table or a function, not a '..type(instance)..' with value '..prettystr(instance)) + end + local className, methodName = M.LuaUnit.splitClassMethod( name ) + if className then + local methodInstance = instance[methodName] + if methodInstance == nil then + error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) + end + table.insert( result, { name, instance } ) + else + M.LuaUnit.expandOneClass( result, name, instance ) + end + end + end + + return result + end + + function M.LuaUnit.applyPatternFilter( patternIncFilter, listOfNameAndInst ) + local included, excluded = {}, {} + for i, v in ipairs( listOfNameAndInst ) do + -- local name, instance = v[1], v[2] + if patternFilter( patternIncFilter, v[1] ) then + table.insert( included, v ) + else + table.insert( excluded, v ) + end + end + return included, excluded + end + + local function getKeyInListWithGlobalFallback( key, listOfNameAndInst ) + local result = nil + for i,v in ipairs( listOfNameAndInst ) do + if(listOfNameAndInst[i][1] == key) then + result = listOfNameAndInst[i][2] + break + end + end + if(not M.LuaUnit.asFunction( result ) ) then + result = _G[key] + end + return result + end + + function M.LuaUnit:setupSuite( listOfNameAndInst ) + local setupSuite = getKeyInListWithGlobalFallback("setupSuite", listOfNameAndInst) + if self.asFunction( setupSuite ) then + self:updateStatus( self:protectedCall( nil, setupSuite, 'setupSuite' ) ) + end + end + + function M.LuaUnit:teardownSuite(listOfNameAndInst) + local teardownSuite = getKeyInListWithGlobalFallback("teardownSuite", listOfNameAndInst) + if self.asFunction( teardownSuite ) then + self:updateStatus( self:protectedCall( nil, teardownSuite, 'teardownSuite') ) + end + end + + function M.LuaUnit:setupClass( className, instance ) + if type( instance ) == 'table' and self.asFunction( instance.setupClass ) then + self:updateStatus( self:protectedCall( instance, instance.setupClass, className..'.setupClass' ) ) + end + end + + function M.LuaUnit:teardownClass( className, instance ) + if type( instance ) == 'table' and self.asFunction( instance.teardownClass ) then + self:updateStatus( self:protectedCall( instance, instance.teardownClass, className..'.teardownClass' ) ) + end + end + + function M.LuaUnit:internalRunSuiteByInstances( listOfNameAndInst ) + --[[ Run an explicit list of tests. Each item of the list must be one of: + * { function name, function instance } + * { class name, class instance } + * { class.method name, class instance } + + This function is internal to LuaUnit. The official API to perform this action is runSuiteByInstances() + ]] + + local expandedList = self.expandClasses( listOfNameAndInst ) + if self.shuffle then + randomizeTable( expandedList ) + end + local filteredList, filteredOutList = self.applyPatternFilter( + self.patternIncludeFilter, expandedList ) + + self:startSuite( #filteredList, #filteredOutList ) + self:setupSuite( listOfNameAndInst ) + + for i,v in ipairs( filteredList ) do + local name, instance = v[1], v[2] + if M.LuaUnit.asFunction(instance) then + self:execOneFunction( nil, name, nil, instance ) + else + -- expandClasses() should have already taken care of sanitizing the input + assert( type(instance) == 'table' ) + local className, methodName = M.LuaUnit.splitClassMethod( name ) + assert( className ~= nil ) + local methodInstance = instance[methodName] + assert(methodInstance ~= nil) + self:execOneFunction( className, methodName, instance, methodInstance ) + end + if self.result.aborted then + break -- "--error" or "--failure" option triggered + end + end + + if self.lastClassName ~= nil then + self:endClass() + end + + self:teardownSuite( listOfNameAndInst ) + self:endSuite() + + if self.result.aborted then + print("LuaUnit ABORTED (as requested by --error or --failure option)") + self:unregisterSuite() + os.exit(-2) + end + end + + function M.LuaUnit:internalRunSuiteByNames( listOfName ) + --[[ Run LuaUnit with a list of generic names, coming either from command-line or from global + namespace analysis. Convert the list into a list of (name, valid instances (table or function)) + and calls internalRunSuiteByInstances. + ]] + + local instanceName, instance + local listOfNameAndInst = {} + + for i,name in ipairs( listOfName ) do + local className, methodName = M.LuaUnit.splitClassMethod( name ) + if className then + instanceName = className + instance = _G[instanceName] + + if instance == nil then + self:unregisterSuite() + error( "No such name in global space: "..instanceName ) + end + + if type(instance) ~= 'table' then + self:unregisterSuite() + error( 'Instance of '..instanceName..' must be a table, not '..type(instance)) + end + + local methodInstance = instance[methodName] + if methodInstance == nil then + self:unregisterSuite() + error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) + end + + else + -- for functions and classes + instanceName = name + instance = _G[instanceName] + end + + if instance == nil then + self:unregisterSuite() + error( "No such name in global space: "..instanceName ) + end + + if (type(instance) ~= 'table' and type(instance) ~= 'function') then + self:unregisterSuite() + error( 'Name must match a function or a table: '..instanceName ) + end + + table.insert( listOfNameAndInst, { name, instance } ) + end + + self:internalRunSuiteByInstances( listOfNameAndInst ) + end + + function M.LuaUnit.run(...) + -- Run some specific test classes. + -- If no arguments are passed, run the class names specified on the + -- command line. If no class name is specified on the command line + -- run all classes whose name starts with 'Test' + -- + -- If arguments are passed, they must be strings of the class names + -- that you want to run or generic command line arguments (-o, -p, -v, ...) + local runner = M.LuaUnit.new() + return runner:runSuite(...) + end + + function M.LuaUnit:registerSuite() + -- register the current instance into our global array of instances + -- print('-> Register suite') + M.LuaUnit.instances[ #M.LuaUnit.instances+1 ] = self + end + + function M.unregisterCurrentSuite() + -- force unregister the last registered suite + table.remove(M.LuaUnit.instances, #M.LuaUnit.instances) + end + + function M.LuaUnit:unregisterSuite() + -- print('<- Unregister suite') + -- remove our current instqances from the global array of instances + local instanceIdx = nil + for i, instance in ipairs(M.LuaUnit.instances) do + if instance == self then + instanceIdx = i + break + end + end + + if instanceIdx ~= nil then + table.remove(M.LuaUnit.instances, instanceIdx) + -- print('Unregister done') + end + + end + + function M.LuaUnit:initFromArguments( ... ) + --[[Parses all arguments from either command-line or direct call and set internal + flags of LuaUnit runner according to it. + + Return the list of names which were possibly passed on the command-line or as arguments + ]] + local args = {...} + if type(args[1]) == 'table' and args[1].__class__ == 'LuaUnit' then + -- run was called with the syntax M.LuaUnit:runSuite() + -- we support both M.LuaUnit.run() and M.LuaUnit:run() + -- strip out the first argument self to make it a command-line argument list + table.remove(args,1) + end + + if #args == 0 then + args = cmdline_argv + end + + local options = pcall_or_abort( M.LuaUnit.parseCmdLine, args ) + + -- We expect these option fields to be either `nil` or contain + -- valid values, so it's safe to always copy them directly. + self.verbosity = options.verbosity + self.quitOnError = options.quitOnError + self.quitOnFailure = options.quitOnFailure + + self.exeRepeat = options.exeRepeat + self.patternIncludeFilter = options.pattern + self.shuffle = options.shuffle + + options.output = options.output or os.getenv('LUAUNIT_OUTPUT') + options.fname = options.fname or os.getenv('LUAUNIT_JUNIT_FNAME') + + if options.output then + if options.output:lower() == 'junit' and options.fname == nil then + print('With junit output, a filename must be supplied with -n or --name') + os.exit(-1) + end + pcall_or_abort(self.setOutputType, self, options.output, options.fname) + end + + return options.testNames + end + + function M.LuaUnit:runSuite( ... ) + testNames = self:initFromArguments(...) + self:registerSuite() + self:internalRunSuiteByNames( testNames or M.LuaUnit.collectTests() ) + self:unregisterSuite() + return self.result.notSuccessCount + end + + function M.LuaUnit:runSuiteByInstances( listOfNameAndInst, commandLineArguments ) + --[[ + Run all test functions or tables provided as input. + + Input: a list of { name, instance } + instance can either be a function or a table containing test functions starting with the prefix "test" + + return the number of failures and errors, 0 meaning success + ]] + -- parse the command-line arguments + testNames = self:initFromArguments( commandLineArguments ) + self:registerSuite() + self:internalRunSuiteByInstances( listOfNameAndInst ) + self:unregisterSuite() + return self.result.notSuccessCount + end + + + +-- class LuaUnit + +-- For compatbility with LuaUnit v2 +M.run = M.LuaUnit.run +M.Run = M.LuaUnit.run + +function M:setVerbosity( verbosity ) + -- set the verbosity value (as integer) + M.LuaUnit.verbosity = verbosity +end +M.set_verbosity = M.setVerbosity +M.SetVerbosity = M.setVerbosity + + +return M \ No newline at end of file diff --git a/luci-app-gpoint-main/test/matrix_test.lua b/luci-app-gpoint-main/test/matrix_test.lua new file mode 100644 index 000000000..a4b615a0c --- /dev/null +++ b/luci-app-gpoint-main/test/matrix_test.lua @@ -0,0 +1,46 @@ + +local matrix = require("matrix_lib") + + +function test_copy() + local foo = matrix.create(3, 3) + local bar = matrix.create(3, 3) + foo[1][1] = 1337.0 + bar = matrix.copy(foo, bar) + + assert(bar[1][1] == 1337.0) +end + +function test_inverse() + local foo = matrix.create(4, 4) + foo = matrix.set(foo, 1.0, 2.0, 3.0, 4.0, + 4.0, 1.0, 7.0, 9.0, + 0.0, 0.0, -4.0, -4.0, + 2.3, 3.4, 3.1, 0.0) + + local foo_copy = matrix.copy(foo) + local bar = matrix.create(4, 4) + local identity = matrix.create(4, 4) + + identity = matrix.set_identity(identity) + + matrix.print(foo) + print("--------------") + matrix.print(bar) + print("--------------") + assert(matrix.destructive_invert(foo, bar)) + matrix.print(foo) + print("--------------") + matrix.print(bar) + print("--------------") + + assert(matrix.equal(foo, identity, 0.0001)) + foo = matrix.multiply(foo_copy, bar, foo) + assert(matrix.equal(foo, identity, 0.0001)) + foo = matrix.multiply(bar, foo_copy, foo) + assert(matrix.equal(foo, identity, 0.0001)) +end + +test_copy() +test_inverse() +print("OK") \ No newline at end of file diff --git a/luci-app-gpoint-main/test/nmea_test.lua b/luci-app-gpoint-main/test/nmea_test.lua new file mode 100644 index 000000000..b6e4721ee --- /dev/null +++ b/luci-app-gpoint-main/test/nmea_test.lua @@ -0,0 +1,547 @@ +common_path = '/usr/share/gpoint/tests/luaunitlib/?.lua;' +package.path = common_path .. package.path + +lu = require('luaunit') +---------------------------------------------------------------------------- +-- nmea test + +-- Bitxor +-- checkcrc +-- getcropdata +---------------------------------------------------------------------------- + +function createGnssForm() + local GnssData = { + warning = {app, gga, rmc, vtg, gsa, gp} + } + return GnssData +end +-- To calculate the checksum +-- Bitwise xor +local function BitXOR(a, b) + local p, c = 1, 0 + while a > 0 and b > 0 do + local ra, rb = a % 2, b % 2 + if ra ~= rb then c = c + p end + a, b, p = (a - ra) / 2, (b - rb) / 2, p * 2 + end + + if a < b then a = b end + while a > 0 do + local ra = a % 2 + if ra > 0 then c = c + p end + a, p = (a - ra) / 2, p * 2 + end + return c +end + +-- To calculate the checksum +function decimalToHex(num) + if num == 0 then + return '0' + end + + local neg = false + if num < 0 then + neg = true + num = num * -1 + end + + local hexstr = "0123456789ABCDEF" + local result = "" + while num > 0 do + local n = math.mod(num, 16) + result = string.sub(hexstr, n + 1, n + 1) .. result + num = math.floor(num / 16) + end + + if neg then + result = '-' .. result + end + return result +end + +-- Сalculate the checksum (CRC-8) +function checkCRC(data) + local crc8 = string.sub(data, #data - 1) + data = string.sub(data, 2, #data - 3) + + local b_sum = string.byte(data, 1) + for i = 2, #data do + b_sum = BitXOR(b_sum, string.byte(data, i)) + end + + return decimalToHex(b_sum) == crc8 and true or false +end + +--Converting coordinates from the NMEA protocol to degrees +function nmeaCoordinatesToDouble(coord) + local deg = math.floor(coord / 100) + return deg + (coord - 100 * deg) / 60 +end + +--We are looking for the desired data line in the line received from the device +function findInResp(data, begin) + local err = true + local b = string.find(data, begin) + local e = string.find(data, "\r\n", b) + + if b and e then + err = false + else + b = nil + e = nil + end + return err, b, e +end + +function getCropData(data, msg) + local err, b, e = findInResp(data, msg) + if not err then + data = string.gsub(string.sub(data, b, e), '%c', "") + if checkCRC(data) then + data = string.gsub(data, msg, '', 1) + data = string.gsub(data, "*%d+%w+", '', 1) + err = {false, "OK"} + else + err = {true, "Checksum error"} + data = nil + end + else + err = {true, "No data found"} + data = nil + end + return err, data +end + +-- Creating a table with data before adding data to a single space +function doTable(data, keys) + local tabl = {} + + while string.find(data, ',,') do + data = string.gsub(data, ',,', ",-,") + end + + local i = 1 + for val in string.gmatch(data, "[^,]+") do + tabl[keys[i]] = val + i = i + 1 + end + return tabl +end + +-- The function of searching the time zone by the received coordinates +function findTimeZone(time, date, lon) + local datetime = { year,month,day,hour,min,sec } + -- calculate the time zone by coordinates + local timeZone = math.floor((tonumber(lon) + (7.5 * (tonumber(lon) > 0 and 1.0 or -1.0))) / 15.0) + + datetime.hour, datetime.min, datetime.sec = string.match(time, "(%d%d)(%d%d)(%d%d)") + datetime.day, datetime.month, datetime.year = string.match(date,"(%d%d)(%d%d)(%d%d)") + datetime.year = "20" .. datetime.year -- Someone change this to 21 in the 2100 year + + --we request the unix time and then add the time zone + local unix = os.time(datetime) + unix = unix + ((math.floor(timeZone* 100)) % 100) * 36 + unix = unix + math.floor(timeZone) * 3600 + + return os.date("*t", unix) +end +-- Add 0 for the time and date values if < 10 +function addZero(val) + return tonumber(val) > 9 and tostring(val) or '0' .. tostring(val) +end + +--------------------------------------------------------------------------------------------------------------- +-- GGA - Global Positioning System Fix Data +function getGGA(resp) + local err, gga = getCropData(resp, "$GPGGA,") + if not err[1] then + local ggakeys = { + "utc", -- UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds. + "latitude", -- Latitude, dd is degrees, mm.mm is minutes + "ne", -- N or S (North or South) + "longitude", -- Longitude, dd is degrees, mm.mm is minutes + "ew", -- E or W (East or West) + "qual", -- GPS Quality Indicator (non null) + "sat", -- Number of satellites in use, 00 - 12 + "hdp", -- Horizontal Dilution of precision (meters) + "alt", -- Antenna Altitude above/below mean-sea-level (geoid) (in meters) + "ualt", -- Units of antenna altitude, meters + "gsep", -- Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level + "ugsep", -- Units of geoidal separation, meters + "age", -- Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, null field when DGPS is not used + "drs" -- Differential reference station ID, 0000-1023 + } + + if string.gsub(gga, ',', '') ~= '0' then + gga = doTable(gga, ggakeys) + else + err = {true, "Bad GGA data"} + gga = nil + end + end + return err, gga +end + +-- RMC - Recommended Minimum Navigation Information +function getRMC(resp) + local err, rmc = getCropData(resp, "$GPRMC,") + if not err[1] then + local rmckeys = { + "utc", -- UTC of position fix, hh is hours, mm is minutes, ss.ss is seconds. + "valid", -- Status, A = Valid, V = Warning + "latitude", -- Latitude, dd is degrees. mm.mm is minutes. + "ns", -- N or S + "longitude", -- Longitude, ddd is degrees. mm.mm is minutes. + "ew", -- E or W + "knots", -- Speed over ground, knots + "tmgdt", -- Track made good, degrees true + "date", -- Date, ddmmyy + "mv", -- Magnetic Variation, degrees + "ewm", -- E or W + "nstat" -- Nav Status (NMEA 4.1 and later) A=autonomous, D=differential, E=Estimated, -> + -- -> M=Manual input mode N=not valid, S=Simulator, V = Valid + } + + if not string.find(rmc, 'V') then + rmc = doTable(rmc, rmckeys) + else + err = {true, "Bad RMC data"} + rmc = nil + end + end + return err, rmc +end + +-- VTG - Track made good and Ground speed +function getVTG(resp) + local err, vtg = getCropData(resp, "$GPVTG,") + if not err[1] then + local vtgkeys = { + "course_t", -- Course over ground, degrees True + 't', -- T = True + "course_m", -- Course over ground, degrees Magnetic + 'm', -- M = Magnetic + "knots", -- Speed over ground, knots + 'n', -- N = Knots + "speed", -- Speed over ground, km/hr + 'k', -- K = Kilometers Per Hour + "faa" -- FAA mode indicator (NMEA 2.3 and later) + } + + if string.find(vtg, 'A') or string.find(vtg, 'D') then + vtg = doTable(vtg, vtgkeys) + else + err = {true, "Bad VTG data"} + vtg = nil + end + end + return err, vtg +end + +--GSA - GPS DOP and active satellites +function getGSA(resp) + local err, gsa = getCropData(resp, "$GPGSA,") + if not err[1] then + local gsakeys = { + "smode", -- Selection mode: M=Manual, forced to operate in 2D or 3D, A=Automatic, 2D/3D + "mode", -- Mode (1 = no fix, 2 = 2D fix, 3 = 3D fix) + "id1", -- ID of 1st satellite used for fix + "id2", -- ID of 2nd satellite used for fix + "id3", -- ID of 3rd satellite used for fix + "id4", -- ID of 4th satellite used for fix + "id5", -- ID of 5th satellite used for fix + "id6", -- ID of 6th satellite used for fix + "id7", -- ID of 7th satellite used for fix + "id8", -- ID of 8th satellite used for fix + "id9", -- ID of 9th satellite used for fix + "id10", -- ID of 10th satellite used for fix + "id11", -- ID of 11th satellite used for fix + "id12", -- ID of 12th satellite used for fix + "pdop", -- PDOP + "hdop", -- HDOP + "vdop" -- VDOP + } + + if string.find(gsa, '2') then + gsa = doTable(gsa, gsakeys) + else + err = {true, "Bad GSA data"} + gsa = nil + end + end + return err, gsa +end + +function parseAllData(resp) + local GnssData = createGnssForm() + GnssData.warning.gga, GnssData["GGA"] = getGGA(resp) + GnssData.warning.rmc, GnssData["RMC"] = getRMC(resp) + GnssData.warning.vtg, GnssData["VTG"] = getVTG(resp) + GnssData.warning.gsa, GnssData["GSA"] = getGSA(resp) + return GnssData +end + +-- This function prepares data for the web application (Some custom data) +function getGPoint(resp) + + local web = { + longitude = '-', + latitude = '-', + altitude = '-', + utc = '-', + date = '-', + nsat = '-', + hdop = '-', + cog = '-', + spkm = '-' + } + + local GnssData = parseAllData(resp) + local err = {true, ""} + + if not GnssData.warning.gga[1] then + web.latitude = string.format("%0.6f", nmeaCoordinatesToDouble(GnssData.GGA.latitude)) + web.longitude = string.format("%0.6f",nmeaCoordinatesToDouble(GnssData.GGA.longitude)) + web.altitude = GnssData.GGA.alt + web.nsat = GnssData.GGA.sat + web.hdop = GnssData.GGA.hdp + else + err[2] = "GGA: " .. GnssData.warning.gga[2] .. ' ' + end + + if not GnssData.warning.vtg[1] then + web.cog = GnssData.VTG.course_t + web.spkm = GnssData.VTG.speed + else + err[2] = err[2] .. "VTG: " .. GnssData.warning.vtg[2] .. ' ' + end + + if not GnssData.warning.rmc[1] then + local dateTime = findTimeZone(GnssData.RMC.utc, GnssData.RMC.date, nmeaCoordinatesToDouble(GnssData.RMC.longitude)) + + web.utc = string.format("%s:%s", addZero(dateTime.hour), addZero(dateTime.min)) + web.date = string.format("%s.%s.%d", addZero(dateTime.day), addZero(dateTime.month), dateTime.year) + else + err[2] = err[2] .. "RMC: " .. GnssData.warning.vtg[2] + end + + if GnssData.warning.gga[1] and GnssData.warning.vtg[1] and GnssData.warning.rmc[1] then + err[2] = "Updating data..." + else + err = {false, "OK"} + end + + return err, web +end + +----------------------------------------------------------------------------------- +-- public +----------------------------------------------------------------------------------- + +function getData(line, port) + local err, resp = serial.read(port) + local GnssData = createGnssForm() + if err[1] then + GnssData.warning.app = {true, err[2]} + return GnssData + end + + GnssData.warning.app = {false, "OK"} + + if line == "GP" then + GnssData.warning.gp, GnssData["gp"] = getGPoint(resp) + elseif line == "GGA" then + GnssData.warning.gga, GnssData["gga"] = getGGA(resp) + elseif line == "RMC" then + GnssData.warning.rmc, GnssData["rmc"] = getRMC(resp) + elseif line == "VTG" then + GnssData.warning.vtg, GnssData["vtg"] = getVTG(resp) + elseif line == "GSA" then + GnssData.warning.gsa, GnssData["gsa"] = getGSA(resp) + else + GnssData.warning.app = {true, "Bad argument..."} + end + return GnssData +end + +function getAllData(port) + local err, resp = serial.read(port) + local GnssData = createGnssForm() + if err[1] then + GnssData.warning.app = {true, err[2]} + return GnssData + end + + GnssData = parseAllData(resp) + GnssData.warning.gp, GnssData["gp"] = getGPoint(resp) + GnssData.warning.app = {false, "OK"} + return GnssData +end + +------------------------------------------------------------------------------ + +local goodGNSSSdata = { + "$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*4E", + "$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*52", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*3F", + "$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*66", + "$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*73", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*36", + "$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*69", + "$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*7D", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*3C", + "$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*6E", + "$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*77", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*32", + "$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*65", + "$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*7A", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*34", + "$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*49", + "$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*7E", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*36", + "$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*53", + "$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*76", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*30", + "$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*4E", + "$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*71", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*37", + "$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*4F", + "$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*73", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*37", + "$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*4B", + "$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*71", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*34", + "$GPRMC,113732.568,V,4154.949,N,08002.497,W,47.2,0.20,220721,,E*4A", + "$GPGGA,113733.568,4154.949,N,08002.497,W,0,00,0.2,,M,,M,,*72", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.3,0.8,0.2*39", + "$GPRMC,113735.568,V,4154.951,N,08002.496,W,31.1,0.13,220721,,E*47", + "$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*75", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*35", + "$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*47", + "$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*76", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*39", + "$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*41", + "$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*73", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*30", + "$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*44", + "$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*76", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*3B", + "$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*4D", + "$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*7C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*35", + "$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*40", + "$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*7C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*36", + "$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*41", + "$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*7B", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*37", + "$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*4E", + "$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*71", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*37" +} + +local badGNSSSdata = { + "$GPRMC,113702.568,V,4154.931,N,08002.497,W,95.5,0.02,220721,,E*48", + "$GPGGA,113703.568,4154.931,N,08002.497,W,0,00,,,M,,M,,*54", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.1,0.8,0.6*33", + "$GPRMC,113720.568,V,4154.942,N,08002.498,W,0.2,-0.02,220721,,E*5F", + "$GPGGA,113721.568,4154.942,N,08002.498,W,0,00,0.1,,M,,M,,*82", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.5,0.7*35", + "$GPRMC,113723.568,V,4154.944,N,08002.498,W,53.6,0.05,220721,,E*5A", + "$GPGGA,113724.568,4154.944,N,08002.498,W,0,00,0.5,,M,,M,,*12", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,0.6,0.1*35", + "$GPRMC,113726.568,V,4154.946,N,08002.498,W,76.6,0.04,220721,,E*9E", + "$GPGGA,113727.568,4154.946,N,08002.498,W,0,00,0.6,,M,,M,,*94", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.2,0.3,0.7*32", + "$GPRMC,113729.568,V,4154.948,N,08002.497,W,30.9,0.12,220721,,E*9C", + "$GPGGA,113730.568,4154.948,N,08002.497,W,0,00,0.3,,M,,M,,*79", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.2,0.2*39", + "$GPRMC,113705.568,V,4154.933,N,08002.497,W,86.0,-0.05,220721,,E*90", + "$GPGGA,113706.568,4154.933,N,08002.497,W,0,00,0.8,,M,,M,,*42", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.0,0.4*43", + "$GPRMC,113708.568,V,4154.935,N,08002.498,W,55.1,-0.10,220721,,E*44", + "$GPGGA,113709.568,4154.935,N,08002.498,W,0,00,0.0,,M,,M,,*4A", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.2,0.8*4D", + "$GPRMC,113711.568,V,4154.937,N,08002.498,W,95.0,-0.10,220721,,E*44", + "$GPGGA,113712.568,4154.937,N,08002.498,W,0,00,0.2,,M,,M,,*44", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.7,0.6,0.3*4D", + "$GPRMC,113714.568,V,4154.939,N,08002.498,W,28.0,-0.07,220721,,E*4D", + "$GPGGA,113715.568,4154.939,N,08002.498,W,0,00,0.6,,M,,M,,*4D", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,1.0,1.0,0.5*24", + "$GPRMC,113717.568,V,4154.940,N,08002.498,W,30.1,0.03,220721,,E*59", + "$GPGGA,113718.568,4154.940,N,08002.498,W,0,00,1.0,,M,,M,,*4D", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.5,0.1,0.2*39", + "$GPGGA,113736.568,4154.951,N,08002.496,W,0,00,0.8,,M,,M,,*79", + "$GPGSA,A,3,09,02,08,05,11,15,,,,,,,0.2,0.6,0.1*39", + "$GPRMC,113738.568,V,4154.953,N,08002.496,W,58.2,0.10,220721,,E*67", + "$GPGGA,113739.568,4154.953,N,08002.496,W,0,00,0.6,,M,,M,,*79", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.6,0.9,0.7*35", + "$GPRMC,113741.568,V,4154.955,N,08002.496,W,88.3,0.03,220721,,E*31", + "$GPGGA,113742.568,4154.955,N,08002.496,W,0,00,0.9,,M,,M,,*33", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.8,0.8,0.1*34", + "$GPRMC,113744.568,V,4154.956,N,08002.496,W,89.3,0.10,220721,,E*34", + "$GPGGA,113745.568,4154.956,N,08002.496,W,0,00,0.8,,M,,M,,*75", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.9,0.2,1.0*4B", + "$GPRMC,113747.568,V,4154.958,N,08002.495,W,99.1,0.14,220721,,E*5D", + "$GPGGA,113748.568,4154.958,N,08002.495,W,0,00,0.2,,M,,M,,*5C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.1,0.2*55", + "$GPRMC,113750.568,V,4154.960,N,08002.495,W,84.0,0.19,220721,,E*50", + "$GPGGA,113751.568,4154.960,N,08002.495,W,0,00,0.1,,M,,M,,*5C", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.3,1.0,0.5*35", + "$GPRMC,113753.568,V,4154.962,N,08002.495,W,24.0,0.13,220721,,E*51", + "$GPGGA,113754.568,4154.962,N,08002.495,W,0,00,1.0,,M,,M,,*5B", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.7,0.8,0.9*35", + "$GPRMC,113756.568,V,4154.963,N,08002.494,W,27.8,0.03,220721,,E*5E", + "$GPGGA,113757.568,4154.963,N,08002.494,W,0,00,0.8,,M,,M,,*51", + "$GPGSA,A,2,09,02,08,05,11,15,,,,,,,0.5,0.4,0.7*57" +} + +local GNSStr = "$GPRMC,143753.498,V,3854.930,N,07902.496,W,91.3,0.75,220721,,E*4B\r\n\ + $GPGGA,143754.498,3854.930,N,07902.496,W,0,00,,,M,,M,,*53\r\n\ + $GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.1,0.0,0.2*3E\r\n\ + $GPRMC,143756.498,V,3854.931,N,07902.494,W,92.7,0.76,220721,,E*49\r\n\ + $GPGGA,143757.498,3854.931,N,07902.494,W,0,00,0.0,,M,,M,,*7D\r\n\ + $GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.4,0.4,0.5*38\r\n\ + $GPRMC,143759.498,V,3854.932,N,07902.492,W,15.5,0.75,220721,,E*4D\r\n" + +local badCRC = "$GPRMC,143753.498,V,3854.930,N,07902.496,W,91.3,0.75,220721,,E*6B\r\n\ + $GPGGA,143754.498,3854.930,N,07902.496,W,0,00,,,M,,M,,*43\r\n\ + $GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.1,0.0,0.2*5E\r\n\ + $GPRMC,143756.498,V,3854.931,N,07902.494,W,92.7,0.76,220721,,E*48\r\n\ + $GPGGA,143757.498,3854.931,N,07902.494,W,0,00,0.0,,M,,M,,*4D\r\n\ + $GPGSA,A,2,13,09,13,09,18,16,,,,,,,0.4,0.4,0.5*39\r\n\ + $GPRMC,143759.498,V,3854.932,N,07902.492,W,15.5,0.75,220721,,E*3D\r\n" + +local nmeaDataType = {"$GPGSA,", "$GPRMC,", "$GPGGA,"} +local badString = "oskdsajdij232391i*&^^&7^&^(*&*YDUDHJSBDNBNVyywfdywf" + +function testBitXOR() + lu.assertEquals(BitXOR(string.byte('q'), string.byte('r')), 3) + lu.assertEquals(BitXOR(string.byte('a'), string.byte('b')), 3) + lu.assertEquals(BitXOR(string.byte('1'), string.byte('5')), 4) + lu.assertEquals(BitXOR(string.byte('0'), string.byte('0')), 0) + lu.assertEquals(BitXOR(string.byte('9'), string.byte('1')), 8) + lu.assertEquals(BitXOR(string.byte('f'), string.byte('w')), 17) +end + +function testCRC() + for i = 1, #goodGNSSSdata do + lu.assertEquals(checkCRC(goodGNSSSdata[i]), true) + end + for i = 1, #badGNSSSdata do + lu.assertEquals(checkCRC(badGNSSSdata[i]), false) + end +end + +function testCropData() + for i = 1, #nmeaDataType do + lu.assertEquals(getCropData("", nmeaDataType[i]), {true, "No data found"}) + lu.assertEquals(getCropData(badString, nmeaDataType[i]), {true, "No data found"}) + lu.assertEquals(getCropData(badCRC, nmeaDataType[i]), {true, "Checksum error"}) + lu.assertEquals(getCropData(GNSStr, nmeaDataType[i]), {false, "OK"}) + end +end + +os.exit(lu.LuaUnit.run()) \ No newline at end of file diff --git a/luci-app-gpoint-main/test/nmea_test2.lua b/luci-app-gpoint-main/test/nmea_test2.lua new file mode 100644 index 000000000..fc8121981 --- /dev/null +++ b/luci-app-gpoint-main/test/nmea_test2.lua @@ -0,0 +1,56 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + + +function printData(data, key) + for i, j in pairs(data) do + if i == key then + print("----->",i) + for k, v in pairs(j) do + print(k, v) + print("*********************") + end + end + end + print("----------------------------------------------") +end + +function printError(data) + for i, j in pairs(data) do + print(i) + for k, v in pairs(j) do + if k == "app" then + for m, n in pairs(v) do + print(m, n) + end + end + end + end +end + +local nmea = require("nmea") +local port = "/dev/ttyUSB1" + +-- test get NMEA data (GGA) +--local GGA = nmea.getData("GGA", port) +--printData(GGA, "gga") + +-- test get NMEA data (GNS) +--local GNS = nmea.getData("GNS", port) +--printData(GNS, "gns") + +-- test get RMC data (RMC) +--local RMC = nmea.getData("RMC", port) +--printData(RMC, "rmc") + +-- test get NMEA data (VTG) +--local VTG = nmea.getData("VTG", port) +--printData(VTG, "vtg") + +-- test get NMEA data (GSA) +--local GSA = nmea.getData("GSA", port) +--printData(GSA, "gsa") + +-- test get NMEA data (BAD STRING) +--local AAA = nmea.getData("AAA", port) +--printError(AAA) \ No newline at end of file diff --git a/luci-app-gpoint-main/test/serial_test.lua b/luci-app-gpoint-main/test/serial_test.lua new file mode 100644 index 000000000..8529ff9e1 --- /dev/null +++ b/luci-app-gpoint-main/test/serial_test.lua @@ -0,0 +1,16 @@ +common_path = '/usr/share/gpoint/lib/?.lua;' +package.path = common_path .. package.path + +serial = require("serial") + +function serial_read(port) + local err, data = serial.read(port) + assert(err, data) + print("Error data: OK") + assert(data) + print("Data from serial: OK") +end +local port = "/dev/ttyUSB1" +serial_read(port) + +print("OK") \ No newline at end of file